气象数据NC、grb2解析成矢量json、CMIS、MICPS及图片应用到webgis

兴诚 2024-07-07 13:03:03 阅读 80

一、基础概念

气象数据通常以多种格式存储和交换,以适应不同的应用需求和处理工具。以下是一些常见的气象数据格式及其转换方法的概述:

常见气象数据格式

1. NetCDF(Network Common Data Form):一种自描述、自包含的数据格式,广泛用于存储气象和气候模型输出、观测数据等。NetCDF支持多维度数组、属性和变量,便于跨平台共享。

2. GRIB(GRIdded Binary):主要用于气象预报产品,包括GRIB1和GRIB2两种版本。它以高效的方式存储大量的气象格点数据,如温度、风速等,是气象预报中心之间交换数据的标准格式。

3. HDF(Hierarchical Data Format):类似于NetCDF,支持复杂的数据结构,适用于大规模科学数据集的存储和分发。HDF4和HDF5是两个主要版本。

4. CSV(Comma-Separated Values):简单的文本格式,易于阅读和处理,常用于数据交换,但缺乏自描述性,不包含元数据。

5. GeoTIFF:一种地理空间图像格式,用于存储栅格数据,如卫星图像或气象地图,包含地理坐标信息。

6. BUFR(Binary Universal Form for the Representation of Meteorological Data):国际气象组织推荐的一种二进制编码格式,特别适用于气象观测数据的交换。

数据格式转换方法

1. 使用专业软件:

•Panoply:NASA开发的免费软件,可以打开、查看和转换NetCDF、HDF、GRIB等格式数据。•NCAR Command Language (NCL):提供丰富的脚本语言功能,用于处理NetCDF、GRIB等气象数据。

•GDAL/OGR:开源地理空间库,提供了广泛的格式转换能力,支持NetCDF、HDF、GeoTIFF等格式的转换。

2. 编程语言库:

•Python: 利用netCDF4, pygrib, h5py等库进行数据读写和格式转换。xarray库提供了更高级的数据结构,方便处理NetCDF和GRIB数据。

•MATLAB:内置了对NetCDF、HDF的读写支持,也可通过第三方工具箱处理其他格式。

•R语言:使用ncdf4、raster、rgdal等包处理NetCDF、GRIB、GeoTIFF等格式。

3. 在线转换工具:

虽然不如上述方法灵活,但存在一些在线工具,允许用户上传文件并转换为不同格式,不过这类工具在处理大型气象数据集时可能受限。进行数据格式转换时,需注意保留数据的完整性和准确性,特别是元数据信息,以确保转换后的数据在后续分析中仍然有效。

二、实践应用

关键代码实现

<code>package com.netcdf.controller;

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONArray;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;

import static com.netcdf.util.NcUtil.*;

@Controller

@RequestMapping(value = "/ReadGridNc")

public class ReadGridNcData {

//http://localhost:8087/NetcdfServer/ReadGridNc/getNcData?FilePath=20210708_2021070804.nc&FilterBound=&valField=UGRD_10maboveground

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcData", method = RequestMethod.GET)

public @ResponseBody

Map GetNcData(String FilePath,String FilterBound,String valField) {

Map map = AnalysisNC(FilePath, FilterBound,valField,1);

return map;

}

//http://localhost:8087/NetcdfServer/ReadGridNc/GetNcWindData?FilePath=2023042323.grb2

/**

*

* @param FilePath

* @return

*/

@RequestMapping(value = "/GetNcWindData", method = RequestMethod.GET)

public @ResponseBody

JSONArray GetNcWindData(String FilePath) {

String map = AnalysisNcWind(FilePath);

return JSON.parseArray(map);

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataSurface?FilePath=2022090212.nc&FilterBound=&valField=PRES_surface&intvRanges=0,100000,10005,100010,100015&boundPath=1&isIDW=true&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcDataSurface", method = RequestMethod.GET)

public @ResponseBody

String GetNcDataSurface(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

Map map = AnalysisNC(FilePath, FilterBound,valField,1);

if(map!=null) {

String strGeojson = nc2EquiSurface(map, dataInterval,isClip,boundPath,isIDW);

return strGeojson;

}

return "";

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataSurface1?FilePath=wrf_ll.nc&FilterBound=&valField=RH2M&intvRanges=0,100000,10005,100010,100015&boundPath=1&isIDW=true&isClip=false

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataSurface1?FilePath=wrf_ll.nc&FilterBound=&valField=RH2M&intvRanges=0,70,75,80,85,90,95&boundPath=1&isIDW=false&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcDataSurface1", method = RequestMethod.GET)

public @ResponseBody

String GetNcDataSurface1(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

Map map = AnalysisNC1(FilePath, FilterBound,valField,3);

// Map map = AnalysisNC1(FilePath, FilterBound,valField);

if(map!=null) {

String strGeojson = nc2EquiSurface1(map, dataInterval,isClip,boundPath,isIDW);

return strGeojson;

}

return "";

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataMICPS?FilePath=wrf_ll.nc&FilterBound=&valField=RH2M&intvRanges=0,70,75,80,85,90,95&boundPath=1&isIDW=false&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcDataMICPS", method = RequestMethod.GET)

public @ResponseBody

String GetNcDataMICPS(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

Map map = AnalysisNC1(FilePath, FilterBound,valField,1);

// Map map = AnalysisNC1(FilePath, FilterBound,valField);

if(map!=null) {

StringBuilder strGeojson = new StringBuilder();

strGeojson.append("diamond 4 2022年4月1日GFS地面相对湿度3小时预报\n" +

" 2022 4 1 0 3 2 0.5 -0.5 \n" +

" 96.5 106.5 29.5 20.5 \n" +

" 1001 901 5 -50 50 1 0\n");

strGeojson.append(map.get("val").toString().replace("[","").replace("]","").replaceAll(","," "));

return strGeojson.toString();

}

return "";

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataCMISS?FilePath=wrf_ll.nc&FilterBound=&valField=RH2M&intvRanges=0,70,75,80,85,90,95&boundPath=1&isIDW=false&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcDataCMISS", method = RequestMethod.GET)

public @ResponseBody

String GetNcDataCMISS(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

Map map = AnalysisNC1(FilePath, FilterBound,valField,1);

// Map map = AnalysisNC1(FilePath, FilterBound,valField);

if(map!=null) {

StringBuilder strGeojson = new StringBuilder();

strGeojson.append("{\"returnCode\":\"0\",\"returnMessage\":\"Query Succeed\",\"startLat\":29.5,\"startLon\":96.5,\"endLat\":20.5,\"endLon\":106.5,\"latCount\":901,\"lonCount\":1001,\"latStep\":-0.01,\"lonStep\":0.01,\"requestParams\":\"minlat=20.5&time=20190810000000&fcstele=VVP&datacode=NAFP_FOR_FTM_HIGH_JAP_NEHE&maxlat=29.5&fcstlevel=850&maxlon=106.5&validtime=0&minlon=96.5\",\"requestTime\":\"2019-08-20 08:02:24\",\"responseTime\":\"2019-08-20 08:02:24\",\"takeTime\":\"0.022\",\"fieldNames\":\"湿度\",\"fieldUnits\":\"Pa*s^-1\",\"DS\":[");

strGeojson.append(map.get("val").toString());

strGeojson.append("]}");

return strGeojson.toString();

}

return "";

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getNcDataSurfaceWind?FilePath=2024050914.nc&FilterBound=&intvRanges=0,2,4,6,8,10&boundPath=1&isIDW=true&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getNcDataSurfaceWind", method = RequestMethod.GET)

public @ResponseBody

String GetNcDataSurfaceWind(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

//导出图片

AnalysisNCWindyToPng(FilePath, FilterBound,"UGRD_10maboveground","VGRD_10maboveground");

// Map map = AnalysisNCWindy(FilePath, FilterBound,"UGRD_10maboveground","VGRD_10maboveground");

// if(map!=null) {

// String strGeojson = nc2EquiSurface(map, dataInterval,isClip,boundPath,isIDW);

// return strGeojson;

// }

return "nc";

}

//http://localhost:8088/NetcdfServer/ReadGridNc/getGribDataSurfaceWind?FilePath=2024050914.grb2&FilterBound=&intvRanges=0,1,2,3,4,5,6,7,8,9,10,11,12&boundPath=1&isIDW=true&isClip=false

/**

*

* @param FilePath

* @param FilterBound

* @param valField

* @return

*/

@RequestMapping(value = "/getGribDataSurfaceWind", method = RequestMethod.GET)

public @ResponseBody

String GetGribDataSurfaceWind(String FilePath,String FilterBound,String valField,String intvRanges,String boundPath,boolean isIDW,boolean isClip) {

String[] Ranges=intvRanges.substring(0,intvRanges.length()).split(",");

double[] dataInterval = new double[Ranges.length];

for(int i=0;i<Ranges.length;i++){

dataInterval[i]=Double.valueOf(Ranges[i]);

if( i == 1 && dataInterval[i] == 0.0){

dataInterval[i] = 0.0001;

}

}

//导出图片

// AnalysisNCWindyToPng(FilePath, FilterBound,"UGRD_10maboveground","VGRD_10maboveground");

AnalysisGribWindyToPng(FilePath, FilterBound,"u-component_of_wind_height_above_ground","v-component_of_wind_height_above_ground");

return "grib";

}

}

<!DOCTYPE html>

<html>

<head>

<title>Layers Control Tutorial - Leaflet</title>

<meta charset="utf-8" />code>

<meta name="viewport" content="width=device-width, initial-scale=1.0">code>

<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />code>

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>code>

<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>code>

<style>

html, body {

height: 100%;

margin: 0;

}

#map {

width: 1000px;

height: 700px;

}

</style>

</head>

<body>

<div id='map'></div>code>

<script>

var grayscaleLayer= L.tileLayer('http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}', {id: 'map11',maxZoom: 16,minZoom: 4});

var map = L.map('map', {

minZoom: 5,

maxZoom: 12,

center: [21, 100],

zoom: 10,

zoomDelta: 0.5,//点击+-按钮的放缩刻度尺度,默认值1

zoomSnap: 0.5,//地图能放缩的zoom的最小刻度尺度,默认值1

fullscreenControl: false,//全屏控件,不显示

zoomControl: false,//放大缩小控件,不显示

attributionControl: false//右下角属性控件,不显示

});

map.addLayer(grayscaleLayer);

var imageBounds = [[20.5, 96.5], [29.41, 106.5]];//图片的经纬度范围,西南角点,东北角点(纬度、经度)

var imageUrl='pixel_art.png';//图片的地址code>

var imageLayer =L.imageOverlay(imageUrl, imageBounds,{opacity:0.8});//opacity是透明度

map.addLayer(imageLayer);

//如果imageLayer已经创建,后续只需要切换url即可

if(imageLayer!=null)

{

imageLayer.setUrl(imageUrl);

}

</script>

</body>

</html>

转换成图片成果

示例调用实现

转换成矢量json成果

转换成MICPS成果

转换成CMIS成果

 前端可视效果

成果应用展示

参照windy配置可视化实现

 如果对您有所帮助,请点赞打赏支持!

技术合作交流qq:2401315930

最后分享一下地图下载器设计及下载地址:

链接:https://pan.baidu.com/s/1RZX7JpTpxES-G7GiaVUxOw 

提取码:61cn

地图下载器代码结构设计及功能实现_地图下载管理器解析-CSDN博客



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。