因为需要再Cesium中实现风场粒子效果,网上找了许多项目,大多是通过加载NC文件来进行渲染的,因此了解NC文件又成了一件重要的事。特此记录用java成果生成可在前端渲染,QGIS中正常渲染的NetCDF文件的相关代码(有没详细整理,给出了初浅的解释)。
这是Geoserver中关于观察NetCDF文件的一份文档地址:
NetCDF — GeoServer 2.26.x User Manual
文中提到的软件可以到github中去下载:Releases · Unidata/netcdf-java (github.com)
从文档中,我们指导,NC文件由维度(Dimension)、变量(Variable)等信息组成(我的应用中主要关注这两个量),同时维度又决定了变量的数组维度
言归正传,直接放代码:
final int NLon = 500; // x 轴 设置一个数组里面有几个值final int NLat = 300; // y 轴 设置一个数组里面有几个值
//设置文件输出地址String filename = "D:\\test3D.nc";NetcdfFileWriter dataFile = null;Random random = new Random();try {//参数1 : 文件格式 参数2:文件名称//先将输出nc文件的基本信息 写入dataFile = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf3, filename);//创建time depth// Create netCDF dimensions, 创建 nc 文件的维度Dimension timeDim = dataFile.addDimension(null, "time", 1); // 维度X轴 对象 值为: x = NX;Dimension depthDim = dataFile.addDimension(null, "depth", 1); // 维度Y轴 y = NY;List<Dimension> timeDims = new ArrayList<>();timeDims.add(timeDim);List<Dimension> depthDims = new ArrayList<>();depthDims.add(depthDim);Variable timeVariable = dataFile.addVariable(null, "time", DataType.DOUBLE, timeDims);Attribute timeAttri = new Attribute("units", "hours since 2021-03-23 12:00:00.000 UTC");Attribute timeDescriAttri = new Attribute("description", "Forecast time for ForecastModelRunCollection");timeVariable.addAttribute(timeAttri);timeVariable.addAttribute(timeDescriAttri);Variable depthVariable = dataFile.addVariable(null, "depth", DataType.DOUBLE, depthDims);Attribute depthAttri = new Attribute("units", "m");Attribute depthDescriAttri = new Attribute("description", "Depth");depthVariable.addAttribute(depthAttri);depthVariable.addAttribute(depthDescriAttri);//创建 nc 文件的维度Dimension lonDim = dataFile.addDimension(null, "lon", NLon); // 维度X轴 对象 值为: x = NX;Dimension latDim = dataFile.addDimension(null, "lat", NLat); // 维度Y轴 y = NY;//写入lon lat数据List<Dimension> lonDims = new ArrayList<>();lonDims.add(lonDim);Variable lonVariable = dataFile.addVariable(null, "lon", DataType.DOUBLE, lonDims);Attribute lonAttri = new Attribute("units", "degrees_east");Attribute lonDescriAttri = new Attribute("description", "Longitude");lonVariable.addAttribute(lonAttri);lonVariable.addAttribute(lonDescriAttri);List<Dimension> latDims = new ArrayList<>();latDims.add(latDim);Variable latVariable = dataFile.addVariable(null, "lat", DataType.DOUBLE, latDims);Attribute latAttri = new Attribute("units", "degrees_north");Attribute latDescriAttri = new Attribute("description", "Latitude");latVariable.addAttribute(latAttri);latVariable.addAttribute(latDescriAttri);//添加 U V变量List<Dimension> uDims = new ArrayList<>();uDims.add(timeDim);uDims.add(depthDim);uDims.add(latDim);uDims.add(lonDim);List<Dimension> vDims = new ArrayList<>();vDims.add(timeDim);vDims.add(depthDim);vDims.add(latDim);vDims.add(lonDim);Variable uVariable = dataFile.addVariable(null, "U", DataType.FLOAT, uDims);Attribute uAttri = new Attribute("units", "m/s");Attribute uDescriAttri = new Attribute("description", "Eastward Water Velocity");uVariable.addAttribute(uAttri);uVariable.addAttribute(uDescriAttri);Variable vVariable = dataFile.addVariable(null, "V", DataType.FLOAT, vDims);Attribute vAttri = new Attribute("units", "m/s");Attribute vDescriAttri = new Attribute("description", "Northward Water Velocity");vVariable.addAttribute(vAttri);vVariable.addAttribute(vDescriAttri);//创建文件 --------维度 变量的申明 一定要在创建文件前完成dataFile.create();//生成time depth变量的信息ArrayDouble.D1 timeDataOut = new ArrayDouble.D1(timeDim.getLength());for(int i = 0;i < timeDim.getLength();i++) {timeDataOut.set(i, 45.0);}dataFile.write(timeVariable, timeDataOut);ArrayDouble.D1 depthDataOut = new ArrayDouble.D1(depthDim.getLength());dataFile.write(depthVariable, depthDataOut);//生成lon lat变量的信息ArrayDouble.D1 lonDataOut = new ArrayDouble.D1(lonDim.getLength());for(int i = 0;i < lonDim.getLength();i++) {lonDataOut.set(i, 123.0+ (i*0.01));}dataFile.write(lonVariable, lonDataOut);ArrayDouble.D1 latDataOut = new ArrayDouble.D1(latDim.getLength());for(int i = 0;i < latDim.getLength();i++) {latDataOut.set(i, 23.0+ (i*0.005));}dataFile.write(latVariable, latDataOut);ArrayFloat.D4 uDataOut = new ArrayFloat.D4(timeDim.getLength(),depthDim.getLength(),latDim.getLength(),lonDim.getLength());ArrayFloat.D4 vDataOut = new ArrayFloat.D4(timeDim.getLength(),depthDim.getLength(),latDim.getLength(),lonDim.getLength());for(int t = 0;t < timeDim.getLength();t++) {for(int d = 0;d < depthDim.getLength();d++) {for(int l = 0;l < latDim.getLength();l++) {double latValue = latDataOut.get(l);for(int i = 0;i < lonDim.getLength();i++) {double lonValue = lonDataOut.get(i);uDataOut.set(t, d, l, i, Float.valueOf(random.nextDouble().toString()) );vDataOut.set(t, d, l, i, Float.valueOf(random.nextDouble().toString()));}else {uDataOut.set(t, d, l, i, Float.NaN);vDataOut.set(t, d, l, i, Float.NaN);}}}}}dataFile.write(uVariable, uDataOut);dataFile.write(vVariable, vDataOut);} catch (Exception e) {e.printStackTrace();}finally {if (null != dataFile)try {dataFile.close();} catch (IOException ioe) {ioe.printStackTrace();}}
初浅理解NC文件,可以看做GIS中的栅格数据,上面的做法其实就是划分了宽、高分别为NLon,NLat的矩形区域,让后对每块栅格进行赋值,所以才会有U、V的变量的值要考虑lon、lat的纬度影响。
使用上文提到的数据集查看工具,进行详细观察,相信你也会逐渐了解NC文件的结构。
差点忘了,我这里用到的maven依赖是:
<dependency><groupId>edu.ucar</groupId><artifactId>netcdf-java</artifactId><version>5.3.3</version>
</dependency>
我jdk用的17,jdk8不知道是否还支持。