首先来看看效果:
步骤就是鼠标移动然后结束时添加围墙,目前我还只能弄出来绘制矩形的。
直接上代码吧
Cesium.Ion.defaultAccessToken =
"xxx,你的cesium的Token";
var activeShapePoints = [];
var activeShape;
var floatingPoint;
//查看器
var viewer = new Cesium.Viewer('cesiumContainer', {
selectionIndicator: false,
infoBox: false,
terrainProvider: Cesium.createWorldTerrain()
});
var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
//双击鼠标左键清除默认事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
//绘制点
function createPoint(worldPosition) {
var point = viewer.entities.add({
position: worldPosition,
point: {
color: Cesium.Color.WHITE,
pixelSize: 5,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
}
});
return point;
}
var drawingMode = 'rectangle';
let shape = null;
//绘制图形
function drawShape(positionData) {
console.log(shape);
if (drawingMode === 'rectangle') {
//当positionData为数组时绘制最终图,如果为function则绘制动态图
var arr = typeof positionData.getValue === 'function' ? positionData.getValue(0) : positionData;
// 如果已经绘制过就清除
if (shape) {
viewer.entities.remove(shape);
}
shape = viewer.entities.add({
name: 'Blue translucent, rotated, and extruded ellipse with outline',
rectangle: {
coordinates: new Cesium.CallbackProperty(function () {
var obj = Cesium.Rectangle.fromCartesianArray(arr);
//if(obj.west==obj.east){ obj.east+=0.000001};
//if(obj.south==obj.north){obj.north+=0.000001};
return obj;
}, false),
material: Cesium.Color.RED.withAlpha(0.5)
}
});
}
return shape;
}
let we = [];
let ns = [];
//鼠标左键
handler.setInputAction((event) => {
// 我们在这里使用“viewer.scene.pickPosition”而不是“viewer.camera.pick椭球体”
// 我们在地形上移动时得到了正确的点。
var earthPosition = viewer.scene.pickPosition(event.position);
// 如果我们的鼠标不在地球上,地球位置将是不确定的
if (Cesium.defined(earthPosition)) {
if (activeShapePoints.length === 0) {
floatingPoint = createPoint(earthPosition);
activeShapePoints.push(earthPosition);
var dynamicPositions = new Cesium.CallbackProperty(function () {
return activeShapePoints;
}, false);
activeShape = drawShape(dynamicPositions);//绘制动态图
}
//将笛卡尔坐标转换为地理坐标
var cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(earthPosition);
//将弧度转为度的十进制度表示
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude);
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude);
we.push(longitudeString);
ns.push(latitudeString);
//获取相机高度
var height = Math.ceil(viewer.camera.positionCartographic.height);
ns.sort(function (a, b) {
return a - b;
});
we.sort(function (a, b) {
return a - b;
});
var east = we[0];
var west = we[we.length - 1];
var south = ns[0];
var north = ns[ns.length - 1];
// console.log('(' + longitudeString + ', ' + latitudeString );
console.log(we); console.log(ns);
window.we = we;
window.ns = ns;
// 如果是偶数次点击说明是绘制完毕
if (activeShapePoints.length == 2) {
terminateShape();
return;
}
activeShapePoints.push(earthPosition);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//鼠标移动
handler.setInputAction((event) => {
if (Cesium.defined(floatingPoint)) {
var newPosition = viewer.scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
floatingPoint.position.setValue(newPosition);
activeShapePoints.pop();
activeShapePoints.push(newPosition);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 重新绘制形状,使其不是动态的,然后删除动态形状
function terminateShape() {
if (window.wall) {
viewer.scene.primitives.remove(window.wall);
}
// 获取区域内最低最高高程
Cesium.ApproximateTerrainHeights.initialize().then(() => {
let res = Cesium.ApproximateTerrainHeights.getMinimumMaximumHeights(Cesium.Rectangle.fromDegrees(window.we[0], window.ns[0], window.we[1], window.ns[1]))
let maxHeight = res.maximumTerrainHeight
let minHeight = res.minimumTerrainHeight
// activeShapePoints.pop();//去除最后一个动态点
if (activeShapePoints.length) {
drawShape(activeShapePoints);//绘制最终图
}
viewer.entities.remove(floatingPoint);//去除终止点
// viewer.entities.remove(activeShape);//去除动态图形
floatingPoint = undefined;
activeShape = undefined;
activeShapePoints = [];
if (shape) {
viewer.entities.remove(shape);
shape = null
}
var polygon = turf.polygon([[
[we[0], ns[1]], // 注意:polygon首尾坐标要一致
[we[1], ns[1]],
[we[1], ns[0]],
[we[0], ns[0]],
[we[0], ns[1]]
]]);
var area = turf.area(polygon);
// alert(area);
var redWallInstance= new Cesium.GeometryInstance({
geometry: Cesium.WallGeometry.fromConstantHeights({
positions: Cesium.Cartesian3.fromDegreesArray([
we[0],
ns[1],
we[1],
ns[1],
we[1],
ns[0],
we[0],
ns[0],
we[0],
ns[1],
]),
maximumHeight: maxHeight + 50,
minimumHeight: minHeight,
vertexFormat: Cesium.MaterialAppearance.VERTEX_FORMAT,
}),
})
let image = './wall.png' //选择自己的动态材质图片
let color = new Cesium.Color.fromCssColorString('rgba(255, 0, 0, 0.5)')
let speed = 1
let czm_frameNumber = 0.01
let source =
`czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
vec4 colorImage = texture(image, vec2(fract((st.t - speed * czm_frameNumber * 0.005)), st.t));
vec4 fragColor = vec4(0.0);
// 调整透明度偏移
float alphaOffset = -0.6; // 调整此值以控制透明度偏移量
float newAlpha = colorImage.a + alphaOffset;
newAlpha = clamp(newAlpha, 0.0, 1.0);
// 基于偏移后的透明度获取颜色
vec4 offsetColorImage = texture(image, vec2(fract((st.t - speed * czm_frameNumber * 0.005)), st.t - alphaOffset));
// 插值原始颜色和偏移颜色
fragColor.rgb = mix(color.rgb, offsetColorImage.rgb, newAlpha);
fragColor.a = newAlpha;
fragColor = czm_gammaCorrect(fragColor);
material.alpha = newAlpha * color.a;
material.diffuse = (colorImage.rgb + color.rgb) / 2.0;
material.emission = fragColor.rgb;
return material;
}`
let material = new Cesium.Material({
fabric: {
type: 'PolylinePulseLink',
uniforms: {
color: color,
image: image,
speed: speed,
},
source: source,
},
translucent: function () {
return true
},
})
window.wall = viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [redWallInstance],
appearance: new Cesium.MaterialAppearance({
material: material,
}),
ClassificationType: Cesium.ClassificationType.BOTH
})
)
we = [];
ns = [];
})
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
handler.setInputAction(function (event) {
terminateShape();
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
// 相机初始化时定位
viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(-122.2058, 46.1955, 1000.0), new Cesium.Cartesian3(5000.0, 5000.0, 5000.0));
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
代码中绘制的部分是参考了网上的其它代码的,我就不做过多赘述。之前有看到有人在问动态墙被地形所阻挡如何解决,我在这里给出一个办法:
首先动态墙的添加是使用的primitive,在创建几何体实例时,会设置它的最低和最高高度,代码如下:
var redWallInstance = new Cesium.GeometryInstance({
geometry: Cesium.WallGeometry.fromConstantHeights({
positions: Cesium.Cartesian3.fromDegreesArray([
we[0],
ns[1],
we[1],
ns[1],
we[1],
ns[0],
we[0],
ns[0],
we[0],
ns[1],
]),
maximumHeight: maxHeight + 50, // 最大高度
minimumHeight: minHeight, // 最小高度
vertexFormat: Cesium.MaterialAppearance.VERTEX_FORMAT,
}),
})
此时使用cesium自带的一个API(Cesium.ApproximateTerrainHeights.initialize().then(()=>{
let res =Cesium.ApproximateTerrainHeights.getMinimumMaximumHeights(xxx)
}),它可以用于获取一个区域内的最小和最大高程),那么只需要将几何体实例的最低生成高程设置为当前所选范围的的最低高程,最大高度设置为比最大高程高一些,就不会再出现遮盖的问题。
只是使用这个方法还剩下最后一个问题:最大高度加上多少比较合适。我设置50的原因是发现大部分时候绘制的区域范围并不会太大或太小,但是这仅仅是一种折中的解决办法。如果想要完美解决可能需要从绘制区域的面积入手,面积如果很大,设置50就基本上见不到围墙;面积如果很小,围墙就会很高。
计算面积我用的是turf.js,代码是:
var area = turf.area(polygon);
如果想实现多个围墙的绘制,但是依旧只保持屏幕上永远只有一个动态围墙,只需要注释:
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
如果想绘制多个围墙但是不清除之前的可能就得自行研究一下了,最开始的时候也是可以实现的,只是我觉得GIS中似乎很难在一个上绘制那么多的区域,所以就把这个功能去掉了。