temperature.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. const { createCanvas, loadImage } = require('canvas');
  2. // 温度转颜色(蓝 -> 绿 -> 红)
  3. function tempToColor(temp) {
  4. let r, g, b;
  5. if (temp < 15) {
  6. r = 0;
  7. g = Math.floor((temp - 10) / 5 * 255); // 10~15: 黑->绿
  8. b = 255;
  9. } else if (temp < 25) {
  10. r = Math.floor((temp - 15) / 10 * 255);
  11. g = 255;
  12. b = 255 - Math.floor((temp - 15) / 10 * 255);
  13. } else {
  14. r = 255;
  15. g = Math.floor((30 - temp) / 5 * 255);
  16. b = 0;
  17. }
  18. console.log(`Temp: ${temp}, Color: [${r}, ${g}, ${b}]`);
  19. return [r, g, b];
  20. }
  21. // 模拟数据集,实际应用中应替换为真实数据
  22. const data = Array(256).fill().map((_, i) =>
  23. Array(256).fill().map((_, j) => {
  24. // 创建一个渐变效果,方便测试
  25. return 10 + 10 * Math.sin(i / 20) * Math.cos(j / 20);
  26. })
  27. );
  28. function getTileData(data, z, x, y) {
  29. const maxTiles = Math.pow(2, z);
  30. const totalWidth = data[0].length;
  31. const totalHeight = data.length;
  32. // 每个瓦片应覆盖的像素数(向下取整)
  33. const tilePixelWidth = Math.floor(totalWidth / maxTiles);
  34. const tilePixelHeight = Math.floor(totalHeight / maxTiles);
  35. // 计算当前瓦片的像素范围
  36. const startX = x * tilePixelWidth;
  37. const startY = y * tilePixelHeight;
  38. const endX = Math.min(startX + tilePixelWidth, totalWidth);
  39. const endY = Math.min(startY + tilePixelHeight, totalHeight);
  40. // 边界检查
  41. if (startY >= totalHeight || startX >= totalWidth) {
  42. return Array(256).fill().map(() => Array(256).fill(0)); // 返回空白
  43. }
  44. const tileData = [];
  45. for (let i = startY; i < endY; i++) {
  46. const row = [];
  47. for (let j = startX; j < endX; j++) {
  48. row.push(data[i][j]);
  49. }
  50. tileData.push(row);
  51. }
  52. return tileData;
  53. }
  54. function renderTile(tileData) {
  55. const canvas = createCanvas(256, 256);
  56. const ctx = canvas.getContext('2d');
  57. const imageData = ctx.createImageData(256, 256);
  58. const pixels = imageData.data;
  59. // 如果 tileData 为空,返回灰色图
  60. if (tileData.length === 0 || tileData[0].length === 0) {
  61. pixels.fill(128); // 灰色
  62. ctx.putImageData(imageData, 0, 0);
  63. return canvas.toBuffer('image/png');
  64. }
  65. const dataHeight = tileData.length;
  66. const dataWidth = tileData[0].length;
  67. for (let i = 0; i < 256; i++) {
  68. for (let j = 0; j < 256; j++) {
  69. // 将 256x256 的像素坐标映射回 tileData 的数据坐标
  70. const sourceI = Math.floor((i / 256) * dataHeight);
  71. const sourceJ = Math.floor((j / 256) * dataWidth);
  72. // 边界保护
  73. const value = tileData[sourceI]?.[sourceJ] ?? 0; // 空值 fallback 为 0
  74. const color = tempToColor(value);
  75. const pixelIdx = (j * 256 + i) * 4;
  76. pixels[pixelIdx] = color[0];
  77. pixels[pixelIdx + 1] = color[1];
  78. pixels[pixelIdx + 2] = color[2];
  79. pixels[pixelIdx + 3] = 255;
  80. }
  81. }
  82. ctx.putImageData(imageData, 0, 0);
  83. return canvas.toBuffer('image/png');
  84. }
  85. exports.tempapixyz = async (req, res) => {
  86. const { z, x, y } = req.params;
  87. const zoom = parseInt(z);
  88. const tileX = parseInt(x);
  89. const tileY = parseInt(y);
  90. const tileData = await getTileData(data, zoom, tileX, tileY);
  91. const buffer = renderTile(tileData);
  92. res.set('Content-Type', 'image/png');
  93. res.set('Cache-Control', 'no-cache'); // 实时数据不缓存
  94. res.send(buffer);
  95. }