GIS是什么?GIS能做什么?相信很多人接触过GIS相关的业务,但对其概念并没有一个明确的解释。在开始今天的主题之前,我们先来了解一下GIS。
GIS是地理信息系统的简称,他将真实的地理信息数据通过采集、处理、分析,最后以可视化的形式将数据展示出来。在Web端,GIS一般负责:
- BIM模型、倾斜摄影模型等数据的加载,场景模拟
- 矢量数据效果可视化,地理数据的渲染展示
- 数据的三维分析,提供分析结果与建议决策
今天的主题,就从矢量数据中的矢量线说起。
矢量线的应用有很多,其中应用较广泛的便是轨迹线。想必在项目中接触过的伙伴,对轨迹线并不陌生,智慧工地GPS数据整合成轨迹路线,大屏也需要展示轨迹线三维酷炫的可视化效果,产品经理们为了如何在项目上合理有效优雅的展示轨迹线,可谓是绞尽了脑汁。但从开发的角度,无论轨迹线的展现形式如何变,都离不开最基础的两种方式:
- Material-自定义动画材质
- Clock-计时器模拟动画
- Material – 自定义动画材质
Material自定义动画材质原理是利用glsl语言,通过着色器自定义编译将材质在wegl上渲染出来,以两种材质举例,看看自定义材质的渲染效果。
- 流动线
首先,我们要有一张纹理贴图
接着,我们需要将纹理贴图传入着色器中
uniforms: {
color: Cesium.Color.fromCssColorString(‘#7ffeff’),
image: flowImage, //纹理贴图
speed: 10
}
然后,编写着色器代码,对纹理贴图根据帧数变化进行动态采样
const source =`czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
vec4 colorImage = texture2D(image, vec2(fract((st.s – speed * czm_frameNumber * 0.001)), st.t));
material.alpha = colorImage.a * color.a;
material.diffuse = color.rgb;
return material;
}`;
最后,创建矢量对象,并将材质对象赋予他,渲染出来的效果便是:
- 动画线
跟流动线的实现原理相似,只是所用的纹理贴图不同,例如纹理贴图是这样的:
最后着色器动态渲染出来的效果是:
- 应用案例
流动线和动画线,单条线路展示的时候,可视化效果并没有很理想突出,但是当我们应用到实际项目中,或者有大数据量支撑时,配合达到的效果就很清晰直观。
例如北京公交线的应用案例,本质就是流动线的纹理贴图,再对着色器的编译代码进行了一些改动,不同矢量数据分级分类,最后动态渲染展示。
再例如民航航线的OD线,这类数据量大又带有时间信息的数据,适合用动画来展示时态。实现的方式也是对着色器的重新自定义代码再编译处理,虽然没有像动画线一样用材质纹理贴图。
Material自定义材质贴图适合需要循环动态动画展示的数据,那如何让轨迹线根据时间选择动起来呢?
- Clock – 计时器模拟动画
众所周知,轨迹线是由数个点位连接形成的线段,那如何让轨迹线动起来就简单多了,只要增减点位达到动态绘制轨迹线的效果即可。但在此之前,我们还需要将点位进行均匀插值,让其动画的过程中匀速平滑。例如下面代码中就是以一米为间隔进行均匀插值。
getLinearSpline(positions, step = 1) {
let result = [];
for (let i = 0; i <= positions.length – 2; i++) {
let distance = Cesium.Cartesian3.distance(positions[i], positions[i + 1]);
if (distance === 0) continue;
let interpolation = Math.ceil(distance / step);
result = result.concat(.getLinearItem([positions[i], positions[i + 1]], interpolation));
}
return result;
}
getLinearItem(points, interpolation) {
let times = [];
points.map((item, index) => {
times.push(index)
});
let spline = new Cesium.LinearSpline({
times: times,
points: points,
});
let coordinates = [];
let splineCount = times[times.length – 1] * interpolation;
for (let i = 0; i <= splineCount; i++) {
let cartesian3 = spline.evaluate(i / interpolation);
coordinates.push(cartesian3);
}
return coordinates;
}
插值后的数据,是一条以一米一米为间隔的,均匀分布点位的线段,用Clock对象模拟计时器有两种方式。
2.1固定时间间隔动态增加点位数据
在全局clock对象上注册监听事件,全局clock对象会在每帧渲染时触发事件,在事件中动态增加点位数据,重新绘制轨迹线,达到轨迹线动态生长的效果。
let listenerCallback = viewer.clock.onTick.addEventListener(function () {
index++;
if(index >= coordinates.length){
index = 0;
animationCoordinates = [];
}
let tempPosition = coordinates[index];
if(tempPosition){
animationCoordinates.push(tempPosition);
createPolyline(animationCoordinates);
}
});
此方法简单快速的实现了轨迹线动起来,但是缺点是无法根据全局Clock对象系统时间的变化进行速度的变化,只能每帧固定触发事件,而每帧渲染的时间又不受人为自定义调控。
2.2SampledPositionProperty时间采样点
将线段上每个点位,按照时间采样点顺序对应,设置全局Clock对象时间速率,循环时间段等配置,再注册监听事件,在每个触发事件里,监听时间采样点的位置,将此位置赋予给轨迹线,动态触发动态赋予的过程中,实现轨迹线动起来的效果。
let positionProperty = new Cesium.SampledPositionProperty();
let start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
let stop = Cesium.JulianDate.addSeconds(start, points.length – 1, new Cesium.JulianDate());
positionProperty.addSample(start, points[0]);
for(let i = 1; i <= points.length – 1; i++){
let time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
positionProperty.addSample(time, points[i]);
}
const viewer = this.viewer;
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 20;
listenerCallback = viewer.clock.onTick.addEventListener(function(){
let tempPosition = positionProperty.getValue(viewer.clock.currentTime);
//do something
});
viewer.clock.shouldAnimate = true;
此方法可以根据全局Clock对象系统时间的变化进行速度的变化,在有进度条的情况下,能更好掌握轨迹线生长的进度。但缺点就是轨迹线所有的点位,都不是原来的初始点位,而是根据时间采样点采集的点位。
2.3应用案例
两种计时器的方式,根据项目情况和各自的优缺点,合理的利用。例如应用案例中智慧工地轨迹线动态生长,同时还要对比电子围栏做超围计算,超出围栏部分标红显示,采用的是固定时间间隔增加点位,因为需要精确的与围栏相交的点位,而时间采样点不便于计算。
而不需要这类精确计算的轨迹线动态生长,就可采用时间采样点方式,不仅仅是轨迹线动态生长,例如漫游,模型动态改变位置等都可以用Clock的方式。
- 总结
如何让轨迹线优雅的动起来,就讨论到这儿了,也许还有其他的方式未被列出,欢迎大家评论区讨论。如何在项目中熟练且多变的运用GIS能力,这是一个需要不断探索的话题。正如只是让轨迹线动起来,就有不同的方式,需要根据不同的情景去选择运用,那其他的格式的是矢量数据、模型数据呢。只有深耕项目,深挖应用场景,才能让GIS为其提供更强大的3D地图展示和数据可视化的功能。