createWindData.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. const fs = require('fs');
  2. const path = require('path');
  3. const { parse } = require('csv-parse');
  4. const readline = require('readline');
  5. const { leftlon, rightlon, toplat, bottomlat } = require("./region");
  6. // 仅导出中国区域数据
  7. const JUST_EXPORT_CHINADATA = true;
  8. // 配置参数
  9. const config = {
  10. outputDir: path.join(__dirname, `../model`), // 输出目录
  11. sampleRate: 0.1, // 抽样率 (0.1 = 10%的数据)
  12. testMode: false, // 测试模式,只处理少量数据
  13. testLimit: 1000, // 测试模式下的最大行数
  14. outputDir: path.join(__dirname, `../model`),
  15. outputFile: 'windGridData.json',
  16. knownGridParams: {
  17. minLon: -179,
  18. maxLon: 180,
  19. minLat: -90,
  20. maxLat: 90,
  21. dx: 1.0, // 假设1度分辨率
  22. dy: 1.0, // 假设1度分辨率
  23. nx: 361, // 经度点数: 180*2 + 1
  24. ny: 181 // 纬度点数: 90*2 + 1
  25. }
  26. };
  27. async function createWindData(uFile, vFile) {
  28. try {
  29. console.log('开始转换为网格JSON格式...');
  30. // 并行解析U和V的CSV文件
  31. const [uData, vData] = await Promise.all([
  32. parseCSVFile(uFile, 'U'),
  33. parseCSVFile(vFile, 'V')
  34. ]);
  35. // 检查数据是否有效
  36. if (uData.length === 0 || vData.length === 0) {
  37. throw new Error('U或V数据为空');
  38. }
  39. // 使用已知的全球网格参数
  40. const gridParams = config.knownGridParams;
  41. // 为U和V数据创建网格结构
  42. const uGrid = createGridData(uData, gridParams, 'U');
  43. const vGrid = createGridData(vData, gridParams, 'V');
  44. if (!uGrid || !vGrid) {
  45. throw new Error('网格创建失败');
  46. }
  47. // 组合成风场库需要的数组格式
  48. const outputData = [uGrid, vGrid];
  49. // 写入输出文件
  50. const outputPath = path.join(config.outputDir, config.outputFile);
  51. fs.writeFileSync(outputPath, JSON.stringify(outputData));
  52. console.log(`转换成功! 网格JSON已保存到: ${outputPath}`);
  53. console.log(`U网格信息: nx=${uGrid.header.nx}, ny=${uGrid.header.ny}`);
  54. console.log(`V网格信息: nx=${vGrid.header.nx}, ny=${vGrid.header.ny}`);
  55. } catch (error) {
  56. console.error('转换过程中出错:', error);
  57. }
  58. }
  59. // 确保输出目录存在
  60. if (!fs.existsSync(config.outputDir)) {
  61. fs.mkdirSync(config.outputDir, { recursive: true });
  62. }
  63. // 解析CSV文件的函数
  64. async function parseCSVFile(filePath, component) {
  65. return new Promise((resolve, reject) => {
  66. const results = [];
  67. let lineCount = 0;
  68. const rl = readline.createInterface({
  69. input: fs.createReadStream(filePath),
  70. crlfDelay: Infinity
  71. });
  72. rl.on('line', (line) => {
  73. if (!line.trim()) return;
  74. try {
  75. const cleanedLine = line.replace(/"/g, '');
  76. const parts = cleanedLine.split(',');
  77. if (parts.length < 7) return;
  78. const lon = parseFloat(parts[4]);
  79. const lat = parseFloat(parts[5]);
  80. const value = parseFloat(parts[6]);
  81. if (!isNaN(lon) && !isNaN(lat) && !isNaN(value)) {
  82. results.push({ lon, lat, value });
  83. }
  84. } catch (e) {
  85. console.error('解析行时出错:', line, e);
  86. }
  87. lineCount++;
  88. });
  89. rl.on('close', () => {
  90. console.log(`从 ${filePath} 解析了 ${results.length} 条 ${component} 记录`);
  91. resolve(results);
  92. });
  93. rl.on('error', reject);
  94. });
  95. }
  96. // 创建网格数据 - 使用已知的全球网格参数
  97. function createGridData(parsedData, gridParams, component) {
  98. console.log(`开始创建 ${component} 网格数据...`);
  99. const { minLon, maxLon, minLat, maxLat, dx, dy, nx, ny } = gridParams;
  100. // 创建空网格(用null填充)
  101. const gridData = new Array(nx * ny).fill(null);
  102. // 将解析的数据填充到网格中
  103. let filledCount = 0;
  104. let missingCount = 0;
  105. parsedData.forEach(point => {
  106. // 计算网格索引
  107. const i = Math.round((point.lon - minLon) / dx);
  108. const j = Math.round((maxLat - point.lat) / dy); // 注意纬度是从上到下递减
  109. const index = j * nx + i;
  110. if (index >= 0 && index < gridData.length) {
  111. gridData[index] = point.value;
  112. filledCount++;
  113. } else {
  114. missingCount++;
  115. console.warn(`点超出网格范围: (${point.lon}, ${point.lat}) -> 索引: ${index}`);
  116. }
  117. });
  118. console.log(`${component} 网格填充了 ${filledCount} 个数据点,缺失 ${missingCount} 个点`);
  119. console.log(`${component} 数据非空比例: ${(filledCount / gridData.length * 100).toFixed(2)}%`);
  120. if (filledCount === 0) {
  121. console.error(`错误: ${component} 网格没有有效数据`);
  122. return null;
  123. }
  124. return {
  125. header: {
  126. parameterCategory: 2,
  127. parameterCategoryName: "Momentum",
  128. parameterNumber: component === 'U' ? 2 : 3,
  129. parameterNumberName: component === 'U' ? "U-component_of_wind" : "V-component_of_wind",
  130. nx: nx,
  131. ny: ny,
  132. lo1: minLon,
  133. la1: maxLat,
  134. dx: dx,
  135. dy: dy,
  136. numberPoints: nx * ny,
  137. refTime: "2025-09-10T00:00:00Z"
  138. },
  139. data: gridData
  140. };
  141. }
  142. async function main() {
  143. try {
  144. console.log('开始转换为网格JSON格式...');
  145. // 并行解析U和V的CSV文件
  146. const [uData, vData] = await Promise.all([
  147. parseCSVFile(config.uCsvFile, 'U'),
  148. parseCSVFile(config.vCsvFile, 'V')
  149. ]);
  150. // 检查数据是否有效
  151. if (uData.length === 0 || vData.length === 0) {
  152. throw new Error('U或V数据为空');
  153. }
  154. // 使用已知的全球网格参数
  155. const gridParams = config.knownGridParams;
  156. // 为U和V数据创建网格结构
  157. const uGrid = createGridData(uData, gridParams, 'U');
  158. const vGrid = createGridData(vData, gridParams, 'V');
  159. if (!uGrid || !vGrid) {
  160. throw new Error('网格创建失败');
  161. }
  162. // 组合成风场库需要的数组格式
  163. const outputData = [uGrid, vGrid];
  164. // 写入输出文件
  165. const outputPath = path.join(config.outputDir, config.outputFile);
  166. fs.writeFileSync(outputPath, JSON.stringify(outputData));
  167. console.log(`转换成功! 网格JSON已保存到: ${outputPath}`);
  168. console.log(`U网格信息: nx=${uGrid.header.nx}, ny=${uGrid.header.ny}`);
  169. console.log(`V网格信息: nx=${vGrid.header.nx}, ny=${vGrid.header.ny}`);
  170. } catch (error) {
  171. console.error('转换过程中出错:', error);
  172. }
  173. }
  174. module.exports = {
  175. createWindData
  176. };