准备工作
- turfjs, 空间分析(geospatial analysis)工具包,支持在浏览器和 Node.js 环境中运行,空间数据的输入输出使用 GeoJSON 编码。本文对应的版本是 turfjs@5.1.6 。
-
散点数据,也就是业务数据,可以是一些观测点的温度或降雨量。这里以降雨量为例
var rainData = [
{ name: '山东头1#水库水文站', lon: 120.436569, lat: 36.964243, value: '52.4' },
{ name: '午山水库水文站', lon: 120.287671, lat: 37.060722, value: '19.7' },
{ name: '中韩街道办事处雨量站', lon: 120.450068, lat: 36.136672, value: '11.7' },
{ name: '大石村(Q)雨量站', lon: 120.551003, lat: 36.183102, value: '81.7' },
{ name: '流清河水库水文站', lon: 120.613795, lat: 36.133937, value: '31.7' },
{ name: '晓望水库水文站', lon: 120.641786, lat: 36.246563, value: '41.7' },
{ name: '长岭雨量站雨量站', lon: 120.683333, lat: 36.166667, value: '49.7' },
{ name: '曲家沟雨量站', lon: 120.594091, lat: 36.312239, value: '10.7' },
{ name: '大石村(Q)雨量站', lon: 120.554879, lat: 37.004007, value: '81.7' },
{ name: '流清河水库水文站', lon: 120.033822, lat: 36.245937, value: '31.7' },
{ name: '晓望水库水文站', lon: 120.427773, lat: 36.465302, value: '50.7' },
{ name: '长岭雨量站雨量站', lon: 120.233758, lat: 36.427334, value: '49.7' },
{ name: '曲家沟雨量站', lon: 120.40661, lat: 36.768867, value: '13.7' },
{ name: '流清河水库水文站', lon: 119.808603, lat: 36.054676, value: '27.0' },
{ name: '流清河水库水文站', lon: 120.033823, lat: 36.497519, value: '38.9' },
{ name: '晓望水库水文站', lon: 120.420731, lat: 37.45839, value: '69.7' },
{ name: '长岭雨量站雨量站', lon: 119.733072, lat: 35.907987, value: '49.7' },
{ name: '曲家沟雨量站', lon: 19.4633, lat: 36.009421, value: '13.7' },
{ name: '流清河水库水文站', lon: 119.891767, lat:35.842613, value: '38.9' },
];
-
一个边界数据,用于裁剪出一幅针对某一区域干净的色斑图,格式为 GeoJSON。推荐使用 geojson.io 来查看或编辑这类数据,这里给出一个城市的
boundaries
做示例。
请前往 下载资源中免费下载geojson 数据geojson测试数据
在页面中引入 import * as turf from '@turf/turf';
在页面中引入geojson数据 你本地的地址import { areaJson } from '@/assets/json/area';
加载cesium 地图 这里不做过多赘述
let boundaries = areaJson;
mounted(){
// Cesium token授权
Cesium.Ion.defaultAccessToken ='你的cesiumToken';
// 场景地图基础配置
viewer = new Cesium.Viewer('cesiumContainer', {
infoBox: false, //默认弹窗控件
geocoder: false, // 地理位置查询定位控件
vrButton: false, //VR按钮控件
homeButton: false, // 默认相机位置控件
animation: false, // 控制场景动画的播放速度控件
timeline: false, // 时间滚动条控件
navigationHelpButton: false, // 默认的相机控制提示控件
fullscreenButton: false, // 全屏控件
sceneModePicker: false, //场景模式切换控件
baseLayerPicker: false, // 底图切换控件
selectionIndicator: false, //对象指示器控件
imageryProvider: new Cesium.SingleTileImageryProvider({
url: this.createColorCanvas('#0a1729F'),
rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0),
}),
});
// 添加等值线图
this.handleInterscetion(rainData, boundaries);
}
利用 turf.js 处理降雨量数据
-
// 处理得到等值线面数据 添加到cesium地图上 handleInterscetion(rainStation, boundaries) { let arr = rainStation.map((it) => it.value); let max = Math.max(...arr.filter((item) => typeof item === 'number')); console.log(`output->降雨最大值max`, max); let breakList = []; if (max > 100) { breakList = [0, 2.5, 5, 10, 25, 50, 100, max]; } else { breakList = [0, 2.5, 5, 10, 25, 50, 100]; } let rainfeatures = rainStation.map((i) => { return { type: 'Feature', properties: { value: i.value, }, geometry: { type: 'Point', coordinates: [i.lon, i.lat], }, }; }); // console.log('rainfeatures', rainfeatures); var points = turf.featureCollection(rainfeatures); // console.log('points', points); var interpolate_options = { gridType: 'points', property: 'value', units: 'degrees', weight: 4, }; //turf.interpolate() 提供了基于 IDW(反距离权重)算法的将数据插值为格点的方法。插值的精度是由第二个参数与 interpolate_options.units 共同决定的,单位支持 degrees, radians, miles, or kilometers,IDW 要为每个格点计算所有散点的权重,计算规模是 (散点数 * 格点数),所以要在精度与性能间做好平衡。 我们将之前的散点(points)代入 let grid = turf.interpolate(points, 0.05, interpolate_options); // 适当降低插值结果的精度便于显示 grid.features.map((i) => (i.properties.value = i.properties.value.toFixed(2))); var isobands_options = { zProperty: 'value', commonProperties: { 'fill-opacity': 0.8, }, //颜色 breaksProperties: [ { fill: '#DCF6D3' }, { fill: '#B2F9A0' }, { fill: '#6CD869' }, { fill: '#59B83D' }, { fill: '#78B8F1' }, { fill: '#1111F4' }, { fill: '#ED0FED' }, ], }; let isobands = turf.isobands(grid, breakList, isobands_options); //这一步基于插值获得的格点绘制等值区域,并为区域配置颜色。turf.isobands() 根据 zProperty 分段,形成一些 MultiPolygon。 boundaries = turf.flatten(boundaries); isobands = turf.flatten(isobands); let features = []; //利用准备的边界来裁剪整个色斑图 isobands.features.forEach(function (layer1) { boundaries.features.forEach(function (layer2) { let intersection = null; try { intersection = turf.intersect(layer1, layer2); } catch (e) { layer1 = turf.buffer(layer1, 0); intersection = turf.intersect(layer1, layer2); } if (intersection != null) { intersection.properties = layer1.properties; intersection.id = Math.random() * 100000; features.push(intersection); } }); }); let intersection = turf.featureCollection(features); //先清除所有的实体 viewer.entities.removeAll(); Cesium.GeoJsonDataSource.load(intersection).then((dataSource) => { let entities = dataSource.entities.values; for (let i = 0; i < entities.length; i++) { let entity = entities[i]; let positions = entity.polygon.hierarchy._value.positions; //设置面轮廓 entity.polyline = new Cesium.PolylineGraphics({ positions: positions, // clampToGround: true, // 贴地 width: 1, // material: Cesium.Color.WHITE.withAlpha(0.01) }); entity.polygon.outline = false; entity.polygon.height = 1800; // entity.polygon.height = 10; viewer.entities.add(entity); } }); },