| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- const fs = require('fs');
- const path = require('path');
- const { parse } = require('csv-parse');
- const readline = require('readline');
- const { leftlon, rightlon, toplat, bottomlat } = require("./region");
- // 仅导出中国区域数据
- const JUST_EXPORT_CHINADATA = true;
- // 配置参数
- const config = {
- outputDir: path.join(__dirname, `../model`), // 输出目录
- sampleRate: 0.1, // 抽样率 (0.1 = 10%的数据)
- testMode: false, // 测试模式,只处理少量数据
- testLimit: 1000, // 测试模式下的最大行数
- outputDir: path.join(__dirname, `../model`),
- outputFile: 'windGridData.json',
- knownGridParams: {
- minLon: -179,
- maxLon: 180,
- minLat: -90,
- maxLat: 90,
- dx: 1.0, // 假设1度分辨率
- dy: 1.0, // 假设1度分辨率
- nx: 361, // 经度点数: 180*2 + 1
- ny: 181 // 纬度点数: 90*2 + 1
- }
- };
- async function createWindData(uFile, vFile) {
- try {
- console.log('开始转换为网格JSON格式...');
- // 并行解析U和V的CSV文件
- const [uData, vData] = await Promise.all([
- parseCSVFile(uFile, 'U'),
- parseCSVFile(vFile, 'V')
- ]);
- // 检查数据是否有效
- if (uData.length === 0 || vData.length === 0) {
- throw new Error('U或V数据为空');
- }
- // 使用已知的全球网格参数
- const gridParams = config.knownGridParams;
- // 为U和V数据创建网格结构
- const uGrid = createGridData(uData, gridParams, 'U');
- const vGrid = createGridData(vData, gridParams, 'V');
- if (!uGrid || !vGrid) {
- throw new Error('网格创建失败');
- }
- // 组合成风场库需要的数组格式
- const outputData = [uGrid, vGrid];
- // 写入输出文件
- const outputPath = path.join(config.outputDir, config.outputFile);
- fs.writeFileSync(outputPath, JSON.stringify(outputData));
- console.log(`转换成功! 网格JSON已保存到: ${outputPath}`);
- console.log(`U网格信息: nx=${uGrid.header.nx}, ny=${uGrid.header.ny}`);
- console.log(`V网格信息: nx=${vGrid.header.nx}, ny=${vGrid.header.ny}`);
- } catch (error) {
- console.error('转换过程中出错:', error);
- }
- }
- // 确保输出目录存在
- if (!fs.existsSync(config.outputDir)) {
- fs.mkdirSync(config.outputDir, { recursive: true });
- }
- // 解析CSV文件的函数
- async function parseCSVFile(filePath, component) {
- return new Promise((resolve, reject) => {
- const results = [];
- let lineCount = 0;
- const rl = readline.createInterface({
- input: fs.createReadStream(filePath),
- crlfDelay: Infinity
- });
- rl.on('line', (line) => {
- if (!line.trim()) return;
- try {
- const cleanedLine = line.replace(/"/g, '');
- const parts = cleanedLine.split(',');
- if (parts.length < 7) return;
- const lon = parseFloat(parts[4]);
- const lat = parseFloat(parts[5]);
- const value = parseFloat(parts[6]);
- if (!isNaN(lon) && !isNaN(lat) && !isNaN(value)) {
- results.push({ lon, lat, value });
- }
- } catch (e) {
- console.error('解析行时出错:', line, e);
- }
- lineCount++;
- });
- rl.on('close', () => {
- console.log(`从 ${filePath} 解析了 ${results.length} 条 ${component} 记录`);
- resolve(results);
- });
- rl.on('error', reject);
- });
- }
- // 创建网格数据 - 使用已知的全球网格参数
- function createGridData(parsedData, gridParams, component) {
- console.log(`开始创建 ${component} 网格数据...`);
- const { minLon, maxLon, minLat, maxLat, dx, dy, nx, ny } = gridParams;
- // 创建空网格(用null填充)
- const gridData = new Array(nx * ny).fill(null);
- // 将解析的数据填充到网格中
- let filledCount = 0;
- let missingCount = 0;
- parsedData.forEach(point => {
- // 计算网格索引
- const i = Math.round((point.lon - minLon) / dx);
- const j = Math.round((maxLat - point.lat) / dy); // 注意纬度是从上到下递减
- const index = j * nx + i;
- if (index >= 0 && index < gridData.length) {
- gridData[index] = point.value;
- filledCount++;
- } else {
- missingCount++;
- console.warn(`点超出网格范围: (${point.lon}, ${point.lat}) -> 索引: ${index}`);
- }
- });
- console.log(`${component} 网格填充了 ${filledCount} 个数据点,缺失 ${missingCount} 个点`);
- console.log(`${component} 数据非空比例: ${(filledCount / gridData.length * 100).toFixed(2)}%`);
- if (filledCount === 0) {
- console.error(`错误: ${component} 网格没有有效数据`);
- return null;
- }
- return {
- header: {
- parameterCategory: 2,
- parameterCategoryName: "Momentum",
- parameterNumber: component === 'U' ? 2 : 3,
- parameterNumberName: component === 'U' ? "U-component_of_wind" : "V-component_of_wind",
- nx: nx,
- ny: ny,
- lo1: minLon,
- la1: maxLat,
- dx: dx,
- dy: dy,
- numberPoints: nx * ny,
- refTime: "2025-09-10T00:00:00Z"
- },
- data: gridData
- };
- }
- async function main() {
- try {
- console.log('开始转换为网格JSON格式...');
- // 并行解析U和V的CSV文件
- const [uData, vData] = await Promise.all([
- parseCSVFile(config.uCsvFile, 'U'),
- parseCSVFile(config.vCsvFile, 'V')
- ]);
- // 检查数据是否有效
- if (uData.length === 0 || vData.length === 0) {
- throw new Error('U或V数据为空');
- }
- // 使用已知的全球网格参数
- const gridParams = config.knownGridParams;
- // 为U和V数据创建网格结构
- const uGrid = createGridData(uData, gridParams, 'U');
- const vGrid = createGridData(vData, gridParams, 'V');
- if (!uGrid || !vGrid) {
- throw new Error('网格创建失败');
- }
- // 组合成风场库需要的数组格式
- const outputData = [uGrid, vGrid];
- // 写入输出文件
- const outputPath = path.join(config.outputDir, config.outputFile);
- fs.writeFileSync(outputPath, JSON.stringify(outputData));
- console.log(`转换成功! 网格JSON已保存到: ${outputPath}`);
- console.log(`U网格信息: nx=${uGrid.header.nx}, ny=${uGrid.header.ny}`);
- console.log(`V网格信息: nx=${vGrid.header.nx}, ny=${vGrid.header.ny}`);
- } catch (error) {
- console.error('转换过程中出错:', error);
- }
- }
- module.exports = {
- createWindData
- };
|