temperature.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. const { createCanvas, loadImage } = require('canvas');
  2. // // 模拟数据生成函数:根据 x, y, z 返回该瓦片内的温度数据(示例)
  3. // function generateTemperatureData(z, x, y) {
  4. // // 这里可以替换为数据库查询、API 调用等
  5. // const noise = Math.sin(x * 0.5) * Math.cos(y * 0.3) + Math.random() * 0.5;
  6. // const temp = 10 + 20 * (Math.sin(z) + noise); // 10~30°C 之间
  7. // return Array(256 * 256).fill().map(() => temp + (Math.random() - 0.5) * 5);
  8. // }
  9. // 温度转颜色(蓝 -> 绿 -> 红)
  10. function tempToColor(temp) {
  11. let r, g, b;
  12. if (temp < 15) {
  13. r = 0;
  14. g = Math.floor((temp - 10) / 5 * 255); // 10~15: 黑->绿
  15. b = 255;
  16. } else if (temp < 25) {
  17. r = Math.floor((temp - 15) / 10 * 255);
  18. g = 255;
  19. b = 255 - Math.floor((temp - 15) / 10 * 255);
  20. } else {
  21. r = 255;
  22. g = Math.floor((30 - temp) / 5 * 255);
  23. b = 0;
  24. }
  25. console.log(`Temp: ${temp}, Color: [${r}, ${g}, ${b}]`);
  26. return [r, g, b];
  27. }
  28. // exports.tempapixyz = async (req, res) => {
  29. // const { z, x, y } = req.params;
  30. // const zoom = parseInt(z);
  31. // const tileX = parseInt(x);
  32. // const tileY = parseInt(y);
  33. // // 创建 256x256 画布(标准瓦片大小)
  34. // const canvas = createCanvas(256, 256);
  35. // const ctx = canvas.getContext('2d');
  36. // // 生成模拟数据(每个像素一个温度值)
  37. // const data = generateTemperatureData(zoom, tileX, tileY);
  38. // // 创建图像数据
  39. // const imageData = ctx.createImageData(256, 256);
  40. // const pixels = imageData.data;
  41. // for (let i = 0; i < 256; i++) {
  42. // for (let j = 0; j < 256; j++) {
  43. // const idx = (i * 256 + j);
  44. // const temp = data[idx];
  45. // const [r, g, b] = tempToColor(temp);
  46. // const pixelIdx = (j * 256 + i) * 4;
  47. // pixels[pixelIdx] = r; // R
  48. // pixels[pixelIdx + 1] = g; // G
  49. // pixels[pixelIdx + 2] = b; // B
  50. // pixels[pixelIdx + 3] = 200; // A (半透明)
  51. // }
  52. // }
  53. // ctx.putImageData(imageData, 0, 0);
  54. // // 可选:添加网格线或边框
  55. // ctx.strokeStyle = 'rgba(255,255,255,0.2)';
  56. // ctx.strokeRect(0, 0, 256, 256);
  57. // // 输出 PNG
  58. // const buffer = canvas.toBuffer('image/png');
  59. // res.set('Content-Type', 'image/png');
  60. // res.set('Cache-Control', 'no-cache'); // 实时数据不缓存
  61. // res.send(buffer);
  62. // }
  63. // 模拟数据集,实际应用中应替换为真实数据
  64. const data = Array(256).fill().map((_, i) =>
  65. Array(256).fill().map((_, j) => {
  66. // 创建一个渐变效果,方便测试
  67. return 10 + 10 * Math.sin(i / 20) * Math.cos(j / 20);
  68. })
  69. );
  70. function getTileData(data, z, x, y) {
  71. const maxTiles = Math.pow(2, z);
  72. const totalWidth = data[0].length;
  73. const totalHeight = data.length;
  74. // 每个瓦片应覆盖的像素数(向下取整)
  75. const tilePixelWidth = Math.floor(totalWidth / maxTiles);
  76. const tilePixelHeight = Math.floor(totalHeight / maxTiles);
  77. // 计算当前瓦片的像素范围
  78. const startX = x * tilePixelWidth;
  79. const startY = y * tilePixelHeight;
  80. const endX = Math.min(startX + tilePixelWidth, totalWidth);
  81. const endY = Math.min(startY + tilePixelHeight, totalHeight);
  82. // 边界检查
  83. if (startY >= totalHeight || startX >= totalWidth) {
  84. return Array(256).fill().map(() => Array(256).fill(0)); // 返回空白
  85. }
  86. const tileData = [];
  87. for (let i = startY; i < endY; i++) {
  88. const row = [];
  89. for (let j = startX; j < endX; j++) {
  90. row.push(data[i][j]);
  91. }
  92. tileData.push(row);
  93. }
  94. return tileData;
  95. }
  96. function renderTile(tileData) {
  97. const canvas = createCanvas(256, 256);
  98. const ctx = canvas.getContext('2d');
  99. const imageData = ctx.createImageData(256, 256);
  100. const pixels = imageData.data;
  101. // 如果 tileData 为空,返回灰色图
  102. if (tileData.length === 0 || tileData[0].length === 0) {
  103. pixels.fill(128); // 灰色
  104. ctx.putImageData(imageData, 0, 0);
  105. return canvas.toBuffer('image/png');
  106. }
  107. const dataHeight = tileData.length;
  108. const dataWidth = tileData[0].length;
  109. for (let i = 0; i < 256; i++) {
  110. for (let j = 0; j < 256; j++) {
  111. // 将 256x256 的像素坐标映射回 tileData 的数据坐标
  112. const sourceI = Math.floor((i / 256) * dataHeight);
  113. const sourceJ = Math.floor((j / 256) * dataWidth);
  114. // 边界保护
  115. const value = tileData[sourceI]?.[sourceJ] ?? 0; // 空值 fallback 为 0
  116. const color = tempToColor(value);
  117. const pixelIdx = (j * 256 + i) * 4;
  118. pixels[pixelIdx] = color[0];
  119. pixels[pixelIdx + 1] = color[1];
  120. pixels[pixelIdx + 2] = color[2];
  121. pixels[pixelIdx + 3] = 255;
  122. }
  123. }
  124. ctx.putImageData(imageData, 0, 0);
  125. return canvas.toBuffer('image/png');
  126. }
  127. exports.tempapixyz = async (req, res) => {
  128. const { z, x, y } = req.params;
  129. const zoom = parseInt(z);
  130. const tileX = parseInt(x);
  131. const tileY = parseInt(y);
  132. const tileData = await getTileData(data, zoom, tileX, tileY);
  133. const buffer = renderTile(tileData);
  134. res.set('Content-Type', 'image/png');
  135. res.set('Cache-Control', 'no-cache'); // 实时数据不缓存
  136. res.send(buffer);
  137. }