# vue使用cesium

  • 安装
npm install cesium@1.95.0 --save
  • 将node_modules/Build下的Cesium复制到public文件夹下面
<link href="./cesium/Widgets/widgets.css" rel="stylesheet">
<script src="./cesium/Cesium.js"></script>

//获取全局的Cesium
const Cesium = window.Cesium
  • 或者在vue的main.js中引入
//全局引入Cesium
import 'cesium/Build/Cesium/Widgets/widgets.css'
import * as Cesium from 'cesium'
Vue.prototype.$Cesium = Cesium
window.Cesium = Cesium
  • 封装自己的cesium方法,例如在src创建utils文件夹,在utils文件夹下面创建mCesium.js
import * as Cesium from 'cesium'
Cesium.Ion.defaultAccessToken ="token"; //这里的token是自己申请的token
window.CESIUM_BASE_URL = "/";

class mMap{
    constructor(id){
      this.id = id; //地图容器
      this.viewer = null;
      this.scene = null;
    }
    //初始化地图
    initMap(){
      console.log(this.id)
      this.viewer = new Cesium.Viewer(this.id,{
        geocoder: false,                //是否显示地名查找控件
        sceneModePicker: false,         //是否显示投影方式控件
        navigationHelpButton: false,    //是否显示帮助信息控件
        baseLayerPicker: false,         //是否显示图层选择控件
        homeButton: false,              //是否显示Home按钮
        fullscreenButton: false,        //是否显示全屏按钮
        animation: false, //左下角的动画控件的显示
        shouldAnimate: false, //控制模型动画
        timeline: false, //底部的时间轴
        selectionIndicator: false,
        infoBox: false
      });
      //定位到指定位置
      this.viewer.camera.flyTo({
        destination : Cesium.Cartesian3.fromDegrees(112.876942, 28.235312, 1500.0)
      });
      //去cesium logo水印 或 css
      this.viewer.cesiumWidget.creditContainer.style.display = "none";
      //创建场景
      this.scene = this.viewer.scene;
      if(!this.scene.pickPositionSupported){
        window.alert("此浏览器不支持拾取位置!")
      }
      this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas); 
    }
  }
  
  export function createMap(id){
    window.map = new mMap(id)
  }
}
  • 使用
<template>
	<div id="mMap"></div>
</template>
import { createMap} from './utils/cesium/mcesium'
mounted() {
    createMap("mMap");
    window.map.initMap()
}

# 绘制不同的形状(Entity)

// 点实体
viewer.entities.add({
    position: position, // 格式为世界坐标的点位置
	point: {
        show: ture, // 点是否可见
        pixelSize: Number, // 点的大小
        color: color, // 点的颜色
        outlineClor: color, // 边框颜色
        outlineWidth: Number, // 轮廓宽度
    },
});

// 线实体
viewer.entities.add({
    polyline: {
        show: Boolean, // 线是否可见
        positions: positions, // 格式为世界坐标的线位置数组
        width: Number, // 线的宽度
        material: color, // 线的颜色
        clampToGround: Boolean, // 线是否固定在地面
    },
});

// 面实体
viewer.entities.add({
    polygon: {
        show: Boolean, // 面是否可见
        hierarchy: positions, // 格式为世界坐标的面位置数组
        height: Number, // 面相对于椭球表面的高度
        fill: Boolean, // 面是否有填充
        material: color, // 面的填充颜色
        outline: Boolean, // 面是否有边框
        outlinColor: color, // 边框的颜色
        outlineWidth: Number, // 边框的宽度,无论设置为多少宽度始终只会显示为 1
        perPositionHeight: Boolean, // 面是否使用每个位置的高度
    },
});

// 添加广告牌,也就是一张图片、图标数据
addEntityBillboard() {
	let entityBillBoard = new Cesium.Entity({
		id: 'EntityBillboard0',
		name: 'EntityBillboard',
		show: true,
		description: '广告牌招租13390133157',
		position: new Cesium.Cartesian3.fromDegrees(116.0, 39.9, 100),
		billboard: {
			image: 'static/image/ziranzaihai.png',
			show: true,
			sacle: 1000.01,
			color: Cesium.Color.YELLOWGREEN,
			width: 100,
			height: 100
		}
	})
	let billboadrGeom = window.viewer.entities.add(entityBillBoard)
	window.viewer.zoomTo(entityBillBoard)
}

// 添加立方体实体。可以根据不同的材质进行设置box的外观
addEntityBox() {
   var heading = Cesium.Math.toRadians(90.0)
   var pitch = 0.0
   var roll = 0.0
   var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
   var orientation = Cesium.Transforms.headingPitchRollQuaternion(position,hpr)
	 let entityBox = new Cesium.Entity({
        id: 'entityBox0',
        name: 'entityBox',
        position: Cesium.Cartesian3.fromDegrees(111.0, 41.0),
        orientation: orientation,
        box: {
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            dimensions: new Cesium.Cartesian3(100000, 100000, 100000),
            material: new Cesium.ImageMaterialProperty({
                image: 'static/image/entityBillboard.ico',
                color: Cesium.Color.YELLOWGREEN,
                repeat: new Cesium.Cartesian2(16, 16),
                transparent: true
            }),
            outline: true,
            fill: true,
            outlineColor: Cesium.Color.RED,
            outlineWidth: 100
        }
    })

    let boxGeom = window.viewer.entities.add(entityBox)
    window.viewer.flyTo(entityBox, {
        duration: 3,
        maximumHeight: 1000,
        offset: Cesium.HeadingPitchRange(1, 1, 1)
    })
}

// 添加走廊通道实体,可以拉伸高度
addEntityCorridor() {
    let entityCorridor = new Cesium.Entity({
        id: 'entityCorridor0',
        name: 'entityCorridor0',
        position: Cesium.Cartesian3.fromDegrees(123.0, 43.0),
        corridor: {
            positions: Cesium.Cartesian3.fromDegreesArray([
                123.0, 40.0, 122.0, 40.0, 123.0, 42.0, 121.0, 42.0
            ]),
            width: 10000,
            material: new Cesium.ImageMaterialProperty({
								//可以设置贴图
                // image: 'static/image/corridor.png',
                color: Cesium.Color.YELLOWGREEN,
                repeat: new Cesium.Cartesian2(32, 32)
                // transparent: true
            }),
            extrudedHeight: 10000,
            outline: false
        }
    })
    let corridorGeom = window.viewer.entities.add(entityCorridor)
    window.viewer.zoomTo(entityCorridor)
},

// 添加圆柱实体
addEntityCylinder() {
    let entityCylinder = new Cesium.Entity({
        id: 'entityCylinder0',
        name: 'entityCylinder',
        position: Cesium.Cartesian3.fromDegrees(116.0, 40.0, 50000),
        cylinder: {
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            topRadius: 10000,
            bottomRadius: 10000,
            length: 20000,
            material: new Cesium.ImageMaterialProperty({
                // image: 'static/image/corridor.png',
                color: Cesium.Color.fromCssColorString('#FFD700'),
                transparent: false
            })
        }
    })
    let cylinderGeom = window.viewer.entities.add(entityCylinder)
    window.viewer.zoomTo(entityCylinder)
}

// 添加椭圆实体,面状,可以拉伸成体
addEntityEllipse() {
    let entityEllipse = new Cesium.Entity({
        id: 'entityEllipse0',
        name: 'entityEllipse',
        position: Cesium.Cartesian3.fromDegrees(123.0, 45.0),
        ellipse: {
            semiMajorAxis: 100000,
            semiMinorAxis: 60000,
            material: Cesium.Color.fromRandom().withAlpha(1),
						//拉伸
            extrudedHeight: 100000,
            heightReference: Cesium.HeightReference.CLAMP_TO_EDGE,
            rotation: 0.9,
            shadows: Cesium.ShadowMode.ENABLED
        }
    })
    let ellipseCeom = window.viewer.entities.add(entityEllipse)
    window.viewer.zoomTo(entityEllipse)
}

// 添加椭球实体
addEntityEllipsoid() {
    this.trackedEntity = new Cesium.Entity({
        id: 'EntityEllipsoid0',
        name: 'EntityEllipsoid',
        position: Cesium.Cartesian3.fromDegrees(111, 35, 100),
        ellipsoid: {
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            radii: new Cesium.Cartesian3(30000, 50000, 30000),
            material: Cesium.Color.fromRandom().withAlpha(1),
            outline: true,
            outlineColor: Cesium.Color.BLACK
        }
    })
    let ellipsoidGeom = window.viewer.entities.add(this.trackedEntity)
    window.viewer.zoomTo(this.trackedEntity)
},

// 添加文本标签, 可以根据缩放的程度控制标签大小或者显示与隐藏
addEntityLabel() {
    let entityLabel = new Cesium.Entity({
        id: 'entityLabel0',
        name: 'entityLabel',
        position: Cesium.Cartesian3.fromDegrees(123.5, 45.5, 1000),
        label: {
            text: '臣本布衣,躬耕于陇亩',
            fillColor: Cesium.Color.YELLOWGREEN,
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            showBackground: true,
            backgroundColor: Cesium.Color(255, 255, 0),
            scaleByDistance: new Cesium.NearFarScalar(1.5e2, 2.0, 1.5e7, 0.5),
            translucencyByDistance: new Cesium.NearFarScalar(
                1.5e2,
                1.0,
                1.5e8,
                0.0
            ),
            verticalOrigin: Cesium.VerticalOrigin.LEFT
        }
    })
    let labelsGeom = window.viewer.entities.add(entityLabel)
    window.viewer.zoomTo(entityLabel)
},

// 添加平面实体, 没有厚度
addEntityPlane() {
    let entityPlane = new Cesium.Entity({
        id: 'entityPlane0',
        name: 'entityPlane',
        position: Cesium.Cartesian3.fromDegrees(123.6, 45.8),
        plane: {
            plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_Y, 0.0),
            dimensions: new Cesium.Cartesian2(400000.0, 300000.0),
            material: Cesium.Color.BLUE
        }
    })
    let planeGeom = window.viewer.entities.add(entityPlane)
    window.viewer.zoomTo(planeGeom)
},

// 添加类似管装线实体
addEntityPloylineVolum() {
    let entityPolylineVolum = new Cesium.Entity({
        id: 'EntityPloylineVolum',
        name: 'addEntityPloylineVolum',
        polylineVolume: {
            positions: Cesium.Cartesian3.fromDegreesArray([
                120.0, 40.0, 120.0, 45.0, 125.0, 45.0
            ]),
            shape: [
                new Cesium.Cartesian2(-10000, 0),
                new Cesium.Cartesian2(10000, 0),
                new Cesium.Cartesian2(20000, 17340),
                new Cesium.Cartesian2(0, 37340),
                new Cesium.Cartesian2(-20000, 17340)
            ],
            material: Cesium.Color.YELLOWGREEN,
            fill: true,
            outline: true,
            outlineColor: Cesium.Color.YELLOW,
            outlineWidth: 10
        }
    })
    let polylineVolume = window.viewer.entities.add(entityPolylineVolum)
    window.viewer.zoomTo(polylineVolume)
}

// 添加矩形实体
addEntityRectangle() {
    let entityRectangle = new Cesium.Entity({
        id: 'entityRectangle0',
        name: 'entityRectangle',
        rectangle: {
			// west, south, east, north
            coordinates: Cesium.Rectangle.fromDegrees(120.0, 40, 125, 45), 
            // material: Cesium.Color.PURPLE.withAlpha(0.6),
            material: 'static/image/hzw.png'
            // outline: true, // height must be set for outline to display
            // outlineColor: Cesium.Color.RED,
            // extrudedHeight: 10000,
        }
    })
    let rectangleGeom = window.viewer.entities.add(entityRectangle)
    window.viewer.zoomTo(rectangleGeom)
},

// 添加墙实体
addEntityWall() {
    let entityWall = new Cesium.Entity({
        id: 'enttiyWall0',
        name: 'entityWall',
        wall: {
            positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                123.0, 40.0, 100000, 123.5, 40.5, 12012, 124.0, 41.0, 210000, 124.5,
                41.5, 220000, 125.0, 42.0, 52136
            ]),
            material: new Cesium.ImageMaterialProperty({
                image: 'static/image/corridor.png',
                repeat: new Cesium.Cartesian2(32, 32)
            })
        }
    })
    let wallGeom = window.viewer.entities.add(entityWall)
    window.viewer.zoomTo(entityWall)
}

// 点实体
viewer.entities.add({
    position: position, // 格式为世界坐标的点位置
    point: new Cesium.PointGraphics({
        show: Boolean, // 点是否可见
        pixelSize: Number, // 点的大小
        color: color, // 点的颜色
        outlineClor: color, // 边框宽度
        outlineWidth: Boolean, // 轮廓颜色
    });
});

// 线实体
viewer.entities.add({
    golyline: new Cesium.PolylineGraphics({
        show: Boolean, // 线是否可见
        positions: positions, // 格式为 Cartesian3 的线位置数组
        width: Number, // 线的宽度
        material: color, // 线的颜色
        clampToGround: Boolean, // 线是否固定在地面
    });
});

// 面实体
viewer.entities.add({
    golygon: new Cesium.PolygonGraphics({
        show: Boolean, // 面是否可见
        hierarchy: positions, // 格式为 Cartesian3 的面位置数组
        height: Number, // 面相对于椭球表面的高度
        fill: Boolean, // 面是否有填充
        material: color, // 面的填充颜色
        outline: Boolean, // 面是否有边框
        outlinColor: color, // 边框的颜色
        outlineWidth: Number, // 边框的宽度,无论设置为多少宽度始终只会显示为 1
        perPositionHeight: Boolean, // 面是否使用每个位置的高度
    });
});

更多其他的多边形

# polygon添加边界线不起作用

关于cesium添加polygon,有时需要添加边界线。但是设置outline不起作用,outline不起作用的原因

viewer.entities.add({
  name: '等值线',
  polygon: {
    hierarchy: {
      positions: Cesium.Cartesian3.fromDegreesArray(polyArr),
    },
    material: Cesium.Color.RED.withAlpha(1),
    zIndex: zIndex,
    height: zIndex * 10000,  //多层次
    outline: true,
    outlineColor: Cesium.Color.BLACK,
    outlineWidth:1,
  }
});

首先说明一下绘制多个polygon中,zIndex和height的区别:

  • 绘制多个polygon,相当于将多个polygon贴在地球表面
  • zIndex的作用就是设置绘制的等级。zindex越高,绘制的polygon会贴在最上面
  • height的作用则是在不同层次上绘制polygon。每个polygon的height不一样,则边界就会显现出来
所以outline不起作用的原因在于:多个polygon在同一height,没有设置height区分图层
所有polygon在同一图层上,outline不起作用

明白绘制不出原因后,就可以有两个解决思路:

  • 设置zindex和height。如下:可以展示多维效果
viewer.entities.add({
  name: '等值线',
  polygon: {
    hierarchy: {
      positions: Cesium.Cartesian3.fromDegreesArray(polyArr),
    },
    material: Cesium.Color.RED.withAlpha(1),
    zIndex: zIndex,
    height: zIndex * 10000,  //多层次
    outline: true,
    outlineColor: Cesium.Color.BLACK,
    outlineWidth:1,
  }
});
  • 再绘制完polygon后,再循环一遍,同样的数据,绘制polyline
polyline:{
	positions: Cesium.Cartesian3.fromDegreesArray(polyArr),
	material: Cesium.Color.fromCssColorString(contourColor).withAlpha(1),
	loop: true,
	width: 2
}

# polygon中extrudedHeight 和 height 的区别

  • height: 多边形的高度(多边形底部到地球表面的高度)
  • extrudedHeight: extrudedHeight 根据情况不同所代表的高度是不同的

# extrudedHeight > height 情况

  • height:多边形下表面到地球表面的高度
  • extrudedHeight: 多边体的高度 + height`(多边形上表面到地球表面的高度)

extrudedHeight

# extrudedHeight < height 情况

  • height:多边形下表面到地球表面的高度
  • extrudedHeight: height - 多边体的高度`(多边形下表面到地球表面的高度)

extrudedHeight

# extrudedHeight = height 情况

就是多边形的一个平面,不是多边体了

extrudedHeight

# 多边形中心点偏移问题解决

之前在网上搜到的获取中心点的方法如下:

var viewer = new Cesium.Viewer("cesiumContainer");
var polygon = viewer.entities.add({
  name: "Red polygon on surface",
  polygon: {
    hierarchy: Cesium.Cartesian3.fromDegreesArray([-115.0,37.0,-115.0,32.0,-107.0,33.0]),
    material: Cesium.Color.RED.withAlpha(0.5),
  },
});
var polyPositions = polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
var polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center;//中心点
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
// 添加label
redPolygon.label={
    position: polyCenter,
    text:'polygon title',
    color : Cesium.Color.fromCssColorString('#fff'),
    font:'normal 32px MicroSoft YaHei',
    showBackground : true,
    scale : 0.5,
    color: Cesium.Color.fromCssColorString('#fff'),
    font: 'normal 32px MicroSoft YaHei',
    showBackground: true,
    scale: 0.5,
    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
};
viewer.zoomTo(viewer.entities);

但是如果多边形不太规则或者点比较多的情况下,标记的位置就不在中心点了,会有偏移,boundingsphere 计算的中心点是外接圆的圆心,所以导致了中心点会在图形外

// 多边形的坐标集合(如果已经获取到了,就跳过这一步)
var polygon_point_arr = polygon_point_entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
// 保存转换后的点数组,这个格式必须按照 turf 的要求来
let turf_arr = [[]];
// 坐标转换
polygon_point_arr.forEach(val => {
	let polyObj = {}
	// 空间坐标转世界坐标(弧度) 同 Cesium.Cartographic.fromCartesian
	let cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(val)
	// 弧度转为角度(经纬度)
	polyObj.lon = Cesium.Math.toDegrees(cartographic.longitude)
	polyObj.lat = Cesium.Math.toDegrees(cartographic.latitude)
	turf_arr[0].push([polyObj.lon, polyObj.lat])
})
// turf 需要将整个点闭合,所以最后一个点必须和起点重合。
turf_arr[0].push(turf_arr[0][0])
let turf_position = turf.polygon(turf_arr)
let turf_position_point = turf.centerOfMass(turf_position)
// 设置点标记坐标
polygon_point_entity.position = Cesium.Cartesian3.fromDegrees(turf_position_point.geometry.coordinates[0], turf_position_point.geometry.coordinates[1], 0)
// 添加点标记
polygon_point_entity.label = {
	// 点标记参数
}

# 地球二三维切换(视图类型)

API 上有 4 种:3D、3D 、 2.5D(哥伦布视图CV)和变形。常用的是前 3 种。

  • 方式一
// 2.5D哥伦布模式
viewer.scene.mode = Cesium.SceneMode.COLUMBUS_VIEW;
// 2D模式
viewer.scene.mode = Cesium.SceneMode.SCENE2D;
// 3D模式
viewer.scene.mode = Cesium.SceneMode.SCENE3D;
// 变形模式
viewer.scene.mode = Cesium.SceneMode.MORPHING;
  • 方式二
// 切换为3D模式
viewer.scene.morphTo3D(2);//切换花费的时间
// 切换为2D模式
viewer.scene.morphTo2D(2);//切换花费的时间
//哥伦布模式
viewer.scene.morphToColumbusView(2);//切换花费的时间
  • 判断当前场景的视图类型:
if (this._viewer.scene.mode !== SceneMode.SCENE3D) {
    this._viewer.scene.morphTo3D(0);
}

# 填挖方分析

用于计算绘制的填挖三维面与模型图层或地形图层之间的填挖量。常用于工程中计算挖掉的土方体积或者需要往一个坑中填埋多少体积的土或水泥,这个功能应用场景较为广泛,例如修建房屋打地基时计算挖掉多少体积的土。

实现思路:绘制一个多边形,对多边形内区域进行采样,按照设定的精度将其分解成“无数”的三角形,构建一个类似Tin网格的几何结构,这样我们只需要计算每个类三棱柱体的体积和底面积,根据三棱柱体积计算公式:V = 底面积 * 高,即可获取到我们所要计算的填、挖方面积和体积。

# 测量工具

测距测高在Cesium中有相关的接口用来计算,但是测面没有计算方法,这里是通过turf.js来计算,所以计算的是平面面积

# 测量距离

//距离测量类
export default class MeasureDistance {
    constructor(viewer) {
        this.viewer = viewer;
        this.initEvents();
        this.positions = [];
        this.tempPositions = [];
        this.vertexEntities = [];
        this.labelEntity = undefined;
        this.measureDistance = 0; //测量结果
    }

    //初始化事件
    initEvents() {
        this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
        this.MeasureStartEvent = new Cesium.Event(); //开始事件
        this.MeasureEndEvent = new Cesium.Event(); //结束事件        
    }

    //激活
    activate() {
        this.deactivate();
        this.registerEvents(); //注册鼠标事件  
        //设置鼠标状态 
        this.viewer.enableCursorStyle = false;
        this.viewer._element.style.cursor = 'default';
        this.isMeasure = true;
        this.measureDistance = 0;
    }

    //禁用
    deactivate() {
        if (!this.isMeasure) return;
        this.unRegisterEvents();
        this.viewer._element.style.cursor = 'pointer';
        this.viewer.enableCursorStyle = true;
        this.isMeasure = false;
        this.tempPositions = [];
        this.positions = [];
    }

    //清空绘制
    clear() {
        //清除线对象
        this.viewer.entities.remove(this.lineEntity);
        this.lineEntity = undefined;

        //清除节点
        this.vertexEntities.forEach(item => {
            this.viewer.entities.remove(item);
        });
        this.vertexEntities = [];
    }

    //创建线对象
    createLineEntity() {
        this.lineEntity = this.viewer.entities.add({
            polyline: {
                positions: new Cesium.CallbackProperty(e => {
                    return this.tempPositions;
                }, false),
                width: 2,
                material: Cesium.Color.YELLOW,
                depthFailMaterial: Cesium.Color.YELLOW
            }
        })
    }

    //创建线节点
    createVertex() {
        let vertexEntity = this.viewer.entities.add({
            position: this.positions[this.positions.length - 1],
            id: "MeasureDistanceVertex" + this.positions[this.positions.length - 1],
            type: "MeasureDistanceVertex",
            label: {
                text: spaceDistance(this.positions) + "米",
                scale: 0.5,
                font: 'normal 24px MicroSoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(1000, 1, 3000, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -30),
                outlineWidth: 9,
                outlineColor: Cesium.Color.WHITE
            },
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 8,
                disableDepthTestDistance: 500,
            },
        });
        this.vertexEntities.push(vertexEntity);
    }
 
    //创建起点
    createStartEntity() {
        let vertexEntity = this.viewer.entities.add({
            position: this.positions[0],
            type: "MeasureDistanceVertex",
            billboard: {
                image: "../../static/images/start.png",
                scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.4), //设置随图缩放距离和比例
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), //设置可见距离 10000米可见
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 6,
            },
        });
        this.vertexEntities.push(vertexEntity);
    }

    //创建终点节点
    createEndEntity() {
        //结束时删除最后一个节点的距离标识
        let lastLabel = this.viewer.entities.getById("MeasureDistanceVertex" + this.positions[this.positions.length - 1]);
        this.viewer.entities.remove(lastLabel);
        this.viewer.entities.remove(this.moveVertexEntity);

        let vertexEntity = this.viewer.entities.add({
            position: this.positions[this.positions.length - 1],
            type: "MeasureDistanceVertex",
            label: {
                text: "总距离:" + spaceDistance(this.positions) + "米",
                scale: 0.5,
                font: 'normal 26px MicroSoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(1000, 1, 3000, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -50),
                outlineWidth: 9,
                outlineColor: Cesium.Color.WHITE,
                eyeOffset: new Cesium.Cartesian3(0, 0, -10)
            },
            billboard: {
                image: "../../static/images/end.png",
                scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.4), //设置随图缩放距离和比例
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), //设置可见距离 10000米可见
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 6,
            },
        });
        this.vertexEntities.push(vertexEntity);
    }

    //注册鼠标事件
    registerEvents() {
        this.leftClickEvent();
        this.rightClickEvent();
        this.mouseMoveEvent();
    }

    //左键点击事件
    leftClickEvent() {
        //单击鼠标左键画点点击事件
        this.handler.setInputAction(e => {
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.position);
            if (!position) {
                const ellipsoid = this.viewer.scene.globe.ellipsoid;
                position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
            }
            if (!position) return;
            this.positions.push(position);
            if (this.positions.length == 1) { //首次点击  
                this.createLineEntity();
                this.createStartEntity();
                return;
            }
            this.createVertex();

        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }

    //鼠标移动事件
    mouseMoveEvent() {
        this.handler.setInputAction(e => {
            if (!this.isMeasure) return;
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.endPosition);
            if (!position) {
                position = this.viewer.scene.camera.pickEllipsoid(e.startPosition, this.viewer.scene.globe.ellipsoid);
            }
            if (!position) return;
            this.handleMoveEvent(position);
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }

    //处理鼠标移动
    handleMoveEvent(position) {
        if (this.positions.length < 1) return;
        this.tempPositions = this.positions.concat(position);
    }

    //右键事件
    rightClickEvent() {
        this.handler.setInputAction(e => {
            if (!this.isMeasure || this.positions.length < 1) {
                this.deactivate();
                this.clear();
            } else {
                this.createEndEntity();
                this.lineEntity.polyline = {
                    positions: this.positions,
                    width: 2,
                    material: Cesium.Color.YELLOW,
                    depthFailMaterial: Cesium.Color.YELLOW
                };
                this.measureEnd();
            }

        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }

    //测量结束
    measureEnd() {
        this.deactivate();
        this.MeasureEndEvent.raiseEvent(this.measureDistance); //触发结束事件 传入结果
    }

    //解除鼠标事件
    unRegisterEvents() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
}

# 测量高度

//高度测量类
export default class MeasureHeight {
    constructor(viewer) {
        this.viewer = viewer;
        this.initEvents();
        this.positions = [];
        this.vertexEntities = [];
        this.labelEntity = undefined;
        this.measureHeight = 0; //测量结果
    }

    //初始化事件
    initEvents() {
        this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
        this.MeasureStartEvent = new Cesium.Event(); //开始事件
        this.MeasureEndEvent = new Cesium.Event(); //结束事件        
    }

    //激活
    activate() {
        this.deactivate();
        this.registerEvents(); //注册鼠标事件  
        //设置鼠标状态 
        this.viewer.enableCursorStyle = false;
        this.viewer._element.style.cursor = 'default';
        this.isMeasure = true;
        this.circleRadius = 0.1;
        this.measureHeight = 0;
        this.positions = [];
    }

    //禁用
    deactivate() {
        if (!this.isMeasure) return;
        this.unRegisterEvents();
        this.viewer._element.style.cursor = 'pointer';
        this.viewer.enableCursorStyle = true;
        this.isMeasure = false;
    }

    //清空绘制
    clear() {
        //清除线对象
        this.viewer.entities.remove(this.lineEntity);
        this.lineEntity = undefined;

        //清除文本
        this.viewer.entities.remove(this.labelEntity);
        this.labelEntity = undefined;

        //移除圆
        this.removeCircleEntity();

        //清除节点
        this.vertexEntities.forEach(item => {
            this.viewer.entities.remove(item);
        });
        this.vertexEntities = [];
    }

    //创建线对象
    createLineEntity() {
        this.lineEntity = this.viewer.entities.add({
            polyline: {
                positions: new Cesium.CallbackProperty(e => {
                    return this.positions;
                }, false),
                width: 2,
                material: Cesium.Color.YELLOW,
                depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.RED,
                }),
            }
        })
    }

    //创建结果文本标签
    createLabel() {
        this.labelEntity = this.viewer.entities.add({
            position: new Cesium.CallbackProperty(e => {
                return this.positions[this.positions.length - 1]; //返回最后一个点
            }, false),
            label: {
                text: "",
                scale: 0.5,
                font: 'normal 40px MicroSoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(500, 1, 1500, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -30),
                outlineWidth: 9,
                outlineColor: Cesium.Color.WHITE
            }
        })
    }

    //创建线节点
    createVertex(index) {
        let vertexEntity = this.viewer.entities.add({
            position: new Cesium.CallbackProperty(e => {
                return this.positions[index];
            }, false),
            type: "MeasureHeightVertex",
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 6,
                // disableDepthTestDistance: 2000,
            },
        });
        this.vertexEntities.push(vertexEntity);
    }

    //创建圆 这样方便看出水平面的高低
    createCircleEntitiy() {
        this.circleEntity = this.viewer.entities.add({
            position: new Cesium.CallbackProperty(e => {
                return this.positions[this.positions.length - 1]; //返回最后一个点
            }, false),
            ellipse: {
                height: new Cesium.CallbackProperty(e => {
                    return positionHeight(this.positions[this.positions.length - 1]);
                }, false),
                semiMinorAxis: new Cesium.CallbackProperty(e => {
                    return this.circleRadius;
                }, false),
                semiMajorAxis: new Cesium.CallbackProperty(e => {
                    return this.circleRadius;
                }, false),
                material: Cesium.Color.YELLOW.withAlpha(0.5),
            },
        });
    }

    //删除圆
    removeCircleEntity() {
        this.viewer.entities.remove(this.circleEntity);
        this.circleEntity = undefined;
    }

    //注册鼠标事件
    registerEvents() {
        this.leftClickEvent();
        this.rightClickEvent();
        this.mouseMoveEvent();
    }

    //左键点击事件
    leftClickEvent() {
        //单击鼠标左键画点点击事件
        this.handler.setInputAction(e => {
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.position);
            if (!position) {
                const ellipsoid = this.viewer.scene.globe.ellipsoid;
                position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
            }
            if (!position) return;

            if (this.positions.length == 0) { //首次点击
                this.positions.push(position);
                this.createVertex(0);
                this.createLineEntity();
                this.createCircleEntitiy();
                this.createLabel();
            } else { //第二次点击结束测量
                this.measureEnd();
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }

    //鼠标移动事件
    mouseMoveEvent() {
        this.handler.setInputAction(e => {
            if (!this.isMeasure) return;
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.endPosition);
            if (!position) {
                position = this.viewer.scene.camera.pickEllipsoid(e.startPosition, this.viewer.scene.globe.ellipsoid);
            }
            if (!position) return;
            this.handleMoveEvent(position);
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }

    //处理鼠标移动
    handleMoveEvent(position) {
        if (this.positions.length < 1) return;
        let firstPoint = cartesian3Point3(this.positions[0]); //第一个点
        let movePoint = cartesian3Point3(position); //鼠标移动点
        const h = movePoint[2] - firstPoint[2];
        firstPoint[2] = movePoint[2];
        const twoPosition = Cesium.Cartesian3.fromDegrees(firstPoint[0], firstPoint[1], movePoint[2]);
        if (this.positions.length < 2) {
            this.positions.push(twoPosition);
            this.createVertex(1);
        } else {
            this.positions[1] = twoPosition;
            this.measureHeight = h.toFixed(3);
            this.labelEntity.label.text = "高度:" + this.measureHeight + " 米"
        }
        //计算圆的半径   
        this.circleRadius = getDistanceH(this.positions[0], position);
    }

    //右键事件
    rightClickEvent() {
        this.handler.setInputAction(e => {
            if (this.isMeasure) {
                this.deactivate();
                this.clear();
            }
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }

    //测量结束
    measureEnd() {
        this.deactivate();
        this.MeasureEndEvent.raiseEvent(this.measureHeight); //触发结束事件 传入结果
    }

    //解除鼠标事件
    unRegisterEvents() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
}

# 测量面积

//面积测量类
export default class MeasureDistance {
    constructor(viewer) {
        this.viewer = viewer;
        this.initEvents();
        this.positions = [];
        this.tempPositions = [];
        this.vertexEntities = [];
        this.labelEntity = undefined;
        this.measureArea = 0; //测量结果
    }

    //初始化事件
    initEvents() {
        this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
        this.MeasureStartEvent = new Cesium.Event(); //开始事件
        this.MeasureEndEvent = new Cesium.Event(); //结束事件        
    }

    //激活
    activate() {
        this.deactivate();
        this.registerEvents(); //注册鼠标事件  
        //设置鼠标状态 
        this.viewer.enableCursorStyle = false;
        this.viewer._element.style.cursor = 'default';
        this.isMeasure = true;
        this.measureArea = 0;
    }

    //禁用
    deactivate() {
        if (!this.isMeasure) return;
        this.unRegisterEvents();
        this.viewer._element.style.cursor = 'pointer';
        this.viewer.enableCursorStyle = true;
        this.isMeasure = false;
        this.tempPositions = [];
        this.positions = [];
        this.height = undefined;
    }

    //清空绘制
    clear() {
        //清除线面对象
        this.viewer.entities.remove(this.polygonEntity);
        this.polygonEntity = undefined;

        //清除节点
        this.vertexEntities.forEach(item => {
            this.viewer.entities.remove(item);
        });
        this.vertexEntities = [];

        this.viewer.entities.remove(this.mesureResultEntity);
        this.mesureResultEntity = undefined;

        this.height = undefined;
    }

    //创建面对象
    createPolygonEntity() {
        this.polygonEntity = this.viewer.entities.add({
            polygon: {
                hierarchy: new Cesium.CallbackProperty(e => {
                    return new Cesium.PolygonHierarchy(this.tempPositions);
                    //使用最新1.72的时候 必须返回PolygonHierarchy类型 Cannot read property 'length' of undefined
                    //低版本好像都可以
                }, false),
                material: Cesium.Color.RED.withAlpha(0.4),
                perPositionHeight: true, //  
            },
            polyline: {
                positions: new Cesium.CallbackProperty(e => {
                    return this.tempPositions.concat(this.tempPositions[0]);
                }, false),
                width: 1,
                material: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.YELLOW,
                }),
                depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.YELLOW,
                }),
            }

        })
    }

    //创建节点
    createVertex() {
        let vertexEntity = this.viewer.entities.add({
            position: this.positions[this.positions.length - 1],
            type: "MeasureAreaVertex",
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 8,
                disableDepthTestDistance: 500,
            },
        });
        this.vertexEntities.push(vertexEntity);
    }

    //测量结果标签
    createResultLabel() {
        this.mesureResultEntity = this.viewer.entities.add({
            position: new Cesium.CallbackProperty(e => {
                return this.getCenterPosition()
            }, false),
            type: "MeasureAreaResult",
            label: {
                text: new Cesium.CallbackProperty(e => {
                    return "面积" + computeArea(this.tempPositions) + "平方米";
                }, false),
                scale: 0.5,
                font: 'normal 28px MicroSoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(1000, 1, 3000, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -30),
                outlineWidth: 9,
                outlineColor: Cesium.Color.YELLOW
            },
        });

    }

    //获取节点的中心点
    getCenterPosition() {
        let points = [];
        if (this.tempPositions.length < 3) return this.tempPositions[0];
        this.tempPositions.forEach(position => {
            const point3d = this.cartesian3ToPoint3D(position);
            points.push([point3d.x, point3d.y]);
        })

        //构建turf.js  lineString
        let geo = turf.lineString(points);
        let bbox = turf.bbox(geo);
        let bboxPolygon = turf.bboxPolygon(bbox);
        let pointOnFeature = turf.center(bboxPolygon);
        let lonLat = pointOnFeature.geometry.coordinates;

        return Cesium.Cartesian3.fromDegrees(lonLat[0], lonLat[1], this.height + 0.3);
    }


    //注册鼠标事件
    registerEvents() {
        this.leftClickEvent();
        this.rightClickEvent();
        this.mouseMoveEvent();
    }

    //左键点击事件
    leftClickEvent() {
        //单击鼠标左键画点点击事件
        this.handler.setInputAction(e => {
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.position);
            if (!position) {
                const ellipsoid = this.viewer.scene.globe.ellipsoid;
                position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
            }
            if (!position) return;
            this.positions.push(position);
            this.height = this.unifiedHeight(this.positions, this.height);
            if (this.positions.length == 1) { //首次点击  
                this.createPolygonEntity();
            }
            this.createVertex();

        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }

    //鼠标移动事件
    mouseMoveEvent() {
        this.handler.setInputAction(e => {
            if (!this.isMeasure) return;
            this.viewer._element.style.cursor = 'default';
            let position = this.viewer.scene.pickPosition(e.endPosition);
            if (!position) {
                position = this.viewer.scene.camera.pickEllipsoid(e.startPosition, this.viewer.scene.globe.ellipsoid);
            }
            if (!position) return;
            this.handleMoveEvent(position);
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }

    //处理鼠标移动
    handleMoveEvent(position) {
        if (this.positions.length < 1) return;

        this.height = this.unifiedHeight(this.positions, this.height);
        this.tempPositions = this.positions.concat(position);
        if (this.tempPositions.length >= 3 && !this.mesureResultEntity) {
            this.createResultLabel();
        }
    }

    //统一节点的高度
    unifiedHeight(positions, height) {
        if (!height) height = this.getPositionHeight(positions[0]); //如果没有指定高度 就用第一个的高度
        let point3d;
        for (let i = 0; i < positions.length; i++) {
            const element = positions[i];
            point3d = this.cartesian3ToPoint3D(element);
            positions[i] = Cesium.Cartesian3.fromDegrees(point3d.x, point3d.y, height)
        }

        return height;
    }

    //获取某个点的高度
    getPositionHeight(position) {
        const cartographic = Cesium.Cartographic.fromCartesian(position);
        return cartographic.height;
    }

    cartesian3ToPoint3D(position) {
        const cartographic = Cesium.Cartographic.fromCartesian(position);
        const lon = Cesium.Math.toDegrees(cartographic.longitude);
        const lat = Cesium.Math.toDegrees(cartographic.latitude);
        return { x: lon, y: lat, z: cartographic.height };
    }


    //右键事件
    rightClickEvent() {
        this.handler.setInputAction(e => {
            if (!this.isMeasure || this.positions.length < 3) {
                this.deactivate();
                this.clear();
            } else {
                this.tempPositions = [...this.positions];
                this.polygonEntity.polyline = {
                    positions: this.positions.concat(this.positions[0]),
                    width: 2,
                    material: Cesium.Color.YELLOW,
                    depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
                        color: Cesium.Color.YELLOW,
                    }),
                };

                this.polygonEntity.polygon.hierarchy = new Cesium.PolygonHierarchy(this.tempPositions);
                this.mesureResultEntity.position = this.getCenterPosition();
                this.mesureResultEntity.label.text = "总面积" + computeArea(this.positions) + "平方米"
                this.measureEnd();
            }

        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }

    //测量结束
    measureEnd() {
        this.deactivate();
        this.MeasureEndEvent.raiseEvent(this.measureArea); //触发结束事件 传入结果
    }

    //解除鼠标事件
    unRegisterEvents() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
}

# 两种添加 model 方法的区别

Cesium 中包含两种添加 model 的方法,分别为:

  • 通过 viewer.entities.add() 函数添加
  • 通过 viewer.scene.primitives.add() 函数添加,更底层一些

两种方法本质上是相通的,方法 1对方法 2在某种程度上进行了封装。

# 两种方法的区别

  • 方法 1通过指定 model 的 position 和 orientation 来控制模型的位置,对模型进行精确变换难度较大
    方法 2通过 modelMatrix 控制模型的位置和方向,可进行较为精确的模型变换
  • 对相机操作时
    方法1提供了较为方便的 viewer.trackedEntity 函数
    方法 2追踪 model 较为复杂,需要手动操作相机变换
  • 对模型进行缩放、变换等操作
    方法 1 需要修改 object.id(Entity 类型) 中 model(ModelGraphics 类型) 的 scale 和 nodeTransformations
    方法 2 可以直接修改 object.primitive(model 类型) 中的 scale 和 modelMatrix
  • Entity没有加载完成事件,而Primitive有加载完成事件
  • Entity只能加载一个对象,而Primitive可以一次加载多个对象
  • Entity类封装了很多个几何对象,非常方便的绘制出几何对象来,而如果使用Primitive的话你需要懂的知识不少,比如几何对象的构造

# 两种方法的相同

  • 构建的对象中还包含 ModelMesh 和 ModeNode,ModelMesh 中包含了模型的网格和材质,ModeNode 中包含了一个 transform,可以在运行时对模型进行动态变换,以实现自定义模型动画。
  • 分别使用方法 1和方法 2建立两个模型,然后对其进行点击操作,查看点击得到的物体。
    两种方法返回同样的对象:{id: object,mesh: ModelMesh,node: ModeNode,primitive: Model}
  • 从 Entity 获得 Model 的方法:
function getModelForEntity(entity) {
    var primitives = viewer.scene.primitives;
    for (var i = 0; i < primitives.length; i++) {
        var primitive = primitives.get(i);
        if (primitive instanceof Cesium.Model && primitive.id === entity) {
            return primitive;
        }
    }
};

# 示例代码











 
 
 
 
 
 
 
 
 
 



function createModel(url, height) {
    viewer.entities.removeAll();

    var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
    var heading = Cesium.Math.toRadians(135);
    var pitch = 0;
    var roll = 0;
    var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
    var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);

    var entity = viewer.entities.add({
        name : url,
        position : position,
        orientation : orientation,
        model : {
            uri : url,
            minimumPixelSize : 128,
            maximumScale : 20000
        }
    });
    viewer.trackedEntity = entity;
}











 
 
 
 
 






























function createModel(url, height, heading, pitch, roll) {
    height = Cesium.defaultValue(height, 0.0);
    heading = Cesium.defaultValue(heading, 0.0);
    pitch = Cesium.defaultValue(pitch, 0.0);
    roll = Cesium.defaultValue(roll, 0.0);
    var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);

    var origin = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
    var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, hpr);

    scene.primitives.removeAll(); // Remove previous model
    model = scene.primitives.add(Cesium.Model.fromGltf({
        url : url,
        modelMatrix : modelMatrix,
        minimumPixelSize : 128
    }));

    model.readyPromise.then(function(model) {
        model.color = Cesium.Color.fromAlpha(getColor(viewModel.color),
		                                      Number(viewModel.alpha));
        model.colorBlendMode = getColorBlendMode(viewModel.colorBlendMode);
        model.colorBlendAmount = viewModel.colorBlendAmount;
        // Play and loop all animations at half-speed
        model.activeAnimations.addAll({
            speedup : 0.5,
            loop : Cesium.ModelAnimationLoop.REPEAT
        });

        var camera = viewer.camera;

        // Zoom to model
        var controller = scene.screenSpaceCameraController;
        var r = 2.0 * Math.max(model.boundingSphere.radius, camera.frustum.near);
        controller.minimumZoomDistance = r * 0.5;

        var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, 
		                                            model.boundingSphere.center, 
													new Cesium.Cartesian3());
        var heading = Cesium.Math.toRadians(230.0);
        var pitch = Cesium.Math.toRadians(-20.0);
        camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, r * 2.0));
    }).otherwise(function(error){
        window.alert(error);
    });
}

# 获取地形的高度

# 区域地形的最低点和最高点

Cesium.ApproximateTerrainHeights.initialize()
Cesium.ApproximateTerrainHeights.getMinimumMaximumHeights(
                                   Cesium.Rectangle.fromDegrees(110,30,110.5,30.04)
								 )

# 获取地形的高度

  • globe的方法
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
const height = this.viewer.scene.sampleHeightSupported ? 
               this.viewer.scene.sampleHeight(cartographic) : 
			   this.viewer.scene.globe.getHeight(cartographic)
  • sampleTerrain:获取非精确的地形的高度
var terrainProvider = Cesium.createWorldTerrain();
var positions = [
    Cesium.Cartographic.fromDegrees(86.925145, 27.988257),
    Cesium.Cartographic.fromDegrees(87.0, 28.0)
];
var promise = Cesium.sampleTerrain(terrainProvider, 11, positions);
Cesium.when(promise, function(updatedPositions) {
    // positions[0].height and positions[1].height have been updated.
    // updatedPositions is just a reference to positions.
});
  • sampleTerrainMostDetailed:获取尽量精确的地形的高度
// Query the terrain height of two Cartographic positions
var terrainProvider = Cesium.createWorldTerrain();
var positions = [
    Cesium.Cartographic.fromDegrees(86.925145, 27.988257),
    Cesium.Cartographic.fromDegrees(87.0, 28.0)
];
var promise = Cesium.sampleTerrainMostDetailed(terrainProvider, positions);
Cesium.when(promise, function(updatedPositions) {
    // positions[0].height and positions[1].height have been updated.
    // updatedPositions is just a reference to positions.
});

# 获取当前视图范围

getPickRay这个方法能够通过屏幕坐标转换成经纬度坐标。屏幕坐标的话,我们可以通过div位置来获取,这样屏幕的左上角坐标和右下角坐标是可以很容易获取到的。

var pt1 = new Cesium.Cartesian2(0,0);
var pt2= new Cesium.Cartesian2(500,500);
 
var pick1= viewer.scene.globe.pick(viewer.camera.getPickRay(pt1), viewer.scene);
var pick2= viewer.scene.globe.pick(viewer.camera.getPickRay(pt2), viewer.scene);
 
//将三维坐标转成地理坐标
var geoPt1= viewer.scene.globe.ellipsoid.cartesianToCartographic(pick1);
var geoPt2= viewer.scene.globe.ellipsoid.cartesianToCartographic(pick2);
 
//地理坐标转换为经纬度坐标
var point1=[geoPt1.longitude / Math.PI * 180,geoPt1.latitude / Math.PI * 180];
var point2=[geoPt2.longitude / Math.PI * 180,geoPt2.latitude / Math.PI * 180];

# 不同数据的贴地处理

# 实体的贴地处理

实体类型的贴地处理,是设置heightReference属性为CLAMP_TO_GROUND值

let entity = new Cesium.Entity({
  name: '点',
  show: true,
  position: Cesium.Cartesian3.fromDegrees(positions[0], positions[1], positions[2]),
  point: new Cesium.PointGraphics ( {
      show : true,
      pixelSize : 10,
	  //设置HeightReference高度参考类型为CLAMP_TO_GROUND贴地类型
      heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,  
      color : new Cesium.Color ( 255 , 255 , 0 , 1 ),
  } )
});

对于面图层,不设置面的height:0属性,面状数据设置贴地后,边线的数据就不能显示了,需要单独添加边线数据

var geojson = {
  "type": "FeatureCollection",
  "features": [
	{
	  "type": "Feature",
	  "properties": {},
	  "geometry": {
		"type": "Polygon",
		"coordinates": [
		  [
			[
			  93.6474609375,
			  37.10776507118514
			],
			[
			  93.01025390625,
			  35.17380831799959
			],
			[
			  95.11962890625,
			  33.54139466898275
			],
			[
			  97.53662109375,
			  33.54139466898275
			],
			[
			  99.25048828124999,
			  33.96158628979907
			],
			[
			  99.97558593749999,
			  35.191766965947394
			],
			[
			  99.84374999999999,
			  36.38591277287651
			],
			[
			  98.85498046875,
			  37.45741810262938
			],
			[
			  97.18505859374999,
			  37.90953361677018
			],
			[
			  95.16357421875,
			  37.82280243352756
			],
			[
			  93.6474609375,
			  37.10776507118514
			]
		  ]
		]
	  }
	}
  ]
}
var promise = Cesium.GeoJsonDataSource.load(geojson,
{
  camera : viewer.scene.camera,
  canvas : viewer.scene.canvas,
  clampToGround: true//开启贴地
}
);
promise.then(function (dataSource) {
//Get the array of entities
var entities = dataSource.entities.values;
entities.forEach((entitie) => {
  //必须要设置高度边线才有用,可是设置了高度之后贴地就没了
  //entitie.polygon.height = 50;
  entitie.polygon.outline = true;
  entitie.polygon.outlineColor = Cesium.Color.AQUA;
  entitie.polygon.outlineWidth = 50;
  entitie.polygon.closeTop = false;
  //单独设置线条样式
  var positions = entitie.polygon.hierarchy._value.positions;
  entitie.polyline = {
	positions: positions,
	width: 10,
	material: Cesium.Color.BLACK.withAlpha(1),
	clampToGround: true
  }
  viewer.entities.add(entitie);
})
})

# geojson数据

在加载的时候设置clampToGround属性为true

const res = Cesium.GeoJsonDataSource.load("./china.geojson",{
	clampToGround: true,
})

# polyline

设置clampToGround属性为true

const point = viewer.entities.add({
  name: "polyline",
  polyline: {
    positions: Cesium.Cartesian3.fromDegreesArray(arr),
    width: 1,
    material: new Cesium.Color(222/255, 216/255, 192/255, 1),
    clampToGround: true,
  },
});

# kml数据

在加载kml文件时,有的文件,线可以贴地,有的不能贴地,后来通过分析,主要原因是原始的kml文件中设置了贴地,在cesium中才贴地,否则在cesium中设置clampToGround: true也无法实现贴地

  • kml中原始贴地格式如下:tessellate 标签为1时贴地,没有设置贴地一般就没有tessellate这个标签

kml数据

  • cesium中加载kml(主要包含为线和点)
var options = {
	camera: viewer.scene.camera,
	canvas: viewer.scene.canvas,`在这里插入代码片`
	clampToGround: true, //开启贴地
};
var kmlx=viewer.dataSources.add(Cesium.KmlDataSource.load(url, options));
kmlx.then(function (dataSource) {
	viewer.flyTo(kmlx);
    var entities = dataSource.entities.values;
	for (var i = 0; i < entities.length; i++) {
		var entity = entities[i];
	    //只对_billboard进行设置。
		if (entity._billboard) {
			//去掉地形遮挡
			entity._billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY; 
			//设置贴地
			entity.billboard.heightReference=Cesium.HeightReference.CLAMP_TO_GROUND;
			//设置文字标签,没有颜色(白色在cesium中非常不好看)替换成红色			
			entity._label.pixelOffset = new Cesium.Cartesian2(10, -20);
			if ("undefined" === typeof (entity._label.fillColor)) {
				entity._label.fillColor = Cesium.Color.RED;
			}//if
	   	}//if
	}
});

以上//只对_billboard进行设置。当为线时,设置entity.polyline.clampToGround=true可能出现错误,经过测试当发现当kml文件有标签时,是能这样设置贴地;当不存在,线就没这个属性,不能设置,就会导致出错。

  • 解决办法
    • 第一种解决办法是:修改kml文件,在线的标签后添加1
    • 第二种解决办法是修改cesium.js,就是kml加载时,不管kml中有没有标签都设置成贴地;具体如下:
      • 打开cesium.js查找 tessellate字符串
      • 在该字符串后面找到如下代码:else if(t._clampToGround&&!f&&p) 去掉后面的 &&p 保存
      • 就是当cesium中设置了clampToGround,kml无论是否设置了贴地,在加载时都实现贴地
      • 字符P是不同的

# 鼠标事件监听

可以通过Cesium.ScreenSpaceEventHandler的实例化对象的setInputAction方法绑定鼠标事件:

var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function(event) {
   // 屏幕坐标
   var feature = viewer.scene.pick(event.position);
   if (feature && feature instanceof Cesium.Cesium3DTileFeature) {
	   console.log(feature);
   }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 移除事件
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 点击左键时,若点击的实体产生变化才会调用
viewer.selectedEntityChanged.addEventListener(entity => {
    console.log(entity);
})

// 开启抗锯齿
viewer.scene.postProcessStages.fxaa.enabled = true;

setInputAction的第一个参数是当前事件触发的回调,event中的位置格式是屏幕坐标

鼠标左键双击事件 # Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
鼠标左键按下事件 # Cesium.ScreenSpaceEventType.LEFT_DOWN 
鼠标左键抬起事件 # Cesium.ScreenSpaceEventType.LEFT_UP 
鼠标中键单击事​​件 # Cesium.ScreenSpaceEventType.MIDDLE_CLICK 
鼠标中键按下事件 # Cesium.ScreenSpaceEventType.MIDDLE_DOWN 
鼠标中键抬起事件 # Cesium.ScreenSpaceEventType.MIDDLE_UP 
鼠标移动事件 # Cesium.ScreenSpaceEventType.MOUSE_MOVE 
触摸表面上的双指事件的结束 # Cesium.ScreenSpaceEventType.PINCH_END 
触摸表面上双指移动事件 # Cesium.ScreenSpaceEventType.PINCH_MOVE 
触摸表面上双指事件的开始 # Cesium.ScreenSpaceEventType.PINCH_START 
鼠标右键单击事件 # Cesium.ScreenSpaceEventType.RIGHT_CLICK 
鼠标右键按下事件 # Cesium.ScreenSpaceEventType.RIGHT_DOWN 
鼠标滚轮事件 # Cesium.ScreenSpaceEventType.WHEEL 

# 去掉entity的点击事件

//双击
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
//单击
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);

# 四种点击拾取方法

# viewer.scene.pick()

通过坐标位置拾取实体(Entity)、图元(Primitive)、3DTiles对象,返回scene中指定位置最上层的对象。例如点击获取Entity对象,通过pick.id可以拾取当前的entity对象。拾取后可以用于改变对象的属性参数。

//处理用户输入事件
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// 设置左键点击事件
handler.setInputAction(function (event) {
  // 获取 pick 拾取对象
  let pick = viewer.scene.pick(event.position); 
  // 判断是否获取到了对象
  if (Cesium.defined(pick)) {
	// 修改拾取到的entity的样式
    pick.id.billboard.image = "......"          
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

//drillPick 返回拾取到的图元对象集合,图元列表按其在场景中的视觉顺序(从前到后)排序
var pickedObjects = scene.drillPick(new Cesium.Cartesian2(100.0, 200.0));

注意

  • 只能获取一个对象,并且是最顶部的对象。如果拾取点没有对象,则会返回undefined
  • 拾取半透明图元的位置时需要开启:viewer.scene.pickTranslucentDepth = true

# viewer.scene.globe.pick()

返回一个射线(ray)和地球表面的一个交点的Cartesian3坐标。此方法一般用于获取加载地形后的经纬度和高程,不包括模型、倾斜摄影等表面高度。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
    let ray = viewer.camera.getPickRay(event.position);//获取一条射线
    let position = viewer.scene.globe.pick(ray, viewer.scene);
    console.log("当前拾取的坐标:", position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

注意

最好开启深度检测:viewer.scene.globe.depthTestAgainstTerrain = true

# viewer.scene.camera.pickEllipsoid()

返回相机视角下鼠标点击的对应椭球面位置。接收屏幕坐标,返回Cartesian3坐标。适用裸球表面的选取,是基于数学模型的椭圆球体。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
	let ellipsoid = viewer.scene.globe.ellipsoid;
    let position = viewer.scene.camera.pickEllipsoid(event.position, ellipsoid);
    console.log("点击拾取的坐标:", position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

注意

在有地形的情况下误差较大,在使用时需要关闭深度测试

# viewer.scene.pickPosition()

拾取对应位置的Cartesian3,适用于模型表面位置的选取,拾取三维物体的坐标等。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
     let position = viewer.scene.pickPosition(event.position);
     console.log("获取到的坐标:", positionposition);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

TIP

一定开启深度检测:viewer.scene.globe.depthTestAgainstTerrain = true
否则在没有没有3dTile模型的情况下,会出现空间坐标不准的问题

# 倾斜模型单体化

倾斜模型分层单体化需要数据结合代码来实现,数据生产需要采集每层的面坐标串、底部高程、层高、顶部高度等信息。有了数据后在Cesium中通过ClassificationPrimitive渲染出来。

 //创建拉伸的多边形对象
createExtrudedPolygon(id, polygonGeometry) {
	return this.viewer.scene.primitives.add(
		new Cesium.ClassificationPrimitive({
			geometryInstances: new Cesium.GeometryInstance({
				geometry: Cesium.PolygonGeometry.createGeometry(polygonGeometry),
				attributes: {
					color: Cesium.ColorGeometryInstanceAttribute.fromColor(
						Cesium.Color.fromRandom({ alpha: 0.8 })
					),
					show: new Cesium.ShowGeometryInstanceAttribute(true),
				},
				id: id, //设置id有效 其他属性无效
			}),
			classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
		})
	);
},

# 加载影像

# 默认类型

imageryProvider:默认createWorldImagery()

# 其他类型

  • ArcGisMapServerImageryProvider
  • BingMapsImageryProvider
  • OpenStreetMapImageryProvider
  • TileMapServiceImageryProvider
  • GoogleEarthEnterpriseImageryProvider:谷歌企业版
  • GoogleEarthEnterpriseMapsProvider
  • GridImageryProvider
  • IonImageryProvider
  • MapboxImageryProvider
  • MapboxStyleImageryProvider
  • SingleTileImageryProvider
  • TileCoordinatesImageryProvider
  • UrlTemplateImageryProvider:URL模板
  • WebMapServiceImageryProvider
  • WebMapTileServiceImageryProvider

# 示例代码

//加载ArcGIS卫星图
tdtLayer = new Cesium.ArcGisMapServerImageryProvider({
	url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
})
viewer.imageryLayers.addImageryProvider(tdtLayer)
//加载ArcGIS街道图
var tdtLayer = new Cesium.ArcGisMapServerImageryProvider({
	url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
})
viewer.imageryLayers.addImageryProvider(tdtLayer)
//加载ArcGIS蓝色图
var tdtLayer = new Cesium.UrlTemplateImageryProvider({
	url: "https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}"
})
viewer.imageryLayers.addImageryProvider(tdtLayer)

//高德矢量图
let tdtLayer = new Cesium.UrlTemplateImageryProvider({
	url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
	minimumLevel: 3,
	maximumLevel: 18
})
viewer.imageryLayers.addImageryProvider(tdtLayer)

//高德影像
let tdtLayer = new Cesium.UrlTemplateImageryProvider({
	url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
	minimumLevel: 3,
	maximumLevel: 18
})
viewer.imageryLayers.addImageryProvider(tdtLayer)

//高德路网中文注记
let tdtLayer = new Cesium.UrlTemplateImageryProvider({
	url: "http://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
	minimumLevel: 3,
	maximumLevel: 18
})
viewer.imageryLayers.addImageryProvider(tdtLayer)


//天地图矢量地图
this.viewer.imageryLayers.addImageryProvider(
	new Cesium.WebMapTileServiceImageryProvider({
		url: "http://{s}.tianditu.gov.cn/vec_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
			"&LAYER=vec&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
			"&style=default&format=tiles&tk=天地图的TOKEN",
		layer: "tdtCva",
		style: "default",
		format: "tiles",
		tileMatrixSetID: "c",
		subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
		tilingScheme: new Cesium.GeographicTilingScheme(),
		tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
		maximumLevel: 18
	})
)
//天地图影像地图
this.viewer.imageryLayers.addImageryProvider(
	new Cesium.WebMapTileServiceImageryProvider({
		url: "http://{s}.tianditu.gov.cn/img_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
			"&LAYER=img&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
			"&style=default&format=tiles&tk=天地图的TOKEN",
		layer: "tdtCva",
		style: "default",
		format: "tiles",
		tileMatrixSetID: "c",
		subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
		tilingScheme: new Cesium.GeographicTilingScheme(),
		tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
		maximumLevel: 18
	})
)
//天地图栅格地图
this.viewer.imageryLayers.addImageryProvider(
	new Cesium.WebMapTileServiceImageryProvider({
		url: "http://{s}.tianditu.gov.cn/ter_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
			"&LAYER=ter&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
			"&style=default&format=tiles&tk=天地图的TOKEN",
		layer: "tdtCva",
		style: "default",
		format: "tiles",
		tileMatrixSetID: "c",
		subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
		tilingScheme: new Cesium.GeographicTilingScheme(),
		tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
		maximumLevel: 18
	})
)
//天地图标记地图
this.viewer.imageryLayers.addImageryProvider(
	new Cesium.WebMapTileServiceImageryProvider({
		url: "http://{s}.tianditu.gov.cn/cia_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
			"&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
			"&style=default&format=tiles&tk=天地图的TOKEN",
		layer: "tdtCva",
		style: "default",
		format: "tiles",
		tileMatrixSetID: "c",
		subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
		tilingScheme: new Cesium.GeographicTilingScheme(),
		tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
		maximumLevel: 18
	})
)
		
//载Mapbox地图
loadMapboxVectorMap () {
  this.vectorMap = this.viewer.imageryLayers.addImageryProvider(
    new Cesium.MapboxStyleImageryProvider({
      url: 'https://api.mapbox.com/styles/v1',
      username: '注册的账号名称',
      styleId: '自定义地图时获取的styleId',
      accessToken: '自定义地图时获取的AccessToken',
	  scaleFactor:true
    })
  )
  this.vectorMap.title = 'mapboxVectorMap'
  this.vectorMap.show = true
}

# 加载地形

# 默认类型

terrainProvider:new EllipsoidTerrainProvider() 没有地形高程,主要是个椭球体

# 其他类型

ArcGISTiledElevationTerrainProvider
CustomHeightmapTerrainProvider
EllipsoidTerrainProvider
CesiumTerrainProvider
VRTheWorldTerrainProvider
GoogleEarthEnterpriseTerrainProvider

# 示例代码

var viewer = new Cesium.Viewer('cesiumContainer', {
    terrainProvider : new Cesium.CesiumTerrainProvider({
        url : Cesium.IonResource.fromAssetId(3956),
        requestVertexNormals : true
    })
});
//加载全球地形图
var viewer = new Cesium.Viewer('cesiumContainer', {
    terrainProvider : Cesium.createWorldTerrain({
        requestWaterMask : true, // 请求水波纹效果
        requestVertexNormals : true // 请求照明
    });
});

# 设置camera视角

# setView

//setView
view.camera.setView({
  destination : Cesium.Cartesian3.fromDegrees(116.435314,39.960521, 15000.0), // 设置位置
  orientation: {
    heading : Cesium.Math.toRadians(20.0), // 方向
    pitch : Cesium.Math.toRadians(-90.0),// 倾斜角度
    roll : 0
  }
});

//rectangle 方式
view.camera.setView({
	destination: Cesium.Rectangle.fromDegrees(0.0, 20.0, 10.0, 30.0),
	orientation: {
		heading : Cesium.Math.toRadians(20.0), // 方向
		pitch : Cesium.Math.toRadians(-90.0),// 倾斜角度
		roll : 0
	} 
});

# flyto

view.camera.flyTo({
  destination :Cesium.Cartesian3.fromDegrees(116.435314,39.960521, 15000.0), // 设置位置
  orientation: {
    heading :Cesium.Math.toRadians(20.0), // 方向
    pitch :Cesium.Math.toRadians(-90.0),// 倾斜角度
    roll :0
  },
  duration:5, // 设置飞行持续时间,默认会根据距离来计算
  complete:function () {
     // 到达位置后执行的回调函数
  },
  cancle:function () {
     // 如果取消飞行则会调用此函数
  },
  pitchAdjustHeight:-90, // 如果摄像机飞越高于该值,则调整俯仰俯仰的俯仰角度,并将地球保持在视口中。
  maximumHeight:5000, // 相机最大飞行高度
  flyOverLongitude:100, // 如果到达目的地有2种方式,设置具体值后会强制选择方向飞过这个经度(这个,很好用)
});

# lookAt

var center = Cesium.Cartesian3.fromDegrees(114.44455, 22.0444);//camera视野的中心点坐标
var heading = Cesium.Math.toRadians(50.0);
var pitch = Cesium.Math.toRadians(-20.0);
var range = 5000.0;
view.camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, range));

# 加载3DTiles

var tileset = new Cesium.Cesium3DTileset({
  url: 'Data/yudingshan/3dtiles/tileset.json',
  //classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
});
// tileset.style = new Cesium.Cesium3DTileStyle({
//   color: "rgba(255, 0, 0, 0.5)",
// });
tileset.readyPromise.then(function (tileset) {
	viewer.scene.primitives.add(tileset);
	viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(45, -0.5, tileset.boundingSphere.radius * 1.0));
	changeHeight(0);
}).otherwise(function (error) {
	console.log(error);
});
function changeHeight(height) {
	height = Number(height);
	if (isNaN(height)) {
		return;
	}
	var cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
	var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height);
	var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude,height);
	var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
	tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
}

# 加载KML

 // 加载kml数据,更改description信息,多了黄色的标签里面包含的
 var kmlOptions = {
	 camera : viewer.scene.camera,
	 canvas : viewer.scene.canvas,
	 clampToGround : true
 };
 // Load geocache points of interest from a KML file
 // Data from : http://catalog.opendata.city/dataset/pediacities-nyc-neighborhoods/resource/91778048-3c58-449c-a3f9-365ed203e914
 var geocachePromise = Cesium.KmlDataSource.load('/Assets/SampleData/sampleGeocacheLocations.kml', kmlOptions);

 // Add geocache billboard entities to scene and style them
 geocachePromise.then(function(dataSource) {
    // Add the new data as entities to the viewer
    viewer.dataSources.add(dataSource);
    // Get the array of entities
    var geocacheEntities = dataSource.entities.values;
	for (var i = 0; i < geocacheEntities.length; i++) {
		var entity = geocacheEntities[i];
		if (Cesium.defined(entity.billboard)) {
			// Adjust the vertical origin so pins sit on terrain
			entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
			// Disable the labels to reduce clutter
			entity.label = undefined;
			// Add distance display condition
			entity.billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20000.0);
			// Compute latitude and longitude in degrees
			var cartographicPosition = Cesium.Cartographic.fromCartesian(entity.position.getValue(Cesium.JulianDate.now()));
			var latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
			var longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);

			// Modify description(将信息添加到description描述信息中,我们这里只添加经纬度)
			var description = '<table class="cesium-infoBox-defaultTable cesium-infoBox-defaultTable-lighter"><tbody>' +
			'<tr><th>' + "Longitude" + '</th><td>' + longitude.toFixed(5) + '</td></tr>' +
			'<tr><th>' + "Latitude" + '</th><td>' + latitude.toFixed(5) + '</td></tr>' +
			'</tbody></table>';
			entity.description = description;
		}
	}
});

# 加载CZML

// path and drone
// Load a drone flight path from a CZML file
var dronePromise = Cesium.CzmlDataSource.load('./assets/SampleData/SampleFlight.czml');

// Save a new drone model entity
var drone;
dronePromise.then(function(dataSource) {
	viewer.dataSources.add(dataSource);
	// Get the entity using the id defined in the CZML data
	drone = dataSource.entities.getById('Aircraft/Aircraft1');
	// Attach a 3D model
	drone.model = {
		uri : './assets/SampleData/Models/CesiumDrone.gltf',
		minimumPixelSize : 128,
		maximumScale : 1000,
		silhouetteColor : Cesium.Color.WHITE,
		silhouetteSize : 2
	};
	// Add computed orientation based on sampled positions
	drone.orientation = new Cesium.VelocityOrientationProperty(drone.position);

	// Smooth path interpolation
	drone.position.setInterpolationOptions({
		interpolationAlgorithm : Cesium.HermitePolynomialApproximation,
		interpolationDegree : 2
	});
	drone.viewFrom = new Cesium.Cartesian3(0, -30, 30);

	viewer.trackedEntity = drone;/镜头跟随
});

# 加载服务

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Use correct character set. -->
    <meta charset="utf-8"/>
    <!-- Tell IE to use the latest, best version. -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
    <meta
            name="viewport"
            content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
    <title>Cesium 加载天地图示例</title>
    <script src="./Cesium.js"></script>
    <script src="http://www.openlayers.vip/examples/resources/jquery-3.5.1.min.js"></script>
    <style>
        @import url(./Widgets/widgets.css);

        html,
        body,
        #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
    <script>
        var _hmt = _hmt || [];
        (function () {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?f80a36f14f8a73bb0f82e0fdbcee3058";
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(hm, s);
        })();
    </script>
</head>
<body>
<button id="wmts" onClick="WMTS()">添加标准 WMTS 图层</button>
<button id="restWMTS" onClick="restWMTS()">添加 Geoserver Rest WMTS 图层</button>

<br/>
<br/>
<button id="wms" onClick="WMS()">添加 Geoserver WMS 图层</button>
<button id="wfs" onClick="WFS()">添加 Geoserver WFS 图层</button>
<button id="kml" onClick="KML()">添加 Geoserver KML 图层</button>
<div id="cesiumContainer"></div>
<script>
    // 这个 tk 只能在本域名下使用
    var token = '2b7cbf61123cbe4e9ec6267a87e7442f';
    // 服务域名
    var tdtUrl = 'https://t{s}.tianditu.gov.cn/';
    // 服务负载子域
    var subdomains = ['0', '1', '2', '3', '4', '5', '6', '7'];
    var viewer = new Cesium.Viewer('cesiumContainer', {
        shouldAnimate: true,
        selectionIndicator: true,
        animation: false,       //动画
        homeButton: false,       //home键
        geocoder: false,         //地址编码
        baseLayerPicker: false, //图层选择控件
        timeline: false,        //时间轴
        fullscreenButton: false, //全屏显示
        infoBox: false,         //点击要素之后浮窗
        sceneModePicker: false,  //投影方式  三维/二维
        navigationInstructionsInitiallyVisible: false, //导航指令
        navigationHelpButton: false,     //帮助信息
        selectionIndicator: false, // 选择
        imageryProvider: new window.Cesium.WebMapTileServiceImageryProvider({
            //影像底图
            url: "http://t{s}.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" + token,
            subdomains: subdomains,
            layer: "tdtImgLayer",
            style: "default",
            format: "image/jpeg",
            tileMatrixSetID: "GoogleMapsCompatible",//使用谷歌的瓦片切片方式
            show: true
        })
    });

    viewer.imageryLayers.addImageryProvider(new window.Cesium.WebMapTileServiceImageryProvider({
        //影像注记
        url: "http://t{s}.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg&tk=" + token,
        subdomains: subdomains,
        layer: "tdtCiaLayer",
        style: "default",
        format: "image/jpeg",
        tileMatrixSetID: "GoogleMapsCompatible",
        show: true
    }));

    // // 叠加国界服务
    var iboMap = new window.Cesium.UrlTemplateImageryProvider({
        url: tdtUrl + 'DataServer?T=ibo_w&x={x}&y={y}&l={z}&tk=' + token,
        subdomains: subdomains,
        tilingScheme: new window.Cesium.WebMercatorTilingScheme(),
        maximumLevel: 10
    });

    viewer.imageryLayers.addImageryProvider(iboMap);

    // geoserver叠加参数,EPSG码+图层等级
    const TileMatrixLabels = ['EPSG:900913:0', 'EPSG:900913:1', 'EPSG:900913:2',
        'EPSG:900913:3', 'EPSG:900913:4', 'EPSG:900913:5', 'EPSG:900913:6',
        'EPSG:900913:7', 'EPSG:900913:8', 'EPSG:900913:9', 'EPSG:900913:10',
        'EPSG:900913:11', 'EPSG:900913:12', 'EPSG:900913:13', 'EPSG:900913:14',
        'EPSG:900913:15', 'EPSG:900913:16', 'EPSG:900913:17', 'EPSG:900913:18'];

    // WMTS图层对象
    let layerWMTS;

    // 标准 WMTS 方式叠加
    function WMTS(layerName) {

        // cesium加载
        // 注意:所有参数必填
        layerWMTS = layerWMTS || viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
            url: 'http://openlayers.vip/geoserver/gwc/service/wmts',
            layer: layerName || 'cite:xintai18',
            // 强调一下,参数必填,很多博客忽略,导致不能加载成功!
            style: 'raster',
            format: 'image/png',
            tileMatrixSetID: 'EPSG:900913',
            tileMatrixLabels: TileMatrixLabels
        }));

        // 调节图层显隐
        layerWMTSRest && (layerWMTSRest.show = false);
        layerWMTS && (layerWMTS.show = true);

        // 定位
        flyToRectangle();
    }

    // WMTSRest图层
    let layerWMTSRest;

    // Rest 方式叠加图层
    function restWMTS() {

        //cesium加载
        layerWMTSRest = layerWMTSRest || viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
            // 注意:gwc/rest/wmts 很多博客这里提供的是错的
            // 注意:{TileMatrixSet}/{TileMatrixSet}:{TileMatrix} 中间有冒号
            url: 'http://openlayers.vip/geoserver/gwc/rest/wmts/cite:xintai18/{style}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png',
            // 注意:这里的样式参数必须有
            style: 'raster',
            // 图层不传也可以
            layer: 'cite:xintai18',
            // 必填
            format: "image/png",
            // 选填
            maximumLevel: 21,
            // 必填
            tileMatrixSetID: 'EPSG:900913'
        }));

        layerWMTSRest && (layerWMTSRest.show = true);
        layerWMTS && (layerWMTS.show = false);

        flyToRectangle();
    }

    let layerWMS;

    // WMS 图层
    function WMS() {

        // 添加geoserver发布的wms数据
        layerWMS = layerWMS || viewer.imageryLayers.addImageryProvider(new Cesium.WebMapServiceImageryProvider({
            url: "http://openlayers.vip/geoserver/cite/wms",
            // 必填
            layers: "cite:xintai18",
            parameters: {
                transparent: true,
                format: "image/png",
                srs: "EPSG:4326",
                // 非必填
                styles: "",
            },
        }));

        layerWMS && (layerWMS.show = true);
        layerWMTS && (layerWMTS.show = false);
        layerWMTSRest && (layerWMTSRest.show = false);

        flyToRectangle();
    }

    // WFS 图层
    // 这里演示北京区县数据
    function WFS() {

        // 这里使用 cesium加载也可以
        // 因为本身数据为 4490,cesium不识别,因此使用 ajax 请求之后再渲染
        $.ajax({
            //点  坐标:曼哈顿
            url: "http://openlayers.vip/geoserver/cite/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=cite%3At_county_new&maxFeatures=50&outputFormat=application%2Fjson",
            cache: false,
            async: true,
            success: function (data) {
                // 原数据是 4490,这里替换为4326,否则 cesium 不识别4490,会报错
                data.crs.properties.name = data.crs.properties.name.replace("EPSG::4490", "EPSG::4326")
                var datasource = Cesium.GeoJsonDataSource.load(data);
                viewer.dataSources.add(datasource);
            },
            error: function (data) {
                alert("error");
            }
        });

        layerWMS && (layerWMS.show = false);
        layerWMTS && (layerWMTS.show = false);
        layerWMTSRest && (layerWMTSRest.show = false);

        // 定位到北京
        flyToRectangle([
            Cesium.Cartesian3.fromDegrees(
                115.44302181271966,
                39.251141782491445,
                0
            ),
            Cesium.Cartesian3.fromDegrees(
                117.34365657834466,
                40.997967954366445,
                0
            ),
        ]);
    }

    // KML 图层
    // 这里演示北京区县数据
    function KML() {

        var options = {
            camera : viewer.scene.camera,
            canvas : viewer.scene.canvas,
            clampToGround: true //开启贴地
        };
        viewer.dataSources.add(Cesium.KmlDataSource.load('http://openlayers.vip/geoserver/cite/ows?' +
            'service=WFS&version=1.0.0&request=GetFeature&typeName=cite%3At_county_new&maxFeatures=50&' +
            'outputFormat=application%2Fvnd.google-earth.kml%2Bxml', options)).then(function(dataSource){

            // Get the array of entities
            var neighborhoodEntities = dataSource.entities.values;
            for (var i = 0; i < neighborhoodEntities.length; i++) {
                var entity = neighborhoodEntities[i];

                if (Cesium.defined(entity.polygon)) {

                    // 设置颜色
                    entity.polygon.material = Cesium.Color.fromRandom({
                        red : 0.1,
                        maximumGreen : 0.5,
                        minimumBlue : 0.5,
                        alpha : 0.6
                    });
                }
            }

            // 定位到北京
            flyToRectangle([
                Cesium.Cartesian3.fromDegrees(
                    115.44302181271966,
                    39.251141782491445,
                    0
                ),
                Cesium.Cartesian3.fromDegrees(
                    117.34365657834466,
                    40.997967954366445,
                    0
                ),
            ]);
        });

        layerWMS && (layerWMS.show = false);
        layerWMTS && (layerWMTS.show = false);
        layerWMTSRest && (layerWMTSRest.show = false);

        // 定位到北京
        flyToRectangle([
            Cesium.Cartesian3.fromDegrees(
                115.44302181271966,
                39.251141782491445,
                0
            ),
            Cesium.Cartesian3.fromDegrees(
                117.34365657834466,
                40.997967954366445,
                0
            ),
        ]);
    }

    /**
     * @description: 飞行定位到一个矩形
     * @return {*}
     */
    function flyToRectangle(RectangleCD) {

        // 添加定位信息
        RectangleCD = RectangleCD || [
            Cesium.Cartesian3.fromDegrees(
                104.15528644354428,
                30.752166584535513,
                0
            ),
            Cesium.Cartesian3.fromDegrees(
                104.27206271917905,
                30.827572468324576,
                0
            ),
        ];

        var rec = Cesium.Rectangle.fromCartesianArray(RectangleCD);
        var boundingSphere = Cesium.BoundingSphere.fromRectangle3D(rec);
        viewer.camera.flyToBoundingSphere(boundingSphere, {
            duration: 5,
            complete: function () {
            },
            offset: {
                heading: Cesium.Math.toRadians(0.0),
                pitch: Cesium.Math.toRadians(-90),
                range: 0.0,
            },
        });
    }

</script>
</body>
</html>

# 点击请求WMTS GetFeatureInfo接口获取点选的要素信息

# 计算近似层级

Cesium 3D模式下没有层级概念,二维地图才有层级zoom,可以通过相机的高度计算近似层级

heightToZoom(height) {
  let A = 40487.57;
  let B = 0.00007096758;
  let C = 91610.74;
  let D = -40467.74;
  return Math.round(D + (A - D) / (1 + Math.pow(height / C, B)));
}

# 计算点击位置瓦片行列号和像素

分辨率resolution:当前地图范围内,1像素代表实际距离多少米

切片原点在左上角,根据下图可以得到公式:

计算点击位置瓦片行列号和像素

反推,根据鼠标点击坐标转换成web墨卡托[x,y],可计算出tileCol、tileRow、tileI和tileJ

计算点击位置瓦片行列号和像素

# 构建URL发送请求

const _this = this;
var handler3D = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler3D.setInputAction(function (event) {
  // 获取鼠标点击位置的墨卡托坐标
  // 屏幕坐标转世界坐标
  let ellipsoid = viewer.scene.globe.ellipsoid;
  let cartesian3 = viewer.camera.pickEllipsoid(event.position, ellipsoid);
  //笛卡尔坐标转web墨卡托
  let webMercator = _this.cartesianToWebMercator(cartesian3);
  //获取相机高度
  let height = viewer.camera.positionCartographic.height;
  //计算近似层级
  let zoom = _this.heightToZoom(height);  
  let matrixIds = [
    "EPSG:900913:0",
    "EPSG:900913:1",
    "EPSG:900913:2",
    "EPSG:900913:3",
    "EPSG:900913:4",
    "EPSG:900913:5",
    "EPSG:900913:6",
    "EPSG:900913:7",
    "EPSG:900913:8",
    "EPSG:900913:9",
    "EPSG:900913:10",
    "EPSG:900913:11",
    "EPSG:900913:12",
    "EPSG:900913:13",
    "EPSG:900913:14",
    "EPSG:900913:15",
    "EPSG:900913:16",
    "EPSG:900913:17",
    "EPSG:900913:18",
    "EPSG:900913:19",
    "EPSG:900913:20",
    "EPSG:900913:21",
    "EPSG:900913:22",
    "EPSG:900913:23",
    "EPSG:900913:24",
    "EPSG:900913:25",
    "EPSG:900913:26",
    "EPSG:900913:27",
    "EPSG:900913:28",
    "EPSG:900913:29",
    "EPSG:900913:30",
  ];
  let resolutions = [
    156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125,
    9783.939619140625, 4891.9698095703125, 2445.9849047851562,
    1222.9924523925781, 611.4962261962891, 305.74811309814453,
    152.87405654907226, 76.43702827453613, 38.218514137268066,
    19.109257068634033, 9.554628534317017, 4.777314267158508,
    2.388657133579254, 1.194328566789627, 0.5971642833948135,
    0.2985821416974068, 0.1492910708487034, 0.0746455354243517,
    0.0373227677121758, 0.0186613838560879, 0.009330691928044,
    0.004665345964022, 0.002332672982011, 0.0011663364910055,
    0.0005831682455027, 0.0002915841227514, 0.0001457920613757,
  ];
  let origin = [-2.003750834e7, 2.0037508e7];
  let tileSize = [256, 256];
  let fx = (webMercator[0] - origin[0]) / (resolutions[zoom] * tileSize[0]);
  let fy = (origin[1] - webMercator[1]) / (resolutions[zoom] * tileSize[1]);
  let tileCol = Math.floor(fx);
  let tileRow = Math.floor(fy);
  let tileI = Math.floor((fx - tileCol) * tileSize[0]);
  let tileJ = Math.floor((fy - tileRow) * tileSize[1]);
  let matrixId = matrixIds[zoom];
  let url = "http://localhost:8090/geoserver/gwc/service/wmts?layer=Cesium:mianpian1984&style=&Request=GetFeatureInfo&Version=1.0.0&tilematrixset=EPSG:900913&InfoFormat=application/json&";
  url = url + "TILEMATRIX=" + matrixId + "&TileCol=" + tileCol + "&TileRow=" + tileRow + "&I=" + tileI + "&J=" + tileJ;
  //发送请求获取要素信息
  _this.GetFeatureInfo(url)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

变量matrixIds、resolutions、origin和tileSize都是影像发布后自动生成,可在如图所示位置中找到 构建URL发送请求

构建URL发送请求

构建URL发送请求

methods中定义方法

//笛卡尔坐标转web墨卡托
cartesianToWebMercator(cartesian) {
  // 将笛卡尔坐标转换为地理坐标(弧度)
  let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  //弧度转经纬度
  cartographic.latitude = Cesium.Math.toDegrees(cartographic.latitude);
  cartographic.longitude = Cesium.Math.toDegrees(cartographic.longitude);
  //度转墨卡托
  let earthRad = 6378137.0;
  let x = ((cartographic.longitude * Math.PI) / 180) * earthRad;
  let a = (cartographic.latitude * Math.PI) / 180;
  let y = (earthRad / 2) * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
  return [x, y];
},
//调用WMTS服务GetFeatureInfo接口
wmtsGetFeatureInfo(url) {
  return http({
    methods: "GET",
    url: url,
  });
}
//发送请求获取要素信息
async GetFeatureInfo(url){
  const res = await wmtsGetFeatureInfo(url);
  if (res.status === 200){
    console.log(res.data);
  }
}

# 加载不同JSON

# topojson

var viewer = new Cesium.Viewer('cesiumContainer');
var promise= viewer.dataSources.add(Cesium.GeoJsonDataSource.load('../china.topojson', {
	stroke: Cesium.Color.BLACK,
	fill: Cesium.Color.RED,
	strokeWidth: 3,
	markerSymbol: '?'
}));
viewer.flyTo(promise);

# GeoJson

var viewer = new Cesium.Viewer('cesiumContainer');
 //Seed the random number generator for repeatable results.
Cesium.Math.setRandomNumberSeed(0);
var promise=Cesium.GeoJsonDataSource.load('../../Apps/testone.json');
promise.then(function(dataSource) {
	viewer.dataSources.add(dataSource);
	var entities = dataSource.entities.values;
	var colorHash = {};
	for (var i = 0; i < entities.length; i++) {
		var entity = entities[i];
		var name = entity.name;
		var color = colorHash[name];
		if (!color) {
			color = Cesium.Color.fromRandom({
				alpha : 1.0
			});
			colorHash[name] = color;
		}
		entity.polygon.material = color;
		entity.polygon.outline = false;            
		entity.polygon.extrudedHeight =5000.0;
		
		//添加label需要先设置中心点
		entity.position = Cesium.Cartesian3.fromDegrees(x,y,z);
		entity.label={
			text:entity.name,
			color : Cesium.Color.fromCssColorString('#fff'),
			font:'normal 32px MicroSoft YaHei',
			showBackground : true,
			scale : 0.5,
			horizontalOrigin : Cesium.HorizontalOrigin.LEFT_CLICK,
			verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
			disableDepthTestDistance : 100000.0
		};
	}
});
viewer.flyTo(promise);

# 普通json

var viewer = new Cesium.Viewer('cesiumContainer');
    Cesium.Math.setRandomNumberSeed(0);
    Cesium.loadJson('/../Apps/Points.json').then(function(jsonData) {
      for (var i =0 ;i<=jsonData.features.length-10; i++) {
        var ifeature=jsonData.features[i]; 
        for (var k = 0;k<ifeature.geometry.paths[0].length-10; k++) {
            if (ifeature.geometry.paths[0][k].length==2) {
				viewer.entities.add({
					position:Cesium.Cartesian3.fromDegrees(ifeature.geometry.paths[0][k][0],
														 ifeature.geometry.paths[0][k][1]),
					point:{
						pixelSize : 10,
						color :Cesium.Color.YELLOW
					 }
				});
            }           
        }
      }           
   }).otherwise(function(error) {});

# 等高线/坡向/坡度

//设置viewer开启光照
viewer.scene.globe.enableLighting = true;

//定义等高线初始值
var minHeight = -414.0;
var maxHeight = 8777.0;
var contourColor = Cesium.Color.RED.clone();
var contourUniforms = {};
var shadingUniforms = {};

//定义相关处理函数
function getElevationContourMaterial() {
  // Creates a composite material with both elevation shading and contour lines
  return new Cesium.Material({
  fabric: {
	type: "ElevationColorContour",
	materials: {
	contourMaterial: {
	  type: "ElevationContour",
	},
	elevationRampMaterial: {
	  type: "ElevationRamp",
	},
	},
	components: {
	diffuse:
	  "contourMaterial.alpha == 0.0?elevationRampMaterial.diffuse:contourMaterial.diffuse",
	alpha:
	  "max(contourMaterial.alpha, elevationRampMaterial.alpha)",
	},
  },
  translucent: false,
  });
}
 
var elevationRamp = [0.0, 0.045, 0.1, 0.15, 0.37, 0.54, 1.0];
var slopeRamp = [0.0, 0.29, 0.5, Math.sqrt(2) / 2, 0.87, 0.91, 1.0];
var aspectRamp = [0.0, 0.2, 0.4, 0.6, 0.8, 0.9, 1.0];
 
function getColorRamp(selectedShading) {
  var ramp = document.createElement("canvas");
  ramp.width = 100;
  ramp.height = 1;
  var ctx = ramp.getContext("2d");

  var values;
  if (selectedShading === "elevation") {
  values = elevationRamp;
  } else if (selectedShading === "slope") {
  values = slopeRamp;
  } else if (selectedShading === "aspect") {
  values = aspectRamp;
  }

  var grd = ctx.createLinearGradient(0, 0, 100, 0);
  grd.addColorStop(values[0], "#000000"); //black
  grd.addColorStop(values[1], "#2747E0"); //blue
  grd.addColorStop(values[2], "#D33B7D"); //pink
  grd.addColorStop(values[3], "#D33038"); //red
  grd.addColorStop(values[4], "#FF9742"); //orange
  grd.addColorStop(values[5], "#ffd700"); //yellow
  grd.addColorStop(values[6], "#ffffff"); //white

  ctx.fillStyle = grd;
  ctx.fillRect(0, 0, 100, 1);

  return ramp;
}

// The viewModel tracks the state of our mini application.
var viewModel = {
  enableContour: false,
  contourSpacing: 50.0,
  contourWidth: 2.0,
  selectedShading: "none",
  changeColor: function () {
  contourUniforms.color = Cesium.Color.fromRandom(
	{ alpha: 1.0 },
	contourColor
  );
  },
};
 
// Convert the viewModel members into knockout observables.
Cesium.knockout.track(viewModel);

// Bind the viewModel to the DOM elements of the UI that call for it.
var toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);
 
function updateMaterial() {
    var hasContour = viewModel.enableContour;
    var selectedShading = viewModel.selectedShading;
    var globe = viewer.scene.globe;
    var material;
    if (hasContour) {
      if (selectedShading === "elevation") {
        material = getElevationContourMaterial();
        shadingUniforms = material.materials.elevationRampMaterial.uniforms;
        shadingUniforms.minimumHeight = minHeight;
        shadingUniforms.maximumHeight = maxHeight;
        contourUniforms = material.materials.contourMaterial.uniforms;
      }else {
        material = Cesium.Material.fromType("ElevationContour");
        contourUniforms = material.uniforms;
      }
      contourUniforms.width = viewModel.contourWidth;
      contourUniforms.spacing = viewModel.contourSpacing;
      contourUniforms.color = contourColor;
    } else if (selectedShading === "elevation") {
      material = Cesium.Material.fromType("ElevationRamp");
      shadingUniforms = material.uniforms;
      shadingUniforms.minimumHeight = minHeight;
      shadingUniforms.maximumHeight = maxHeight;
    } 
    if (selectedShading !== "none") {
      shadingUniforms.image = getColorRamp(selectedShading);
    }
    globe.material = material;
}
 
updateMaterial();
 
Cesium.knockout.getObservable(viewModel, "enableContour").subscribe(function (newValue) {
  updateMaterial();
});

Cesium.knockout.getObservable(viewModel, "contourWidth").subscribe(function (newValue) {
  contourUniforms.width = parseFloat(newValue);
});

Cesium.knockout.getObservable(viewModel, "contourSpacing").subscribe(function (newValue) {
  contourUniforms.spacing = parseFloat(newValue);
});

Cesium.knockout.getObservable(viewModel, "selectedShading").subscribe(function (value) {
  updateMaterial();
});

绑定页面控件

<div class="demo-container">
<label><input type="radio" name="shadingMaterials"
  value="none" data-bind="checked: selectedShading">无渲染</label> <label><input
  type="radio" name="shadingMaterials" value="elevation"
  data-bind="checked: selectedShading">高程渲染</label>
</div>
<div class="demo-container">
<div>
  <label><input type="checkbox"
	data-bind="checked: enableContour">等高线</label>
</div>
<div>
  高程 <input style="width: 136px; float: left; width: 100px;"
	type="range" min="1.0" max="500.0" step="1.0"
	data-bind="value: contourSpacing, valueUpdate: 'input', enable: enableContour">
  <span data-bind="text: contourSpacing"></span>m
</div>
<div>
  线宽 <input style="width: 125px; float: left; width: 100px;"
	type="range" min="1.0" max="10.0" step="1.0"
	data-bind="value: contourWidth, valueUpdate: 'input', enable: enableContour">
  <span data-bind="text: contourWidth"></span>px
</div>
<div>
  <button type="button"
	data-bind="click: changeColor, enable: enableContour">颜色</button>
</div>
</div>

等高线/坡向/坡度

# 缓冲区分析

//初始化点缓冲
initPointBuffer() {
	let point = [106.422638966289, 29.5698367125623];
	this.addPoint(point);

	let pointF = turf.point(point);
	let buffered = turf.buffer(pointF, 60, { units: 'meters' });
	let coordinates = buffered.geometry.coordinates;
	let points = coordinates[0];
	let degreesArray = this.pointsToDegreesArray(points);
	this.addBufferPolyogn(Cesium.Cartesian3.fromDegreesArray(degreesArray));
},

//添加点
addPoint(point) {
	this.viewer.entities.add({
		position: Cesium.Cartesian3.fromDegrees(point[0], point[1], 0),
		point: {
			pixelSize: 10,
			color: Cesium.Color.YELLOW,
			outlineWidth: 3,
			outlineColor: Cesium.Color.YELLOW.withAlpha(0.4),
		}
	});
},

//初始化线缓冲
initPolylineBuffer() {
	let points = [
		[106.425203158107, 29.5694914480581],
		[106.428808047023, 29.569230166027],
		[106.431661917416, 29.5692674920729],
		[106.434708906857, 29.5693048181049]
	];
	let degreesArray = this.pointsToDegreesArray(points);
	this.addPolyline(Cesium.Cartesian3.fromDegreesArray(degreesArray));

	let polylineF = turf.lineString(points);
	let buffered = turf.buffer(polylineF, 30, { units: 'meters' });
	let coordinates = buffered.geometry.coordinates;
	points = coordinates[0];
	degreesArray = this.pointsToDegreesArray(points);
	this.addBufferPolyogn(Cesium.Cartesian3.fromDegreesArray(degreesArray));
},

//添加线
addPolyline(positions) {
	this.viewer.entities.add({
		polyline: {
			positions: positions,
			width: 2,
			material: Cesium.Color.YELLOW,
		}
	})
},

//初始化面缓冲
initPolygonBuffer() {
	let points = [
		[106.438549830166, 29.5701073244566],
		[106.440695597377, 29.5701073244566],
		[106.440738512722, 29.5688755679036],
		[106.438700033871, 29.5687262630581],
		[106.438034846035, 29.5690248725284],
		[106.438549830166, 29.5701073244566]
	];

	let degreesArray = this.pointsToDegreesArray(points);
	this.addPolygon(Cesium.Cartesian3.fromDegreesArray(degreesArray));

	let polygonF = turf.polygon([points]);
	let buffered = turf.buffer(polygonF, 60, { units: 'meters' });
	let coordinates = buffered.geometry.coordinates;
	points = coordinates[0];
	degreesArray = this.pointsToDegreesArray(points);
	this.addBufferPolyogn(Cesium.Cartesian3.fromDegreesArray(degreesArray));
},

//添加面
addPolygon(positions) {
	this.viewer.entities.add({
		polygon: {
			hierarchy: new Cesium.PolygonHierarchy(positions),
			material: Cesium.Color.YELLOW.withAlpha(0.6),
			classificationType: Cesium.ClassificationType.BOTH
		},
		polyline: {
			positions: positions,
			width: 2,
			material: Cesium.Color.YELLOW.withAlpha(0.4),
		}
	});
},

//添加缓冲面
addBufferPolyogn(positions) {
	this.viewer.entities.add({
		polygon: {
			hierarchy: new Cesium.PolygonHierarchy(positions),
			material: Cesium.Color.RED.withAlpha(0.6),
			classificationType: Cesium.ClassificationType.BOTH
		},
	});
},

//格式转换
pointsToDegreesArray(points) {
	let degreesArray = [];
	points.map(item => {
		degreesArray.push(item[0]);
		degreesArray.push(item[1]);
	});
	return degreesArray;
},

缓冲区分析

# ArcGIS服务进行属性查询

在发起查询请求的时候,建议都用 post 请求,因为如果直接用 get 请求,url 的长度是有限制的,而且直接用 get 请求相对来说也不是很安全,因此建议直接都用 post 请求

//思路很简单,先获取服务的extent,再根据这个txtent去进行identify获取识别到的多图层要素
const item.url = 'xxx/xxx/xxx/MapServer'
const getExtenUrl = `${item.url}/info/iteminfo?f=pjson`;
fetch(getExtenUrl)
.then((response) => response.json())
.then((res) => {
  const extent = res.extent[0][0] + ',' + res.extent[0][1] + ','
                 + res.extent[1][0] + ',' + res.extent[1][1];
  const requestUrl = `${item.url}/identify`;
  const params = {
	f: 'pjson',
	tolerance: 2,
	returnGeometry: false,
	geometry: JSON.stringify(
	  {
	  x: degreePosition.longitude,
	  y: degreePosition.latitude,
	  spatialReference: {
		wkid: 4326,
	  },
	}
	),
	geometryType: 'esriGeometryPoint',
	mapExtent: extent,
	imageDisplay: '1920,1080,96',
	sr: 4326,
	layers: 'all',
  };
  // post数据以表单形式存储
  const formData = new FormData();
  Object.keys(params).forEach((key) => {
	formData.append(key, params[key]);
  });

  fetch(requestUrl, {
	method: 'POST',
	body: formData,
  })
  .then((response) => response.json())
  .then((res) => {
	console.log('res1111111111', res);
	if (res.results.length > 0) {
	  const featureList = res.results.map((item) => {
		const feature = {
		  layerName: item.layerName,
		  attributes: item.attributes,
		};
		return feature;
	  });
	  console.log('featureList', featureList);
	  // 这里可以根据自己的需求进行处理
	  // 例如:弹出一个弹窗,展示查询到的要素信息
	}
  });
})

# 解决图层有遮挡覆盖问题

  • cesium叠加多个图层时 图层有遮挡覆盖
//设置layerIndex,addImageryProvider的第2个参数
viewer.imageryLayers.addImageryProvider(mapboxlayer,0);
  • geojson的面数据遮挡
//设置polygon拉伸高度
const highLightEntity = new Cesium.Entity({
  id: 'highLightEntity',
  polygon: {
	heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
	material: Cesium.Color.RED,
	extrudedHeight: 0
  }
})