说明
因为公司业务需求,要在web前端做一个水流分析的功能。但是提供的数据并不像风场数据一样,带有方向和速度信息。只有坐标点和高程。所以简单做了一个根据点的流向分析动画,里边没有用什么很复杂的算法,所以效果不是很好,但希望能给读者带来一点功能实现上的思路。对于流水路径的计算,肯定有更好的方法,这里只做简单示例。
先上功能实现图:
数据阶段
因为本功能所用的只是带高程的点的合集,所以要对点进行分析
首先从提供的excel表中读取数据,也可能数据来源于其他文件,但最终得到的数据格式相同。
得到点的集合如下图所示:
经过高程点坐标转换与拼接,坐标系转换可参考Proj4js转换坐标的方法,这里暂不展开讲解。
最后经过数组的抽稀与拼接获得高程点集合:
后面我用了一个笨办法,其实并不好,如果路过的大佬有更好的算法请踢我一下,我要学习。
let direction = [];
allPoint.forEach((itema) => {
let arr = [];
allPoint.forEach((itemb) => {
if (itema.value > itemb.value) {
arr.push([itemb.lon, itemb.lat]);
}
});
if (arr.length !== 0) {
let targetPoint = turf.point([itema.lon, itema.lat]);
let computearr = [];
arr.forEach((item) => {
computearr.push(turf.point(item));
});
let points = turf.featureCollection(computearr);
let nearest = turf.nearestPoint(targetPoint, points);
direction.push([[itema.lon, itema.lat], nearest.geometry.coordinates]);
}
});
作为测试用,上边方法并不推荐,可以优化筛选,比如添加距离限制等。大体思路就是,遍历数组中所有的点,找到离当前点最近且比当前点高程低的点,两个点拼接成一个数组对。
最后得到如下数组对:
流线生成
利用之前筛选出的数组对,做一个流线动画,大体思路就是,一个数组对刚好连成线,从高处往低处流。
流线动画代码参考示例如下:
要定义轨迹动态线的着色器,该部分代码可以加到创建cesium地图方法里,哪儿都可以,只要能把这个材质定义上就行。
// 定义线的轨迹动态线纹理-->
function PolylineTrailLinkMaterialProperty(color, duration) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._colorSubscription = undefined;
this.color = color;
this.duration = duration;
this._time = new Date().getTime();
}
Object.defineProperties(PolylineTrailLinkMaterialProperty.prototype, {
isConstant: {
get: function () {
return false;
},
},
definitionChanged: {
get: function () {
return this._definitionChanged;
},
},
color: Cesium.createPropertyDescriptor('color'),
});
PolylineTrailLinkMaterialProperty.prototype.getType = function (time) {
return 'PolylineTrailLink';
};
PolylineTrailLinkMaterialProperty.prototype.getValue = function (time, result) {
if (!Cesium.defined(result)) {
result = {};
}
result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);
result.image = Cesium.Material.PolylineTrailLinkImage;
result.time = ((new Date().getTime() - this._time) % this.duration) / this.duration;
return result;
};
PolylineTrailLinkMaterialProperty.prototype.equals = function (other) {
return this === other || (other instanceof PolylineTrailLinkMaterialProperty && Property.equals(this._color, other._color));
};
// 在Material上挂载相关的流动线纹理 可以根据自己的需要进行封装
Cesium.PolylineTrailLinkMaterialProperty = PolylineTrailLinkMaterialProperty;
Cesium.Material.PolylineTrailLinkType = 'PolylineTrailLink';
Cesium.Material.PolylineTrailLinkImage = './images/line.png';
// 定义着色器源码 核心部分
Cesium.Material.PolylineTrailLinkSource =
'czm_material czm_getMaterial(czm_materialInput materialInput)\n\
{\n\
czm_material material = czm_getDefaultMaterial(materialInput);\n\
vec2 st = materialInput.st;\n\
vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));\n\
material.alpha = colorImage.a;\n\
material.diffuse = colorImage.rgb;\n\
return material;\n\
}';
Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailLinkType, {
fabric: {
type: Cesium.Material.PolylineTrailLinkType,
uniforms: {
color: new Cesium.Color(0.0, 0.0, 1.0, 0.5),
image: Cesium.Material.PolylineTrailLinkImage,
time: 0,
},
source: Cesium.Material.PolylineTrailLinkSource,
},
translucent: function (material) {
return true;
},
});
然后遍历数组生成流线:注意这个材质定义要放在遍历的外边,如果每次都new一个材质的话,cesium会报错。
let linematerial = new Cesium.PolylineTrailLinkMaterialProperty(
Cesium.Color.ORANGE,
3000,
'./images/line.png'
);
direction.forEach((item) => {
let position = Cesium.Cartesian3.fromDegreesArrayHeights([item[0][0], item[0][ 1], 300, item[1][0], item[1][1], 300]);
let route = Viewer.entities.add({
// name: 'Rescue Route',
polyline: {
positions: position,
width: 5,
material: linematerial,
},
});
});
以上就可以生成流线分析了,我这里用的是1.78版本cesium,注意你如果使用的是高版本的cesium,这里的GLSL语法低了,需要自己该新版本的GLSL语法。
代码中所用的line.png图片就是个渐变图,文章受限并不好发。
大体长这样:
这个文章里的水印不知道咋去...........如果需要图片素材可免费私信索要。
我的想法
本来我想象中应该能有个算法,把这些点根据流向连成一条条的线,这样会好看些,并且连成线后,可以用truf等算法库进行平滑处理,肯定比现在好看的多。类似风场动画那种。但是目前我水平有限,时间也比较紧张,没有做更多的优化处理。希望我的这个例子能给大家带来一点思路。