ソースを参照

模拟xyz的气象api

sunzehao 6 ヶ月 前
コミット
4f46e651f3
4 ファイル変更180 行追加1 行削除
  1. 4 1
      app.js
  2. 1 0
      package.json
  3. 166 0
      router_handler/weather/temperature.js
  4. 9 0
      routes/weather/temperature.js

+ 4 - 1
app.js

@@ -48,9 +48,12 @@ app.use((req, res, next) => {
 //用户信息
 // const userinfoRounter = require('./routes/user/userInfo.js');
 // app.use('/my', userinfoRounter);
-//文章
+//天气
 const weatherInchinaRounter = require('./routes/weather/inchina.js');
 app.use('/weather/inchina', weatherInchinaRounter);
+
+const tempapiRounter = require('./routes/weather/temperature.js');
+app.use('/weather/tempapi', tempapiRounter);
 //疫苗接种
 // const vaccinesRounter = require('./routes/vaccines/vaccine.js');
 // app.use('/my/vaccines', vaccinesRounter);

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "axios": "^1.8.4",
     "bcryptjs": "^3.0.2",
     "body-parser": "^2.2.0",
+    "canvas": "^3.2.0",
     "cookie-parser": "~1.4.4",
     "cors": "^2.8.5",
     "debug": "~2.6.9",

+ 166 - 0
router_handler/weather/temperature.js

@@ -0,0 +1,166 @@
+const { createCanvas, loadImage } = require('canvas');
+
+// // 模拟数据生成函数:根据 x, y, z 返回该瓦片内的温度数据(示例)
+// function generateTemperatureData(z, x, y) {
+//   // 这里可以替换为数据库查询、API 调用等
+//   const noise = Math.sin(x * 0.5) * Math.cos(y * 0.3) + Math.random() * 0.5;
+//   const temp = 10 + 20 * (Math.sin(z) + noise); // 10~30°C 之间
+//   return Array(256 * 256).fill().map(() => temp + (Math.random() - 0.5) * 5);
+// }
+
+// 温度转颜色(蓝 -> 绿 -> 红)
+function tempToColor(temp) {
+    let r, g, b;
+    if (temp < 15) {
+        r = 0;
+        g = Math.floor((temp - 10) / 5 * 255); // 10~15: 黑->绿
+        b = 255;
+    } else if (temp < 25) {
+        r = Math.floor((temp - 15) / 10 * 255);
+        g = 255;
+        b = 255 - Math.floor((temp - 15) / 10 * 255);
+    } else {
+        r = 255;
+        g = Math.floor((30 - temp) / 5 * 255);
+        b = 0;
+    }
+    console.log(`Temp: ${temp}, Color: [${r}, ${g}, ${b}]`);
+    return [r, g, b];
+}
+
+// exports.tempapixyz = async (req, res) => { 
+//     const { z, x, y } = req.params;
+//     const zoom = parseInt(z);
+//     const tileX = parseInt(x);
+//     const tileY = parseInt(y);
+
+//     // 创建 256x256 画布(标准瓦片大小)
+//     const canvas = createCanvas(256, 256);
+//     const ctx = canvas.getContext('2d');
+
+//     // 生成模拟数据(每个像素一个温度值)
+//     const data = generateTemperatureData(zoom, tileX, tileY);
+
+//     // 创建图像数据
+//     const imageData = ctx.createImageData(256, 256);
+//     const pixels = imageData.data;
+
+//     for (let i = 0; i < 256; i++) {
+//         for (let j = 0; j < 256; j++) {
+//         const idx = (i * 256 + j);
+//         const temp = data[idx];
+//         const [r, g, b] = tempToColor(temp);
+
+//         const pixelIdx = (j * 256 + i) * 4;
+//         pixels[pixelIdx] = r;     // R
+//         pixels[pixelIdx + 1] = g; // G
+//         pixels[pixelIdx + 2] = b; // B
+//         pixels[pixelIdx + 3] = 200; // A (半透明)
+//         }
+//     }
+
+//     ctx.putImageData(imageData, 0, 0);
+
+//     // 可选:添加网格线或边框
+//     ctx.strokeStyle = 'rgba(255,255,255,0.2)';
+//     ctx.strokeRect(0, 0, 256, 256);
+
+//     // 输出 PNG
+//     const buffer = canvas.toBuffer('image/png');
+//     res.set('Content-Type', 'image/png');
+//     res.set('Cache-Control', 'no-cache'); // 实时数据不缓存
+//     res.send(buffer);
+// }
+
+// 模拟数据集,实际应用中应替换为真实数据
+const data = Array(256).fill().map((_, i) =>
+  Array(256).fill().map((_, j) => {
+    // 创建一个渐变效果,方便测试
+    return 10 + 10 * Math.sin(i / 20) * Math.cos(j / 20);
+  })
+);
+
+function getTileData(data, z, x, y) {
+    const maxTiles = Math.pow(2, z);
+    const totalWidth = data[0].length;
+    const totalHeight = data.length;
+
+    // 每个瓦片应覆盖的像素数(向下取整)
+    const tilePixelWidth = Math.floor(totalWidth / maxTiles);
+    const tilePixelHeight = Math.floor(totalHeight / maxTiles);
+
+    // 计算当前瓦片的像素范围
+    const startX = x * tilePixelWidth;
+    const startY = y * tilePixelHeight;
+    const endX = Math.min(startX + tilePixelWidth, totalWidth);
+    const endY = Math.min(startY + tilePixelHeight, totalHeight);
+
+    // 边界检查
+    if (startY >= totalHeight || startX >= totalWidth) {
+        return Array(256).fill().map(() => Array(256).fill(0)); // 返回空白
+    }
+
+    const tileData = [];
+    for (let i = startY; i < endY; i++) {
+        const row = [];
+        for (let j = startX; j < endX; j++) {
+        row.push(data[i][j]);
+        }
+        tileData.push(row);
+    }
+
+    return tileData;
+}
+
+function renderTile(tileData) {
+    const canvas = createCanvas(256, 256);
+    const ctx = canvas.getContext('2d');
+    const imageData = ctx.createImageData(256, 256);
+    const pixels = imageData.data;
+
+    // 如果 tileData 为空,返回灰色图
+    if (tileData.length === 0 || tileData[0].length === 0) {
+        pixels.fill(128); // 灰色
+        ctx.putImageData(imageData, 0, 0);
+        return canvas.toBuffer('image/png');
+    }
+
+    const dataHeight = tileData.length;
+    const dataWidth = tileData[0].length;
+
+    for (let i = 0; i < 256; i++) {
+        for (let j = 0; j < 256; j++) {
+        // 将 256x256 的像素坐标映射回 tileData 的数据坐标
+        const sourceI = Math.floor((i / 256) * dataHeight);
+        const sourceJ = Math.floor((j / 256) * dataWidth);
+
+        // 边界保护
+        const value = tileData[sourceI]?.[sourceJ] ?? 0; // 空值 fallback 为 0
+
+        const color = tempToColor(value);
+
+        const pixelIdx = (j * 256 + i) * 4;
+        pixels[pixelIdx] = color[0];
+        pixels[pixelIdx + 1] = color[1];
+        pixels[pixelIdx + 2] = color[2];
+        pixels[pixelIdx + 3] = 255;
+        }
+    }
+
+    ctx.putImageData(imageData, 0, 0);
+    return canvas.toBuffer('image/png');
+}
+
+exports.tempapixyz = async (req, res) => { 
+    const { z, x, y } = req.params;
+    const zoom = parseInt(z);
+    const tileX = parseInt(x);
+    const tileY = parseInt(y);
+
+    const tileData = await getTileData(data, zoom, tileX, tileY);
+    const buffer = renderTile(tileData);
+
+    res.set('Content-Type', 'image/png');
+    res.set('Cache-Control', 'no-cache'); // 实时数据不缓存
+    res.send(buffer);
+}

+ 9 - 0
routes/weather/temperature.js

@@ -0,0 +1,9 @@
+const express = require("express");
+const router = express.Router();
+
+const tempapi = require('../../router_handler/weather/temperature.js');
+
+//模拟天气温度瓦片效果
+router.get('/:z/:x/:y.png', tempapi.tempapixyz);
+
+module.exports = router;