【cesium】绘制贴地线面和自定义Primitive

最近遇到一个开发问题,cesium贴地形状默认Primitive在Entity上面,然后Entity被Primitive遮盖,cesium绘制的Entity和Primitive不在一个图层,调整不了zIndex。

js 复制代码
//Primitive贴地多边形
viewer.scene.primitives.add(
  new Cesium.GroundPrimitive({
    geometryInstances: new Cesium.GeometryInstance({
      geometry: new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(
          Cesium.Cartesian3.fromDegreesArray([113, 39.001, 113.002, 39, 113.002, 39.002])
        )
      })
    }),
    appearance: new Cesium.MaterialAppearance({
  material: Cesium.Material.fromType('Color', {
    color: Cesium.Color.BLUE.withAlpha(0.5),//半透明蓝色
  })
})
  })
);
//Entity贴地多边形
viewer.entities.add({
  polygon: {
    hierarchy: Cesium.Cartesian3.fromDegreesArray([113.001, 39, 113.002, 39, 113.002, 39.002]),
    material: Cesium.Color.RED.withAlpha(0.5),//半透明红色
    classificationType: Cesium.ClassificationType.BOTH
  }
});

Entity在Primitive后面添加,按道理应该在Primitive上面,但显示结果是Primitive在上面

为了解决这个问题,不得不将所有Entity改成Primitive。而Entity是动态的,修改属性后会自动更新渲染,而Primitive是静态的,只能删除再添加来实现更新,为了避免频繁增删Primitive,于是采用自定义Primitive的方案。

1.Entity绘制贴地线面

1.1 创建一个绘制基类,监听鼠标动作

ts 复制代码
class DrawBase {
  hander: Cesium.ScreenSpaceEventHandler;
  viewer: Cesium.Viewer;
  isDraw: boolean = false;
  constructor(v: Cesium.Viewer) {
    this.viewer = v;
    //监听canvas动作
    this.hander = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);
  }
  onListener() {
    //添加动作监听
    this.hander.setInputAction(this.onLeftClick.bind(this), Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.hander.setInputAction(this.onMouseMove.bind(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.hander.setInputAction(
      this.onRightClick.bind(this),
      Cesium.ScreenSpaceEventType.RIGHT_CLICK
    );
    //canvas添加鼠标样式draw
    this.viewer.canvas.classList.add('draw');
    //开启绘制
    this.isDraw = true;
  }
  offListener() {
    //移除动作监听
    this.hander.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.hander.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.hander.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    //canvas添加鼠标样式draw
    this.viewer.canvas.classList.remove('draw');
    //关闭绘制
    this.isDraw = false;
  }

  onLeftClick(ev: Cesium.ScreenSpaceEventHandler.PositionedEvent) {}
  onMouseMove(ev: Cesium.ScreenSpaceEventHandler.MotionEvent) {}
  onRightClick(ev: Cesium.ScreenSpaceEventHandler.PositionedEvent) {}
}

1.2 绘制线

点击地面,获取具体坐标点绘制折线。

ts 复制代码
type LineDataType = {
  positions: number[][];
  id: string;
  line: Cesium.Entity | null;
  point: { [n: string]: number };
};
class LineDraw extends DrawBase {
  //当前数据
  currentData: LineDataType | null = null;
  //折线数据
  lineMap = new Map<string, LineDataType>();
  //当前绘制折线id
  currentId: string = '';
  //当前绘制折线坐标点
  positions: number[][] = [];
  constructor(v: Cesium.Viewer) {
    super(v);
  }  
}
  • 左点击收集点
ts 复制代码
  onLeftClick(ev: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
    if (this.isDraw) {
      const p = PosUtil.pickPosWGS84(ev.position);
      if (p) {
        //初始化数据
        if (!this.currentData) {
          const line = {
            id: this.currentId,
            positions: [],

            line: null,
            point: {}
          };
          this.lineMap.set(this.currentId, line);
          this.positions = [];
          this.currentData = line;
        }
        this.positions.push([p[0], p[1]]);
        this.drawLine(this.positions);
      }
    }
  } 
  • 鼠标移动过程中绘制折线
ts 复制代码
  onMouseMove(ev: Cesium.ScreenSpaceEventHandler.MotionEvent) {
    if (this.isDraw && this.currentData) {
      const p = PosUtil.pickPosWGS84(ev.endPosition);
      if (p) {
        this.drawLine([...this.positions, [p[0], p[1]]]);
      }
    }
  }
  • 右点击收集点
ts 复制代码
 onRightClick(ev: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
    if (this.isDraw && this.currentData) {
      const p = PosUtil.pickPosWGS84(ev.position);
      if (p) {
        this.positions.push([p[0], p[1]]);
        this.drawLine(this.positions);
      }
      //关闭绘制
      this.closeDraw();
    }
  }
  • 绘制折线
ts 复制代码
  drawLine(positions: number[][]) {
    const line = this.lineMap.get(this.currentId);
    if (!line) return;
    //更新折线坐标,折线至少2个坐标点
    line.positions = positions?.length === 1 ? [positions[0], positions[0]] : [...positions];
    //没有绘制则新增绘制折线
    if (!line.line) {
      line.line = new Cesium.Entity({
        id: this.currentId,
        polyline: {
          //利用CallbackProperty,自动更新绘制
          positions: new Cesium.CallbackProperty(() => {
            return Cesium.Cartesian3.fromDegreesArray(line.positions.flat(1));
          }, false),
          ...this.lineStyle
        }
      });
      this.viewer.entities.add(line.line);
    }
    //绘制点,对比新旧点坐标,进行更新
    const oldMap = { ...line.point };
    const newMap: LineDataType['point'] = {};
    for (let i = 0; i < positions.length; i++) {
      const p = [positions[i][0], positions[i][1]];
      const id = this.currentId + p.join('_');
      //添加新点
      if (!oldMap[id]) {
        this.viewer.entities.add({
          id,
          position: Cesium.Cartesian3.fromDegrees(p[0], p[1]),
          point: {
            ...this.pointStyle
          }
        });
      }
      newMap[id] = 1;
    }
    //删除没有的点
    for (let k in oldMap) {
      if (!newMap[k]) {
        this.viewer.entities.removeById(k);
      }
    }
    line.point = newMap;
  } 

注意:

  1. Entity点贴地属性开启clampToGround: true,折线贴地属性开启 heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
  2. Entity是动态的,不需要增删来更新形状,只需要基于原来的实例进行管理,赋值属性会自动更新,等于赋值一个新的Cesium.ConstantProperty,会看到明显的抖动。
ts 复制代码
entity.polyline.material=Cesium.Color.GREEN;
//赋值坐标点
entity.polyline.positions=Cesium.Cartesian3.fromDegreesArray(line.positions.flat(1));
//等价于
entity.polyline.positions=new Cesium.ConstantProperty(Cesium.Cartesian3.fromDegreesArray(line.positions.flat(1)))

而使用Cesium.CallbackProperty回调属性,不需要赋值到属性,变量值更新则自动获取新值,进行更新,不会发生抖动。

ts 复制代码
 positions: new Cesium.CallbackProperty(() => {
            return Cesium.Cartesian3.fromDegreesArray(line.positions.flat(1));
          }, false)
          
line.positions = positions?.length === 1 ? [positions[0], positions[0]] : [...positions];      
  • 绘制操作:左点击添加点,右点击结束绘制

1.3 绘制多边形面

同上面绘制线相似,增加多边形的样式设置,其中Entity多边形贴地的属性开启classificationType: Cesium.ClassificationType.BOTH

ts 复制代码
//多边形数据
type PolygonDataType = {
  positions: number[][];
  id: string;
  polygon: Cesium.Entity | null;
  point: { [n: string]: number };
};
class PolygonDraw extends DrawBase {
  //当前数据
  currentData: PolygonDataType | null = null;
  //多边形数据
  polygonMap = new Map<string, PolygonDataType>();
  //当前多边形id
  currentId: string = '';
  //当前绘制多边形坐标点
  positions: number[][] = [];
  }
  • 绘制多边形面,同样采用Cesium.CallbackProperty来动态更新坐标点,另外,多边形polygon自带的边框设置线宽outlineWidth无效,故在Entity内叠加一层折线polyline,而多边形是封闭图形,所以折线坐标点要最后要增加一个起点坐标,形成封闭。
ts 复制代码
drawPolygon(positions: number[][]) {
    const polygon = this.polygonMap.get(this.currentId);
    if (!polygon) return;
     //更新多边形坐标,多边形至少3个坐标点
    polygon.positions =
      positions.length === 1
        ? [positions[0], positions[0], positions[0]]
        : positions.length === 2
        ? [positions[0], positions[1], positions[0]]
        : [...positions];
    if (!polygon.polygon) {
      polygon.polygon = new Cesium.Entity({
        id: this.currentId,
        polyline: {
           //利用CallbackProperty,自动更新绘制
          positions: new Cesium.CallbackProperty(() => {
            //封闭形状
            return Cesium.Cartesian3.fromDegreesArray(
              [...polygon.positions, polygon.positions[0]].flat(1)
            );
          }, false),
          ...this.lineStyle
        },
        polygon: {
           //利用CallbackProperty,自动更新绘制
          hierarchy: new Cesium.CallbackProperty(() => {
            return {
              positions: Cesium.Cartesian3.fromDegreesArray(polygon.positions.flat(1))
            };
          }, false),
          ...this.polygonStyle
        }
      });
      this.viewer.entities.add(polygon.polygon);
    }
    //同上面绘制点
    //...
  }

Entity有贴地相关属性,地形是否开启的时候都可以自动适配,而Primitive和Geometry需对应改成贴地适配,并且PointPrimitive点没有贴地属性,需要通过高度来做到山形贴地适配。

2.Primitive绘制贴地线面

2.1 自定义Primitive基类

  • 添加自定义Primitive的逻辑是,scene.primitivies.add的时候默认添加_primitive属性值,那么自定义Primitive内不论如何操作,只要指向引用不变,那么就还是一个对象,不需scene.primitivies进行增删,避免抖动。
  • 自定义primitive通过update函数判断相关参数是否变化,对需要更新的形状删除后重新创建来实现更新,通过isDestroyed函数标识是否销毁
ts 复制代码
//自定义Primitive配置项
export type CustomPrimitiveOption = {
  id: string;
  //坐标点
  positions: number[][];
  //是否贴地
  isGround?: boolean;
  //坐标点是否有高度
  isHeight?: boolean;
  //是否开启山形
  isTerrain?: boolean;
};
//自定义Primitive基类
class CustomPrimitive {
  id: string = "";
  //图元
  _primitive: any;
  //...
  constructor(options: CustomPrimitiveOption) {
    this.id = options.id || (Math.random() * 9999).toFixed(0);
    this.positions = options.positions;
    this.isGround = options.isGround || false;
    this.isTerrain = options.isTerrain || false;
    //贴地开启则默认坐标点高度不开启
    if (this.isGround) {
      this.isHeight = false;
    } else {
      this.isHeight = options.isHeight || false;
    }
  }
  • 创建点Primitive集合,在外面先判断,再进行遍历,这样性能比较友好
ts 复制代码
getPointPrimitive(that: {
    pointSize: number;
    pointColor: Cesium.Color;
    pointOutline?: boolean;
    pointOutlineColor?: Cesium.Color;
    pointOutlineWidth?: number;
  }) {
    //创建点Primitive集
    if (!this._pointPrimitive) {
      this._pointPrimitive = new Cesium.PointPrimitiveCollection();
    }
    //对比新旧坐标点,进行增删
    const oldMap = { ...this.pointMap };
    const newMap: { [n: string]: number } = {};
    //点样式
    const pointStyle = {
      pixelSize: that.pointSize,
      color: that.pointColor,
      outlineColor: that.pointOutline ? that.pointOutlineColor : undefined,
      outlineWidth: that.pointOutline ? that.pointOutlineWidth : 0.0,
    };
    const baseHeight = 0.0; 
    //贴地
    if (this.isGround) {
      //地形开启,取山形高度
      if (this.isTerrain) {
        this.positions.forEach((it, idx) => {
          const p = [it[0], it[1], it[2] ? it[2] + baseHeight : baseHeight];
          const id = this.id + p.join("_");
         if (!oldMap[id] && !newMap[id]) {
            this._pointPrimitive.add({
              id,
              position: Cesium.Cartesian3.fromDegrees(p[0], p[1], p[2]),
              ...pointStyle,
            });
          }
          newMap[id] = 1;
        });
      } else {        
        this.positions.forEach((it, idx) => {
          const p = [it[0], it[1], baseHeight];
          //...
        });
      }
    } else {
      //坐标自定义高度开启
      if (this.isHeight) {
        this.positions.forEach((it, idx) => {
          const p = [it[0], it[1], it[2] ? it[2] + baseHeight : baseHeight];
         //...
        });
      } else {
        this.positions.forEach((it, idx) => {
          const p = [it[0], it[1], baseHeight];
         //...
        });
      }
    } 
    //删除没有的点
    this._pointPrimitive._pointPrimitives.forEach((point: Cesium.PointPrimitive) => {
      if (!newMap[point.id]) {
        this._pointPrimitive.remove(point);
      }
    });
    this.pointMap = newMap;
    return this._pointPrimitive;
  }
  • 创建线Primitive材质
ts 复制代码
getLineMaterial(that: { color: Cesium.Color; isDashed?: boolean; dashLength?: number }) {
    if (that.isDashed) {
      //虚线样式
      return Cesium.Material.fromType('PolylineDash', { color: that.color, gapColor: Cesium.Color.TRANSPARENT, dashLength: that.dashLength });
    } else {
      //实线样式
      return Cesium.Material.fromType('Color', {  color: that.color });
    }
  }
  • 创建线Primitive
ts 复制代码
  getLinePrimitive(that: {
    outline: boolean;
    outlineColor: Cesium.Color;
    outlineWidth: number;
    isClose?: boolean;//是否闭环
    isDashed?: boolean;
  }) {
    const positions = that.isClose ? [...this.positions, this.positions[0]] : this.positions;
    const material = this.getLineMaterial({ color: that.outlineColor,isDashed: that.isDashed}); 
    if (this.isGround) {//贴地
      return new Cesium.GroundPolylinePrimitive({
        geometryInstances: new Cesium.GeometryInstance({
          id: this.id + 'linegeometryInstance',
          geometry: new Cesium.GroundPolylineGeometry({
            positions: Cesium.Cartesian3.fromDegreesArray(
              PosUtil.posNoHeightTransform(positions).flat(1)
            ), width: that.outlineWidth
          })
        }), appearance: new Cesium.PolylineMaterialAppearance({
          translucent: true,  material: material
        }), asynchronous: false
      });
    } else {
      return new Cesium.Primitive({
        geometryInstances: new Cesium.GeometryInstance({
          id: this.id + 'linegeometryInstance',
          geometry: new Cesium.PolylineGeometry({
            //是否开启高度
            positions: this.isHeight ? Cesium.Cartesian3.fromDegreesArrayHeights(
                  PosUtil.posHeightTransform(positions).flat(1) ) :  Cesium.Cartesian3.fromDegreesArray(PosUtil.posNoHeightTransform(positions).flat(1)),
            width: that.outlineWidth
          })
        }), appearance: new Cesium.PolylineMaterialAppearance({
          translucent: true,
          material: material
        }),  asynchronous: false
      });
    }
  }
  • 创建多边形Primitive
ts 复制代码
  getPolygonPrimitive(that: { color: Cesium.Color }) {   
    if (this.isGround) { //贴地
      return new Cesium.GroundPrimitive({
        geometryInstances: new Cesium.GeometryInstance({
          id: this.id + 'geometryInstance',
          geometry: new Cesium.PolygonGeometry({
            polygonHierarchy: new Cesium.PolygonHierarchy(
              Cesium.Cartesian3.fromDegreesArray(
                //转换为无高度坐标
                PosUtil.posNoHeightTransform(this.positions).flat(1) ) )  }) }),
        appearance: new Cesium.MaterialAppearance({
          faceForward: false,//双面可见
          translucent: true,//透明开启
          material: Cesium.Material.fromType('Color', { color: that.color })
        }),
        asynchronous: false
      });
    } else {
      return new Cesium.Primitive({
        geometryInstances: new Cesium.GeometryInstance({
          id: this.id + 'geometryInstance',
          geometry: new Cesium.PolygonGeometry({
            perPositionHeight: this.isHeight,//是否开启高度
            polygonHierarchy: new Cesium.PolygonHierarchy(
              this.isHeight
                ? Cesium.Cartesian3.fromDegreesArrayHeights(
                //转换为有高度坐标
                    PosUtil.posHeightTransform(this.positions).flat(1) ) : Cesium.Cartesian3.fromDegreesArray(
                //转换为无高度坐标
                    PosUtil.posNoHeightTransform(this.positions).flat(1) )
            )
          })
        }),
        appearance: new Cesium.MaterialAppearance({
          faceForward: false,//双面可见
          translucent: true,//透明开启
          material: Cesium.Material.fromType('Color', { color: that.color  })
        }),
        asynchronous: false
      });
    }
  }

2.2 自定义折线Primitive

  • 自定义Primitive关键函数update逻辑:坐标点相关属性不变则不更新,坐标点更新则重新渲染新的Primitive,点采用PointPrimitiveCollection点集合,并进行对比新旧点进行增删。形状集合更新则是移除形状再新增形状。
ts 复制代码
class CustomLinePrimitive extends CustomPrimitive {
update(frameState: any) {
    //坐标点相关属性不变则保持
    if (  this._primitive &&  JSON.stringify(this.positions) === JSON.stringify(this._positions) &&  this.isGround === this._isGround && this.isHeight === this._isHeight &&  this._isTerrain === this.isTerrain ) {
      this._primitive.update(frameState);
      return;
    }
    //更新坐标点相关属性
    this._positions = [...this.positions];
    this._isGround = this.isGround;
    this._isHeight = this.isHeight;
    this._isTerrain = this.isTerrain;

    //创建图元集合
    if (!this._primitive) {
      this._primitive = new Cesium.PrimitiveCollection();
    }
    //绘制点
    if (this.isPoint) {
      this.getPointPrimitive({ pointSize: this.pointSize,   pointColor: this.pointColor, pointOutline: this.pointOutline, pointOutlineColor: this.pointOutlineColor, pointOutlineWidth: this.pointOutlineWidth });
    }
    //绘制线
    this._linePrimitive && this._primitive && this._primitive.remove(this._linePrimitive);    
    this._linePrimitive = this.getLinePrimitive({ outline: true, outlineColor: this.color, outlineWidth: this.width, isClose: this.isClose, isDashed: this.isDashed });
    //如果未添加则添加到集合
    if (this._linePrimitive && !this._primitive.contains(this._linePrimitive)) {
      this._primitive.add(this._linePrimitive);
    }
    if (this.isPoint && this._pointPrimitive) {
      if (!this._primitive.contains(this._pointPrimitive)) {
        this._primitive.add(this._pointPrimitive);
      }
      //点集合置顶
      this._primitive.raiseToTop(this._pointPrimitive);
    }
    if ( this._primitive) this._primitive.update(frameState); 
  }
}

2.3 自定义多边形Primitive

自定义多边形由点集合,折线,多边形组成,通过增删对集合内Primitive进行更新。

ts 复制代码
class CustomPolygonPrimitive extends CustomPrimitive {
 update(frameState: any) {
    //坐标点相关属性不变则保持
    if ( this._primitive &&  JSON.stringify(this.positions) === JSON.stringify(this._positions) &&  this.isGround === this._isGround && this.isHeight === this._isHeight && this._isTerrain === this.isTerrain ) {
      this._primitive.update(frameState);
      return;
    }
    //更新坐标点相关属性
    this._positions = [...this.positions];
    this._isTerrain = this.isTerrain;
    this._isGround = this.isGround;
    this._isHeight = this.isHeight;
    //创建图元集合
    if (!this._primitive) {
      this._primitive = new Cesium.PrimitiveCollection();
    }
    //绘制线
    if (this.outline) {
      this._linePrimitive && this._primitive && this._primitive.remove(this._linePrimitive);
      //至少两个坐标点
      if (this.positions.length >= 2) {
        this._linePrimitive = this.getLinePrimitive({ outline: this.outline, outlineColor: this.outlineColor,   outlineWidth: this.outlineWidth,   isClose: true, isDashed: false  });
      }
    }
    //绘制点
    if (this.isPoint) {
      this.getPointPrimitive({  pointSize: this.pointSize, pointColor: this.pointColor,  pointOutline: this.pointOutline,  pointOutlineColor: this.pointOutlineColor, pointOutlineWidth: this.pointOutlineWidth  });
    }
    //绘制面
    this._polygonPrimitive && this._primitive && this._primitive.remove(this._polygonPrimitive);
    //至少两个坐标点
    if (this.positions.length >= 3) {
      this._polygonPrimitive = this.getPolygonPrimitive({ color: this.color });
    }

    //如果未添加则添加到集合
    if (this._polygonPrimitive && !this._primitive.contains(this._polygonPrimitive)) {
      this._primitive.add(this._polygonPrimitive);
    }

    if (this.outline && this._linePrimitive && !this._primitive.contains(this._linePrimitive)) {
      this._primitive.add(this._linePrimitive);
    }

    if (this.isPoint && this._pointPrimitive) {
      if (!this._primitive.contains(this._pointPrimitive)) {
        this._primitive.add(this._pointPrimitive);
      }
      //点集合置顶
      this._primitive.raiseToTop(this._pointPrimitive);
    }

    if (this._primitive)  this._primitive.update(frameState);
  }
  }

2.4 动态自定义Primitive管理类

ts 复制代码
export class DynamicPrimitive {
  static viewer: Cesium.Viewer;
  //折线集合
  static lines = new Map<string, CustomLinePrimitive>();
  //多边形集合
  static polygons = new Map<string, CustomPolygonPrimitive>();
  //Primitive集合用于收集自定义Primitive
  static group: Cesium.PrimitiveCollection = new Cesium.PrimitiveCollection();
  //是否开启山形
  static isTerrain: boolean;
  //初始化管理类
  static init(v: Cesium.Viewer, isTerrain?: boolean) {
    this.viewer = v;
    this.viewer.scene.primitives.add(this.group);
    this.isTerrain = isTerrain || false;
  }
  
}

因为绘制点Primitive没有贴地相关属性,则需要获取坐标点的高程,作为地形开启与否的高度

ts 复制代码
//缓存坐标地形高程
static terrainPosMap = new Map<string, number>();
  //更新贴地坐标点的高程
  static async updateGroundPos(positions: number[][]) {
    for (let i = 0; i < positions.length; i++) {
      const a = positions[i];
      let height;
      const id = [a[0], a[1]].join('_');
      const h = this.terrainPosMap.get(id);
      if (h !== undefined) {
        height = h;
      } else {
        height = await PosUtil.getLngLatTerrainHeight(a[0], a[1]);
        this.terrainPosMap.set(id, height);
      }
      a[2] = height;
    }
  }
  • 多边形添加和修改操作
ts 复制代码
  //添加多边形
  static async addPolygon(set: CustomPolygonPrimitiveOption) {
    //如果贴地,更新坐标点高程
    if (set.isGround) {
      await this.updateGroundPos(set.positions);
    }
    let p = new CustomPolygonPrimitive({
      ...set,
      isTerrain: this.isTerrain
    });
    this.group.add(p);
    this.polygons.set(set.id, p);
    //Primitive置顶
    this.viewer.scene.primitives.raiseToTop(this.group);
    return p;
  }
  //更新坐标点
  static async updatePolygonPos(id: string, pos: number[][]) {
    const p = this.polygons.get(id);
    if (p) {
      //如果贴地,更新坐标点高程
      if (p.isGround) {
        await this.updateGroundPos(pos);
      }
      p.positions = pos;
    }
  }
  • 地形是否开启时,更新自定义Primitive
ts 复制代码
  static updateTerrain(isTerrain: boolean) {
    this.isTerrain = isTerrain;
    this.lines.forEach((line) => {
      if (line.isGround) line.isTerrain = isTerrain;
    });
    this.polygons.forEach((polygon) => {
      if (polygon.isGround) polygon.isTerrain = isTerrain;
    });
  }
  • 监听地形是否添加
ts 复制代码
const terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
      'https://data.marsgis.cn/terrain',
      {
        requestVertexNormals: true
      }
    );
    //添加地形
    this.viewer.terrainProvider = terrainProvider;
    this.terrainProvider = terrainProvider;
    //监听地形变化
 this.viewer.scene.globe.terrainProviderChanged.addEventListener((ev) => {
 //延迟更新,避免山形未加载就更新
      setTimeout(() => {
        DynamicPrimitive.updateTerrain(this.viewer.terrainProvider === this.terrainProvider);
      }, 100);
      console.log('%c地形改变', 'background:yellow', ev);
    });

2.5 使用自定义折线Primitive绘制

通过管理类DynamicPrimitive来增加和修改折线

ts 复制代码
  async drawLine(positions: number[][]) {
    if (!this.currentData || this.isLock) return;
    //上锁
    this.isLock = true;
    //更新折线坐标,折线至少2个坐标点
    const pos = positions?.length === 1 ? [positions[0], positions[0]] : [...positions];
    console.log('🚀 ~ LinePrimitiveDraw ~ drawLine ~ pos:', pos);

    //没有绘制则新增绘制折线
    if (!this.currentData.line) {
      this.currentData.line = await DynamicPrimitive.addPolyline({
        id: this.currentId,
        positions: pos,
        ...this.lineStyle
      });
    } else {
      //更新坐标点
      await DynamicPrimitive.updatePolylinePos(this.currentId, pos);
    }
    //取消上锁
    this.isLock = false;
  }

开启和关闭山形后,自定义折线Primitive会自动更新绘制

2.6 使用自定义多边形Primitive绘制

通过管理类DynamicPrimitive来增加和修改多边形

ts 复制代码
async drawPolygon(positions: number[][]) {
    if (!this.currentData || this.isLock) return;
    //上锁
    this.isLock = true;
    //更新多边形坐标,多边形至少3个坐标点
    const pos =
      positions.length === 1
        ? [positions[0], positions[0], positions[0]]
        : positions.length === 2
        ? [positions[0], positions[1], positions[0]]
        : [...positions];
    //没有绘制则新增绘制多边形
    if (!this.currentData.polygon) {
      this.currentData.polygon = await DynamicPrimitive.addPolygon({
        id: this.currentId,
        positions: pos,
        ...this.polygonStyle
      });
    } else {
       //更新坐标点
      await DynamicPrimitive.updatePolygonPos(this.currentId, pos);
    }
    //取消上锁
    this.isLock = false;
  }

开启和关闭山形后,自定义多边形Primitive会自动更新绘制

3.GitHub地址

这里省略了一些非关键的代码,详细代码请看github地址 https://github.com/xiaolidan00/cesium-demo

参考

  1. 山形数据marsgis
  2. 底图数据ArcGIS
相关推荐
前端拾光者2 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
木子02041 小时前
前端VUE项目启动方式
前端·javascript·vue.js
endingCode1 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript
Myli_ing2 小时前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
I_Am_Me_3 小时前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ3 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z3 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁3 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜3 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish3 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue