const fs = require('fs'); const path = require('path'); const { createCanvas, ImageData } = require('canvas'); const sharp = require('sharp'); // 配置参数 const config = { outputDir: 'G:/tiles/cloud', // 输出目录 emptyValue: 9.999e+20, // 空值标识 minZoom: 0, // 最小缩放级别 maxZoom: 5 // 最大缩放级别 }; async function createCloudImageDeep(inputFile) { try { // 确保输出目录存在 if (!fs.existsSync(inputFile)) { fs.mkdirSync(inputFile, { recursive: true }); } // 解析数据文件 const { grid, width, height } = parseDataFile(inputFile); // 生成各级别瓦片 for (let zoom = config.minZoom; zoom <= config.maxZoom; zoom++) { await generateTiles(grid, width, height, zoom); } // 生成图例 generateLegend(); console.log('瓦片生成完成!'); console.log(`输出目录: ${path.resolve(config.outputDir)}`); } catch (error) { console.error('处理过程中发生错误:', error); } } // 读取并解析数据文件 function parseDataFile(filePath) { console.log('正在读取数据文件...'); const data = fs.readFileSync(filePath, 'utf8').split('\n'); // 解析第一行获取尺寸信息 const dimensions = data[0].split(' ').map(Number); const dataWidth = dimensions[0]; const dataHeight = dimensions[1]; console.log(`数据尺寸: ${dataWidth}x${dataHeight}`); // 解析数据值 const grid = []; for (let i = 1; i < data.length; i++) { if (data[i].trim() === '') continue; const value = parseFloat(data[i]); // 处理空值 grid.push(value === config.emptyValue ? NaN : value); } console.log(`已读取 ${grid.length} 个数据点`); return { grid, width: dataWidth, height: dataHeight }; } // 将经纬度转换为瓦片坐标 function latLonToTileIndex(lat, lon, zoom) { const x = Math.floor((lon + 180) / 360 * Math.pow(2, zoom)); const y = Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)); return { x, y }; } // 将瓦片坐标转换为经纬度范围 function tileToLatLon(x, y, zoom) { const n = Math.pow(2, zoom); const lon1 = x / n * 360 - 180; const lat1 = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n))) * 180 / Math.PI; const lon2 = (x + 1) / n * 360 - 180; const lat2 = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 1) / n))) * 180 / Math.PI; return { west: lon1, east: lon2, south: lat2, north: lat1 }; } // 生成指定缩放级别的瓦片 async function generateTiles(data, dataWidth, dataHeight, zoom) { console.log(`正在生成第 ${zoom} 级瓦片...`); const tileSize = 256; const totalTiles = Math.pow(2, zoom); // 创建进度跟踪 let processed = 0; const totalToProcess = totalTiles * totalTiles; for (let x = 0; x < totalTiles; x++) { for (let y = 0; y < totalTiles; y++) { const canvas = createCanvas(tileSize, tileSize); const ctx = canvas.getContext('2d'); const imageData = ctx.createImageData(tileSize, tileSize); // 获取当前瓦片的经纬度范围 const bounds = tileToLatLon(x, y, zoom); // 为瓦片中的每个像素计算数据值 for (let py = 0; py < tileSize; py++) { for (let px = 0; px < tileSize; px++) { // 计算当前像素的经纬度 const pixelLon = bounds.west + (px / tileSize) * (bounds.east - bounds.west); const pixelLat = bounds.south + (py / tileSize) * (bounds.north - bounds.south); // 将经纬度转换为数据网格索引 const dataX = Math.floor((pixelLon + 180) / 360 * dataWidth); const dataY = Math.floor((90 - pixelLat) / 180 * dataHeight); // 确保索引在有效范围内 const safeX = Math.max(0, Math.min(dataWidth - 1, dataX)); const safeY = Math.max(0, Math.min(dataHeight - 1, dataY)); const dataIndex = safeY * dataWidth + safeX; // 获取数据值并处理空值 let value = data[dataIndex]; if (isNaN(value)) { // 空值处理:设置为完全透明 setPixel(imageData, px, py, 0, 0, 0, 0); } else { // 将云量值(0-100)映射到颜色(蓝色到白色) const intensity = Math.min(255, Math.max(0, Math.floor(value * 2.55))); // 使用蓝色渐变表示云量 setPixel(imageData, px, py, intensity, intensity, 255, 180); } } } ctx.putImageData(imageData, 0, 0); // 创建目录并保存瓦片 const tileDir = path.join(config.outputDir, zoom.toString(), x.toString()); if (!fs.existsSync(tileDir)) { fs.mkdirSync(tileDir, { recursive: true }); } const tilePath = path.join(tileDir, `${y}.png`); const buffer = canvas.toBuffer('image/png'); await sharp(buffer).png().toFile(tilePath); // 更新进度 processed++; if (processed % 100 === 0) { console.log(`进度: ${Math.round(processed / totalToProcess * 100)}%`); } } } } // 设置像素颜色 function setPixel(imageData, x, y, r, g, b, a) { const index = (y * imageData.width + x) * 4; imageData.data[index] = r; imageData.data[index + 1] = g; imageData.data[index + 2] = b; imageData.data[index + 3] = a; } // 生成图例 function generateLegend() { const legend = `