前端开发过程中会有各种的图表效果,今天讲一下等值线,首先我使用的是克里斯金这个插kriging.js,
还有就是turf.js插件。上代码安装完插件之后引入
import * as Cesium from 'cesium'
// import kriging from '@sakitam-gis/kriging';
import * as turf from '@turf/turf'
const lineGeoJson = turf.lineString(list)
// 缓冲距离(单位:米)
const bufferDistance = 25
// 使用Turf库的缓冲函数进行线的缓冲
const bufferedLine = turf.buffer(lineGeoJson, bufferDistance, {
units: 'meters'
})
const res: any = [[]]
const resa: any = []
bufferedLine.geometry.coordinates[0].forEach((element) => {
res[0].push([element[0], element[1]])
resa.push(element[0])
resa.push(element[1])
})
const len = res[0].length - 1
if (res[0][len] != undefined) {
res[0].push(res[0][len])
}
// console.log("res", res);
this.extent = Cesium.PolygonGeometry.computeRectangle({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(resa)
)
});
这里的turf.buffer方法是可以把传入的点生成一个缓冲区 这样的话传入的数据生成的画面不至于太纤细,具体效果可以看,缓冲区
let canvasViewer: any = null
canvasViewer = document.createElement('canvas');
canvasViewer.width = this.IsolineWidth;
canvasViewer.height = this.IsolineHeight;
canvasViewer.getContext('2d').globalAlpha = 1;//设置透明度
const n = list.length;
const value: any = [];
const lngs: any = [];
const lats: any = [];
for (let i = 0; i < n; i++) {
// 高度值
const r = Number(Math.random() / 100);
value.push(list[i][2] + r);
// 经纬度点
lngs.push(list[i][0]);// 经度
lats.push(list[i][1]); // 纬度
}
const colors = [
{min:10,max:20,color: '#f1b079'},
{min:20,max:40,color: '#f1b079'}
...
];
// console.log('value', value);
// console.log('lngs', lngs);
// console.log('lats', lats);
const minlng: any = Cesium.Math.toDegrees(this.extent.west);//转换为经纬度
const minlat: any = Cesium.Math.toDegrees(this.extent.south);
const maxlng: any = Cesium.Math.toDegrees(this.extent.east);
const maxlat: any = Cesium.Math.toDegrees(this.extent.north);
const variogram = kriging.train(value, lngs, lats, "exponential", 0, 200);
const grid = kriging.grid(res, variogram, (maxlat - minlat) / 500);
//将得到的格网grid渲染至canvas上。
kriging.plot(canvasViewer, grid, [minlng, maxlng], [minlat, maxlat], colors);
以上是用插件把生成效果添加到,canvas画布上 然后把生成的画布添加到cesium上去
this.viewer.entities.add({
id: "isolineView",
polygon: {
show: true,
// clampToGround: true,
hierarchy: new Cesium.CallbackProperty(function () {//回调函数实现叠加的多边形画面随数据变化而动态变化,直接赋值的话显示会闪烁
return {
positions: Cesium.Cartesian3.fromDegreesArray(coords_data)
}
}, false),
material: new Cesium.ImageMaterialProperty({//填充多边形的材质的属性
image: canvas,//使用贴图的方式将结果贴到面上
transparent: true,
color: new Cesium.Color(1.0, 1.0, 1.0, 0.65),
})
}
})
添加完等值线图之后把区域线条也添加上去就完事了
const pointGrid = this.toGeometry(grid.targetArr)
const lines = turf.isolines(pointGrid, breaks, { zProperty: 'temperature' })
const pointArrs = this.poinspips(lines, res)
// Cesium.GeoJonDataSource.clampToGround = false
Cesium.GeoJsonDataSource.load(pointArrs, {
clampToGround: true,
stroke: new Cesium.Color(255, 250, 250, 1),
strokeWidth: 0.75,
}).then((dataSource) => {
if (this.viewer.dataSources.getByName('geojson_map')) {
this.viewer.dataSources.remove(this.viewer.dataSources.getByName('geojson_map')[0])
}
// 添加 geojson
this.viewer.dataSources.add(dataSource);
// 给定义好的 geojson 的 name 赋值(这里的 dataSource 就是定义好的geojson)
dataSource.name = "geojson_map";
})
/* 设置格式 */
public toGeometry(stationData) {
const dataset: any = {
"type": "FeatureCollection",
"features": []
};
for (let i = 0; i < stationData.length; i++) {
const geom = {
"type": "Point",
"coordinates": [stationData[i][0], stationData[i][1]]
};
dataset.features.push({
"type": "Feature",
"properties": {
"temperature": stationData[i][2],
},
"geometry": geom
});
}
return dataset;
}
}
当然了里面还有turf.isolines处理过后的geojson数据这是为了cesium加载数据方便,下面是数据的格式,根据具体需求去处理
pointss: [
[114.33082, 22.80038, 21.52160168],
[114.3308447, 22.80004, 30.90271926],
[114.3308367, 22.79961, 140.4738089],
[114.3308033, 22.79927, 300.1216358],
[114.3307886, 22.79887, 39.76725519],
[114.3307744, 22.79857, 89.51673284],
...
]
数组里面前面是经纬度 后面是具体的数据值,也可以根据具体的颜色范围设置自己的颜色值区间,这样的话需要修改kriging.js赋值方式,这样的话就不能通过npm安装插件了,需要手动下载kriging.js放到静态文件里面引入下面是kriging.js源码修改,turf.isolines方法可以看看
kriging.plot = function (canvas, grid, xlim, ylim, colors, idx) {
// Clear screen
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Starting boundaries
var range = [xlim[1] - xlim[0], ylim[1] - ylim[0], grid.zlim[1] - grid.zlim[0]];
var i, j, x, y, z;
var n = grid.length;
var m = grid[0].length;
var wx = Math.ceil(grid.width * canvas.width / (xlim[1] - xlim[0]));
var wy = Math.ceil(grid.width * canvas.height / (ylim[1] - ylim[0]));
for (i = 0; i < n; i++)
for (j = 0; j < m; j++) {
if (grid[i][j] == undefined) continue;
x = canvas.width * (i * grid.width + grid.xlim[0] - xlim[0]) / range[0];
y = canvas.height * (1 - (j * grid.width + grid.ylim[0] - ylim[0]) / range[1]);
z = (grid[i][j] - grid.zlim[0]) / range[2];
if (z < 0.0) z = 0.0;
if (z > 1.0) z = 1.0;
zs = grid[i][j]
// ctx.fillStyle = colors[Math.floor((colors.length - 1) * z)];
ctx.fillStyle = colors[getaidx(zs, idx)];//这个方法做了修改
// ctx.fillStyle = colors[Math.floor((colors.length - 1) * z)];
ctx.fillRect(Math.round(x - wx / 2), Math.round(y - wy / 2), wx, wy);
}
};
getaidx = function (v, d) {//这个方法做了修改
for (let i = 0; i < d.length; i++)
if (v <= d[i]) {
return i;//根据值来选定颜色索引号,小于等于
}
return d.length;
}
最后效果看下