当前位置:   article > 正文

Cesium实现大体量城市道路穿梭线效果,详细教程_cesium道路图

cesium道路图

1.实现效果:

2.实现方法:

实现效果 有两种方式:

2.1 参考右弦GISer大佬的方法

​​​​​​​实现思路就是通过GeoJsonDataSource、entity绘制线,东躲自定义的shader实现流动效果。其缺点就是,加载大体量的geojson数据,entity绘制元素较多容易卡死

2.1.1 使用Cesium的GeoJsonDataSource接口添加线状道路geojson文件

我使用的是VUE3.0写的项目

  1. /**
  2. * @description: 加载城市路网OD线
  3. * @param {*} CesiumViewer
  4. * @return {*}
  5. */
  6. import { Spriteline1MaterialProperty } from './Cesium_OD'
  7. export function urbanRoadNetworkOD() {
  8. removeRoadOD()
  9. // 道路穿梭线
  10. const imageUrl = new URL(
  11. '../assets/images/specialEffects/lineYellow.png',
  12. import.meta.url
  13. ).href
  14. Cesium.GeoJsonDataSource.load(
  15. // './geojson/WaterDistributionMainLine5.geojson'
  16. './geojson/WaterDistributionMainLineLittlel.geojson'
  17. ).then(function (dataSource) {
  18. // console.log(dataSource, 'dataSource')
  19. RoadOdData = dataSource
  20. CesiumViewer.dataSources.add(RoadOdData)
  21. const entities = RoadOdData.entities.values
  22. entities.forEach((el: { polyline: { width: number; material: any } }) => {
  23. el.polyline.width = 2
  24. el.polyline.material = new Spriteline1MaterialProperty(1000, imageUrl)
  25. })
  26. })
  27. }

2.1.2 遍历加载后文件的entity,将其材质设置为自定义的传输线材质。

2.1.3 材质设置

将材质文件保存为单独的.ts文件,在进行entity材质赋值时直接调用即可。主要的逻辑实现代码如下:

  1. /**
  2. * 精灵穿梭路光效果,
  3. * entity的材质使用MaterialProperty,而primitive使用的是material。
  4. * @Data:2022-01-11
  5. */
  6. import * as Cesium from 'cesium'
  7. function Spriteline1MaterialProperty(this: any, duration: any, image: any) {
  8. this._definitionChanged = new Cesium.Event()
  9. this.duration = duration
  10. this.image = image || '/images/ceshi/linBlue.png'
  11. this._time = performance.now()
  12. }
  13. Object.defineProperties(Spriteline1MaterialProperty.prototype, {
  14. isConstant: {
  15. get: function () {
  16. return false
  17. }
  18. },
  19. definitionChanged: {
  20. get: function () {
  21. return this._definitionChanged
  22. }
  23. },
  24. color: Cesium.createPropertyDescriptor('color'),
  25. duration: Cesium.createPropertyDescriptor('duration')
  26. })
  27. Spriteline1MaterialProperty.prototype.getType = function (time: any) {
  28. return 'Spriteline1'
  29. }
  30. Spriteline1MaterialProperty.prototype.getValue = function (
  31. time: any,
  32. result: { image?: any; time?: any }
  33. ) {
  34. if (!Cesium.defined(result)) {
  35. result = {}
  36. }
  37. result.image = this.image
  38. result.time =
  39. ((performance.now() - this._time) % this.duration) / this.duration
  40. return result
  41. }
  42. Spriteline1MaterialProperty.prototype.equals = function (e: { duration: any }) {
  43. return (
  44. this === e ||
  45. (e instanceof Spriteline1MaterialProperty && this.duration === e.duration)
  46. )
  47. }
  48. const CesiumExtensions = Object.assign({}, Cesium)
  49. CesiumExtensions.Spriteline1MaterialProperty = Spriteline1MaterialProperty
  50. window.Cesium = CesiumExtensions
  51. Cesium.Material.Spriteline1Type = 'Spriteline1'
  52. Cesium.Material.Spriteline1Source = `
  53. czm_material czm_getMaterial(czm_materialInput materialInput)
  54. {
  55. czm_material material = czm_getDefaultMaterial(materialInput);
  56. vec2 st = materialInput.st;
  57. vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
  58. material.alpha = colorImage.a;
  59. material.diffuse = colorImage.rgb / 2.0;
  60. return material;
  61. }
  62. `
  63. // st :二维纹理坐标
  64. // czm_material:保存可用于照明的材质信息
  65. Cesium.Material._materialCache.addMaterial(Cesium.Material.Spriteline1Type, {
  66. fabric: {
  67. type: Cesium.Material.Spriteline1Type,
  68. uniforms: {
  69. color: new Cesium.Color(1, 0, 0, 0.5),
  70. image: '',
  71. transparent: true,
  72. time: 20
  73. },
  74. source: Cesium.Material.Spriteline1Source
  75. },
  76. translucent: function (material: any) {
  77. return true
  78. }
  79. })
  80. export { Spriteline1MaterialProperty }

此处出现两个坑需要注意:

第一个坑vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));这里,因为使用的是高版本的cesium1.104,直接写texture2D会报错:[Cesium WebGL] Fragment shader compile log: ERROR: 0:8: ‘texture2D‘,原因是Cesium 新版对 WebGL2 支持有变化!


报错原因:Cesium 自 1.102.0 开始,为了更好支持跨平台,尤其是移动端,Cesium 默认使用 WebGL2 上下文,如果想使用 WebGL1 上下文,需要在地球初始化的时候设置相应的参数。

为了在 WebGL2 上下文中工作,任何自定义材质、自定义图元和自定义着色器都需要升级到使用 GLSL 300。

该文章解决方法

以下是两种方法解决问题,推荐第二种

1. 初始化地球时(不推荐),传入参数,使用 WebGL1 上下文

  1. const viewer = new Viewer("cesiumContainer", {
  2. // 指定上下文
  3. contextOptions: {
  4. requestWebgl1: true,
  5. },
  6. });

2. 修正 GLSL 代码(推荐)

错误代码(texture2D):

vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t))

修正为:

vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));

但使用方式2修正后,出现了个问题,uv纹理流动方向顺时针旋转了90度。。。因为对gls语言不太熟,暂时没能力修改shader,代码基本都是参考各大佬的,所以最后还是使用了方式一解决了,也比较省事。。。

第二个坑:如果按右弦大佬的Cesium.Spriteline1MaterialProperty = Spriteline1MaterialProperty定义Cesium新的类,将会报错:TypeError: Cannot add property Spriteline1MaterialProperty, object is not extensible

原因可能是:Cesium 对象已经被其他库或代码修改为不可扩展,导致你无法向它添加新的属性或方法。为了解决这个问题,可以考虑使用另一个对象来替代 Cesium 对象,并向它添加你需要的属性和方法,就可以了

  1. import Cesium from 'cesium';
  2. // 创建一个新对象,包含所有原始的 Cesium 属性和方法
  3. const CesiumExtensions = Object.assign({}, Cesium);
  4. // 向该对象添加新的属性和方法
  5. CesiumExtensions.myCustomMethod = function() {
  6. // ...
  7. };
  8. // 将原始的 Cesium 对象替换为新的对象
  9. window.Cesium = CesiumExtensions;

2.2 使用primitive方法合并图元,并自定义sharder实现流动线条效果(强烈推荐,亲测,集显显卡加载3.6W个要素线,帧率基本保持在30~40之间)

2.2.1 primitive方法实现思路

首先需要使用jQuery异步加载geojson文件,其次依次遍历,使用Cesium.Cartesian3.fromDegreesArray()方法将经纬度坐标转换为笛卡尔坐标,设置geometry的默认颜色、线宽,Cesium.PolylineGeometry创建线段的几何实例,创建一个新的Cesium.Material对象,并设置其fabric属性,用于定义材质的外观和行为。在czm_material czm_getMaterial(czm_materialInput materialInput)函数中,使用texture2D()函数获取当前时间下的纹理颜色,并将其作为材质的漫反射颜色。再将纹理颜色的alpha值设置为材质的透明度。最后,同时将材质返回给Cesium进行渲染。然后将单个geometry合并一次传输,提高渲染效率。最后通过CesiumViewer.clock.onTick事件,根据当前时间更新材质中的time属性。这个属性用于控制纹理的平移,从而实现流动线的效果。

具体代码如下:

.vue组件:

  1. function getGeojson() {
  2. // 使用primitive方式加载geojson
  3. let urlPath = './geojson/WaterDistributionMainLine.geojson'
  4. // 使用jQuery异步加载json数据
  5. $.get(urlPath, function (data) {
  6. let features = reactive([])
  7. if (data.features && data.features.length > 0) {
  8. features = reactive(data.features)
  9. CesiumMap.addDataToGlobe(toRaw(features))
  10. }
  11. })
  12. }

.ts文件:

  1. export function addDataToGlobe(features) {
  2. const imageUrl = new URL(
  3. '../assets/images/specialEffects/lineYellow.png',
  4. import.meta.url
  5. ).href
  6. const instances = []
  7. for (let i = 0; i < features.length; i++) {
  8. for (let j = 0; j < features[i].geometry.coordinates.length; j++) {
  9. const positions = Cesium.Cartesian3.fromDegreesArray(
  10. features[i].geometry.coordinates[j].flat()
  11. )
  12. const polyline = new Cesium.PolylineGeometry({
  13. positions: positions,
  14. width: 2,
  15. vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
  16. })
  17. const polygon = new Cesium.GeometryInstance({
  18. geometry: polyline,
  19. id: i
  20. })
  21. instances.push(polygon)
  22. }
  23. }
  24. const material = new Cesium.Material({
  25. fabric: {
  26. type: 'Spriteline1',
  27. uniforms: {
  28. color: Cesium.Color.WHITE,
  29. image: imageUrl,
  30. transparent: true,
  31. time: 0
  32. },
  33. source: `
  34. czm_material czm_getMaterial(czm_materialInput materialInput)
  35. {
  36. czm_material material = czm_getDefaultMaterial(materialInput);
  37. vec2 st = materialInput.st;
  38. vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
  39. material.alpha = colorImage.a;
  40. material.diffuse = colorImage.rgb / 2.0;
  41. return material;
  42. }
  43. `
  44. }
  45. })
  46. const polylinePrimitive = CesiumViewer.scene.primitives.add(
  47. new Cesium.Primitive({
  48. geometryInstances: instances,
  49. appearance: new Cesium.PolylineMaterialAppearance({
  50. flat: true,
  51. material: material
  52. })
  53. })
  54. )
  55. CesiumViewer.clock.onTick.addEventListener(function (clock) {
  56. const startTime = Cesium.JulianDate.toDate(
  57. CesiumViewer.clock.startTime
  58. ).getTime()
  59. const currentTime = Cesium.JulianDate.toDate(clock.currentTime).getTime()
  60. const elapsedTime = (currentTime - startTime) / 1000.0
  61. material.uniforms.time = elapsedTime
  62. })
  63. }

参考文献:

右弦GISer-cesium实现道路穿梭线效果_cesium流动路网_右弦GISer的博客-CSDN博客

非科班Java出身GISer-​​​​​​​Cesium 1.02.0 及以上版本下自定义材质报错:[Cesium WebGL] Fragment shader compile log: ERROR: 0:8: ‘texture2D‘_非科班Java出身GISer的博客-CSDN博客

cesium解决DeveloperError报错‘Expected longitude to be typeof number, actual type of was string‘_右弦GISer的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/330394
推荐阅读
相关标签
  

闽ICP备14008679号