一:天际线实现思路:
利用Cesium 的边缘检测和后期处理效果的叠加。即在地图上检测此视角下的地形边缘,叠加标价效果层,叠加针对边缘实例和前一个边缘效果层的标记进行颜色处理和纹理处理。
主要用到的就是Cesium 提供的 Post Processing 功能:对整个场景后期处理的功能,类似模型描边,夜视效果,云雨雾之类的都用到。
二:参考官网文档:
1.supermap:Skyline - SuperMap3D Documentation
2.ceisum:PostProcessStage - Cesium Documentation
三:参考博客文章:Cesium实战记录(五)天际线分析_cesium 天际线分析-CSDN博客
四:代码部分:
1.主要引用:
<script src="../../../Cesium-1.99/Build/Cesium/Cesium.js"></script>
<link rel="stylesheet" href="../../../Cesium-1.99/Build/Cesium/Widgets/widgets.css">
<script type="text/javascript" src="../dist/SuperMap3D.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
2.样式和html部分:
<style>
#cesiumContainer {
width: 100vw;
height: 100vh;
overflow: hidden;
}
.map-tool {
position: fixed;
top: 20px;
left: 20px;
z-index: 999;
}
.map-tool button {
margin-bottom: 10px;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
#mapContainer {
width: 600px; /* 调整为合适的宽度 */
height: 400px; /* 调整为合适的高度 */
position: absolute;
top: 80px;
left: 20px;
z-index: 1000;
display: none; /* 默认隐藏 */
}
</style>
<div class="map-tool">
<button onclick="openSkylineAnay()">打开天际线</button>
<button onclick="closeSkylineAnay()">关闭天际线</button>
<button onclick="show2DSkyline()">显示二维天际线</button>
</div>
<div id="cesiumContainer"></div>
<div id="mapContainer"></div>
3.js(最主要的是着色器渲染):
<script type="text/javascript">
let viewer;
let skylineAnayStages;
let silhouette;
let myChart;
let mapContainer = document.getElementById('mapContainer');
window.onload = function () {
initMap();
addS3M();
// 延迟初始化 Skyline
setTimeout(() => {
if (!viewer.scene.skyline) {
viewer.scene.skyline = new SuperMap3D.Skyline(viewer.scene);
}
console.log(viewer.scene.skyline);
}, 2000); // 延迟2秒
};
function initMap() {
Cesium.Ion.defaultAccessToken = 'Your token'
//初始化Cesium场景
viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(),
navigation: false,
infoBox: false,
selectionIndicator: false,
terrainProvider: Cesium.createWorldTerrain({
requestVertexNormals: true,
requestWaterMask: true,
})
});
}
function addS3M() {
// S3M 瓦片层的 URL
var s3mUrl = 'your address';
// S3M 瓦片层的选项
var s3mOptions = {
name: 'name'
};
// 地理边界
var geoBounds = {
bottom: 39.895410985427681,
left: 116.42774386919061,
right: 116.4844750738345,
top: 39.932657418004872
};
// 添加 S3M 瓦片层到场景中
var promise = viewer.scene.addS3MTilesLayerByScp(s3mUrl, s3mOptions);
// 瓦片层加载成功后,飞行到指定区域
promise.then(function(result) {
viewer.camera.flyTo({
destination: Cesium.Rectangle.fromDegrees(
geoBounds.left,
geoBounds.bottom,
geoBounds.right,
geoBounds.top
),
duration: 3.0
});
console.log('S3M 瓦片层成功加载', result);
}).catch(function(error) {
console.error('加载 S3M 瓦片层失败', error);
});
}
function openSkylineAnay() {
if (skylineAnayStages) {
silhouette.enabled = true;
return;
}
skylineAnayStages = viewer.scene.postProcessStages;
let edgeDetection = Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
let postProccessStage = new Cesium.PostProcessStage({
fragmentShader: 'uniform sampler2D colorTexture;' +
'uniform sampler2D depthTexture;' +
'varying vec2 v_textureCoordinates;' +
'void main(void) {' +
'float depth = czm_readDepth(depthTexture, v_textureCoordinates);' +
'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
'if (depth < 1.0 - 0.000001) {' +
'gl_FragColor = color;' +
'} else {' +
'gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);' +
'}' +
'}'
});
let postProccesStage_1 = new Cesium.PostProcessStage({
fragmentShader: 'uniform sampler2D colorTexture;' +
'uniform sampler2D redTexture;' +
'uniform sampler2D silhouetteTexture;' +
'varying vec2 v_textureCoordinates;' +
'void main(void) {' +
'vec4 redcolor = texture2D(redTexture, v_textureCoordinates);' +
'vec4 silhouetteColor = texture2D(silhouetteTexture, v_textureCoordinates);' +
'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
'if (redcolor.r == 1.0) {' +
'gl_FragColor = mix(color, vec4(5.0, 0.0, 0.0, 1.0), silhouetteColor.a);' +
'} else {' +
'gl_FragColor = color;' +
'}' +
'}',
uniforms: {
redTexture: postProccessStage.name,
silhouetteTexture: edgeDetection.name
}
});
silhouette = new Cesium.PostProcessStageComposite({
stages: [edgeDetection, postProccessStage, postProccesStage_1],
inputPreviousStageTexture: false,
uniforms: edgeDetection.uniforms
});
skylineAnayStages.add(silhouette);
}
function closeSkylineAnay() {
if (silhouette) {
silhouette.enabled = false;
}
}
function show2DSkyline() {
// 如果 Skyline 还未初始化,则进行初始化
if (!viewer.scene.skyline) {
viewer.scene.skyline = new SuperMap3D.Skyline(viewer.scene);
}
console.log(viewer.scene.skyline);
if (!skylineAnayStages) {
alert("请先打开天际线分析!");
return;
}
// 获取二维天际线数据
let object = viewer.scene.skyline.getSkyline2D();
if (object) {
// 使用 ECharts 绘制二维天际线
if (myChart) {
myChart.dispose(); // 销毁旧的图表实例
}
myChart = echarts.init(mapContainer);
let option = {
backgroundColor: "rgba(73,139,156,0.9)",
title: {
text: "二维天际线",
},
tooltip: {
trigger: "axis",
},
xAxis: [
{
type: "category",
boundaryGap: false,
data: object.x,
show: false,
},
],
//遮挡率
yAxis: [
{
type: "value",
min: 0,
max: 1,
},
],
series: [
{
name: "",
type: "line",
data: object.y,
},
],
};
myChart.setOption(option);
mapContainer.style.display = 'block'; // 显示图表容器
} else {
alert("无法获取二维天际线数据!");
}
}
</script>
</body>
</html>
其中二维部分未能成功运行,具体原因可能是SuperMap3D
版本不支持这些功能或存在某些已知的bug,从两次console.log的返回信息来看,viewer.scene.skyline
对象在初始化时和在 show2DSkyline
函数中都是相同的。它在初始化时的状态和在 show2DSkyline
函数中是一样的,但缺少了一些关键的属性,比如 _depthBuffer
, _command
和 _lineCommand
。这些缺失的属性可能导致 getSkyline2D
方法无法正常工作。
五:代码中需要自己添加和更改的地方:
1.Cesium.Ion.defaultAccessToken,可以在cesium官网申请,这里可以看:Cesium: The Platform for 3D Geospatial
2.var s3mUrl = 'your address',这里的三维服务可以在supermap iserver中找到,这里给出一个例子:
'https://www.supermapol.com/realspace/services/3D-CBD-2/rest/realspace/datas/Building@CBD/config'
下面的name字段改成:
'Building@CBD'
都弄完之后直接live server 就可以了。