1.格点数据说明
1.1 数据来源
格点数据的原始数据一般是netcdf数据或grib2数据,然后解析后的数据。
1.2 数据传输
需要将格点气象数据实现前端的展示,数据传输的方式有三种:
- json
- 二进制 (前端转json)
- 灰度图 (暂时不讲,不知道具体如何实现)
1.3 数据解析
{
"header": {
"la1": 54,
"lo1": 73,
"la2": 18,
"lo2": 136,
"nx": 630,
"ny": 360
},
"data": [
......
]
}
- 说明:
header
为头文件,用以说明数据的信息,其中:la1
为左上,lo1
为左下,la2
为右下,lo2
为右上,nx
为行数,ny
为列数。data
为数据,数据是从左上开始逐行将格点的气象数据转换为一个一维数组。
2 色斑图渲染
2.1原理
- 根据网格点的值进行颜色插值。
- idw插值(反距离权重)
2.2 实现
2.2.1二维实现
准备 如要生成全球 bound应为-90 --90 -180--180,分辨率稍微大点。
let tileLayer = DRAWIMGfun(data, {
color: rgba, //阈值,必传参数
tile_size: 1290, //设置渲染分辨率,越大越清晰
gradient: 0.25, //设置渐变度,越小渐变越小,最大0.5,0.25是完全渐变
opacity: 0.6,
pane: "radar",
noWrap: false,
viewer: viewer,
opacityThreshold: null,
boundData: bound,//必传
});
首先生成网格
this.setGrid = function(data) {
//因为地理位置的原因,lat坐标要反转
// this.grid_data = data.data.reverse();
data.data = data.data.reverse();
this.grid = {
latmin: data.latmin,
latmax: data.latmax,
lonmin: data.lonmin,
lonmax: data.lonmax,
nlat: data.nlat,
nlon: data.nlon,
ele: data.ele,
interval_lat: data.interval_lat,
interval_lng: data.interval_lng,
}
this.grid_data = []
var p = 0;
var isContinuous = Math.floor(data.nlon * data.interval_lng) >= 360;
//x方向的跨度乘以x方向的数量是否大于360
for (var j = 0; j < data.nlat; j++) {
var row = [];
for (var i = 0; i < data.nlon; i++) {
row[i] = data.data[(j * data.nlon) + i];
}
if (isContinuous) {
// For wrapped grids, duplicate first column as last column to simplify interpolation logic
row.push(row[0]);
}
this.grid_data[j] = row;
}
// console.log(data.data, this.grid_data);
//grid是一个二维数组
//第一纬表示行数
//第二纬表示列数
//值为uv
// [
// [
// 'uv'
// ]
// ]
}
然后绘制
原理是根据经纬度边界与canvas总像素与网格数据对应,得到像素与经纬度对应的值,根据值对应色带设置像素的rgba.
this.draw = function(ctx, tile_latlng, tile) {
var color = this.color;
var grid = this.grid;
var grid_Data = this.grid_data;
var vs = [];
setTimeout(function() {
var rgba = ctx.createImageData(this.tile_size, this.tile_size);
for (var i = 0; i < this.tile_size; i++) {
vs.push([])
for (var j = 0; j < this.tile_size; j++) {
// 只需要得到经纬度的边界值(tile_latlng)就可插值
// j, i为像素值 --> 得到权重值插值 --> 得到颜色值
var latlng = imgfun.get_px_latlng(tile_latlng, j, i, this.tile_size);
// var latlng = this.pixelPointToLayerPoint(tile_latlng.p, j, i, tile_latlng.z, this.tile_size);
var v = imgfun.insertData(latlng.lat, latlng.lng, grid, grid_Data);
var thisrgb = imgfun.getrgba(v, color, this.gradient);
rgba.data[(i * this.tile_size + j) * 4 + 0] = thisrgb[0];
rgba.data[(i * this.tile_size + j) * 4 + 1] = thisrgb[1];
rgba.data[(i * this.tile_size + j) * 4 + 2] = thisrgb[2];
rgba.data[(i * this.tile_size + j) * 4 + 3] = thisrgb[3] *
this.opacity;
vs[i].push(v);
}
}
// console.log(vs);
ctx.putImageData(rgba, 0, 0);
// ctx.globalAlpha = this.opacity;
tile.style.pointerEvents = 'initial';
}.bind(this), 50)
}
get_px_latlng: function(t, x, y, tile_size) {
//参数:瓦片的最大最小经纬度,瓦片画布像素,需要计算的点的x,y坐标
var lng = t.lonmin + (t.lonmax - t.lonmin) * x / tile_size;
var lat = t.latmax - (t.latmax - t.latmin) * y / tile_size;
return { lat, lng };
},
2.2.2三维实现
原理与二维一样,将生成的canvas图片以贴图形式展示即可。
// 不宜使用entitiy类因为entitiy类更倾向具体实体,不易用作全局场景,可能会有材质问题(展示不全,切换视角消失图块等)。
// this.boundData.lonmax = this.boundData.lonmax - 180;
// this.boundData.lonmin = Number(this.boundData.lonmin) - 180;
// this.viewer.entities.add({
// rectangle: {
// coordinates: Cesium.Rectangle.fromDegrees(this.boundData.lonmin, this.boundData.latmin, this.boundData.lonmax, this.boundData.latmax),
// material: new Cesium.ImageMaterialProperty({
// image: tile.toDataURL("image/png"), //使用贴图的方式将结果贴到面上
// // transparent: true
// }),
// },
// });
//全局底图倾向Scene类表示全局,不易使用entitiy类因为entitiy类更倾向具体实体,不宜用作全局场景,可能会有材质问题(展示不全,切换视角消失图块等)。
//使用SingleTileImageryProvider 是 Cesium 中的一个类,用于表示单个图块的影像数据源。无材质bug
this.meteLayer = this.viewer.imageryLayers.addImageryProvider(new Cesium.SingleTileImageryProvider({
url: tile.toDataURL("image/png"),
tileWidth: 256, // 图块宽度
tileHeight: 256, // 图块高度
ellipsoid: Cesium.Ellipsoid.WGS84
}));
this.meteLayer._id = "meteLayerProvider";