一、简介
在Cesium中坐标的拾取非常重要,比如在标绘、测量等功能中需要获取地球表面、地形、三维模型、实体元素等物体表面的经纬度和高程,这样绘制的图形才能准确,拿到的坐标才可以使用。
与拾取相关的API比较多,不同的API适用的场景不同。比较常用的有scene.pickPosition、scene.pickPositionSupported、scene.pickTranslucentDepth、scene.pick、scene.drillPick、scene.camera.pickEllipsoid、scene.camera.getPickRay、scene.globe.pick等。另外还有一些私有类或者方法有时候也会用到,包括scene.picking、scene.pickFromRay、scene.drillPickFromRay等。
下面根据我自己在产品封装和项目中实际经验先大致说一下。【不涉及原理细节,拾取原理请等待第2篇文章】
1) scene.pick [拾取到实体对象]
该API签名为pick(windowPosition, width, height) → Object
返回一个具有“primitive”属性的对象,该对象包含特定窗口坐标处场景中的第一个(顶部)基元,如果该位置没有任何内容,则返回未定义的对象。可以根据基元的类型潜在地设置其他属性,并且可以用于进一步识别拾取的对象。
拾取3dtiles后,拾取将返回Cesium3DTileFeature对象。
2)scene.drillPick [拾取到实体对象]
该API签名为drillPick(windowPosition, limit, width, height) → Array.<*>
穿透拾取到多个对象。
3)scene.globe.pick[拾取到坐标]
该API签名为pick(ray, scene, result) → Cartesian3|undefined
返回射线和地球Globe交点的坐标,Globe包括地形图层、影像图层等,因此拾取到与地形交点的坐标。
4)scene.pickPosition[拾取到坐标]
该API签名为pickPosition(windowPosition, result) → Cartesian3
返回从深度缓冲区和窗口位置重建的笛卡尔位置。
5)scene.camera.pickEllipsoid[拾取到坐标]
该API签名为pickEllipsoid(windowPosition, ellipsoid, result) → Cartesian3|undefined
拾取椭球体表面的坐标。
通常拾取坐标的时候要做一些判断,先用scene.pick判断拾取到的对象类型,如果是三维模型(gltf、3dtiles等)或者Entity、primitive等,则调用scene.pickPosition;然后再判断是否加载了地形图层,如果有地形(不是默认地形),则调用scene.globe.pick,否则调用scene.camera.pickEllipsoid。
也可以调用私有方法scene.drillPickFromRay,这个也是准确的,可以穿透拾取,可以排除掉不想拾取的对象,该API有一些特殊的优势,后续再介绍。
另外在做绘制功能的时候有时候还需要直接采用拾取到的点对象的坐标作为拾取到的最终坐标。
在调用scene.drillpick再调用scene.pickPosition会导致坐标不准,参考资料:Result of `pickPosition` changes after call to `drillPick` - CesiumJS - Cesium Community
其他与拾取相关的博文请参考Cesium的拾取问题总结 - 知乎,Cesium——PICK不同使用场景_cesium pick-CSDN博客
其他参考资料Graphics Tech in Cesium - Renderer Architecture – Cesium
How to get the object (ID) of every pixel? - CesiumJS - Cesium Community
https://github.com/CesiumGS/cesium/blob/1.95/Source/Scene/Scene.js#L154
二、效果
三、代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标绘</title>
<script src="./js/config.js"></script>
<script src="./scripts/vue.min.js"></script>
<script type="text/javascript" src="./scripts/element.index.js"></script>
<link rel="stylesheet" href="./scripts/element.index.css">
<link rel="stylesheet" href="./css/common.css">
<script type="text/javascript" src="../anov-gis-sdk/index.js"></script>
<link rel="stylesheet" href="../anov-gis-sdk/index.css">
<style>
.draw-panel {
height: 70px;
line-height: 70px;
padding-left: 20px;
padding-right: 20px;
}
</style>
</head>
<body>
<div id="global">
</div>
<script>
let g_polylineMeasurement;
let g_pointMeasurement;
let g_polygonMeasurement;
let g_rectMeasurement;
let g_CirgleMeasurement;
let g_triangleMeasurement;
new Vue({
el: '#global',
template: `
<div id="cesiumContainer">
<div class="draw-click draw-panel">
<el-row>
<el-button type="primary" @click="pointMeasurement">点</el-button>
<el-button type="primary" @click="distanceMeasurement">折线</el-button>
<el-button type="primary" @click="areaMeasurement">多边形</el-button>
<el-button type="primary" @click="areaMeasurement_rect">矩形</el-button>
<el-button type="primary " @click="areaMeasurement_circle">圆</el-button>
<el-button type="primary " @click="addTerrain">地形</el-button>
<el-checkbox style="display:inline-block" @change="depthTest">深度测试</el-checkbox>
<el-button type="danger" @click="clearMeasurementResults">清除</el-button>
</el-row>
</div>
</div>`,
data() {
return {
tool: "",
inited: false
};
},
mounted() {
this.init3D();
this.initMeasure();
},
methods: {
init3D() {
window.viewer = new ANOVGIS.Viewer("cesiumContainer", {
contextOptions: {
id: "cesiumCanvas",
webgl: {
preserveDrawingBuffer: true,
},
showRenderLoopErrors: false,
},
geocoderType: ANOVGIS.GeocoderType.TIANDITU,
vrButton: false,
baseLayerPicker: true,
fullscreenButton: true,
homeButton: true,
sceneModePicker: true,
navigationHelpButton: true,
copyRight: false,
showMapInfo: true,
drillPick: false
});
//viewer.scene.debugShowFramesPerSecond = true;
const baselayer = ANOVGIS.ImageryLayerFactory.createImageryLayer(
ANOVGIS.ImageryType.TDT,
{
style: "img",
key: globalConfig.tdtKey
}
);
viewer.imageryLayers.addImageryProvider(baselayer);
this.initMouseEvent();
this.add3dtiles();
this.addBoxGraphic();
this.addModelGraphic();
},
initMouseEvent() {
viewer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
console.log(e);
})
},
add3dtiles() {
let layer = new ANOVGIS.TilesetLayer();
viewer.addLayer(layer);
let tileset = new ANOVGIS.Tileset({
url: 'http://localhost/gis-share/shanghaiafter/tileset.json'
})
// tileset.clampToGround();
layer.addGraphic(tileset);
},
addBoxGraphic() {
const redBox = viewer.entities.add({
name: "Red box with black outline",
position: Cesium.Cartesian3.fromDegrees(121.44, 31.1434, 500.0),
box: {
dimensions: new Cesium.Cartesian3(400.0, 300.0, 500.0),
material: Cesium.Color.RED.withAlpha(0.8),
outline: true,
outlineColor: Cesium.Color.BLACK,
},
});
},
addModelGraphic() {
viewer.entities.add({
name: 'modeltest',
position: Cesium.Cartesian3.fromDegrees(121.64, 31.6434, 0.0),
model: {
scale: 1000,
uri: 'http://localhost/gis-share/other/CesiumMilkTruck.glb',
},
});
},
addTerrain() {
if (window.viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
const CesiumTerrian = Cesium.createWorldTerrain();
viewer.terrainProvider = CesiumTerrian;
} else {
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
}
},
depthTest(t) {
viewer.scene.globe.depthTestAgainstTerrain = t;
},
initMeasure() {
if (!this.inited) {
this.inited = true;
}
this.clear();
g_pointMeasurement = new ANOVGIS.DrawPoint(viewer, {
justDraw: false,
pointSize: 12,
pointColor: Cesium.Color.YELLOW,
tempPoint_show: true,
showToolTip: false,
tempPoint_color: Cesium.Color.YELLOW,
onCompleted(positions) {
console.log(positions);
}
});
g_polylineMeasurement = new ANOVGIS.DrawPolyLine(viewer, {
lineColor: Cesium.Color.YELLOW,
justDraw: false,
onCompleted(positions) {
console.log(positions);
}
});
g_polygonMeasurement = new ANOVGIS.DrawPolygon(viewer, {
justDraw: false,
fillColor: Cesium.Color.YELLOW.withAlpha(0.5),
onCompleted(positions) {
console.log(positions);
}
});
g_rectMeasurement = new ANOVGIS.DrawRectangle(viewer, {
justDraw: false,
onCompleted(positions) {
console.log(positions);
}
});
g_CirgleMeasurement = new ANOVGIS.DrawCircle(viewer, {
justDraw: false,
fill: false,
outline: true,
outlineWidth: 4,
outlineColor: ANOVGIS.Color.YELLOW,
clampToGround: true,
onCompleted(positions) {
console.log(positions);
}
});
},
clear() {
if (g_polylineMeasurement) {
g_polylineMeasurement.clear();
}
if (g_polygonMeasurement) {
g_polygonMeasurement.clear();
}
if (g_pointMeasurement) {
g_pointMeasurement.clear();
}
if (g_rectMeasurement) {
g_rectMeasurement.clear();
}
if (g_CirgleMeasurement) {
g_CirgleMeasurement.clear();
}
},
end() {
if (g_polylineMeasurement) {
g_polylineMeasurement.end();
}
if (g_polygonMeasurement) {
g_polygonMeasurement.end();
}
if (g_pointMeasurement) {
g_pointMeasurement.end();
}
if (g_rectMeasurement) {
g_rectMeasurement.end();
}
if (g_CirgleMeasurement) {
g_CirgleMeasurement.end();
}
},
pointMeasurement: function () {
this.end();
g_pointMeasurement.startDraw();
this.tool = "point";
},
distanceMeasurement: function () {
this.end();
g_polylineMeasurement.startDraw();
this.tool = "distance";
},
triangleMeasurement: function () {
this.end();
g_triangleMeasurement.startDraw();
this.tool = "triangle";
},
areaMeasurement: function () {
this.end();
g_polygonMeasurement.startDraw();
this.tool = "area";
},
areaMeasurement_rect: function () {
this.end();
g_rectMeasurement.startDraw();
this.tool = "area_rect";
},
areaMeasurement_circle: function () {
this.end();
g_CirgleMeasurement.startDraw();
this.tool = "area_circle";
},
clearMeasurementResults: function () {
this.clear();
this.tool = "";
}
}
});
</script>
</body>
</html>