最近开发一个数据可视化大屏,需要渲染楼栋,但是手里并没有楼栋的数据,今天我们来记录如何获取geoJson以及渲染geoJson数据为楼栋。
获取geoJson数据
这里我们使用到了阿里云的边界生成器 .
我们找到我们想要的小区放大地图到最大,这样我们就可以看到楼栋的轮廓了,
我们通过多边形工具,将大楼轮廓勾勒出来,这样就可以获取到这个大楼的轮廓了,接下来我们导出geoJson数据,然后增加字段,我们手动查询这个小区各个楼栋的高度。
然后我们将获取到的楼栋高度回填到geoJson中的height,这样我们的楼栋数据就大概成型了。
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 14969,
"properties": {
"_draw_type": "fill",
"name": "1座",
"height": 46
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
113.14957777,
22.98884755
],
[
113.14990915,
22.9888465
],
[
113.14990857,
22.98868735
],
[
113.14957397,
22.98868732
],
[
113.14957299,
22.98868911
],
[
113.14957777,
22.98884755
]
]
]
},
"bbox": [
113.14957299,
22.98868732,
113.14990915,
22.98884755
]
}
]
}
数据渲染
数据渲染,接下来,我们进行数据渲染
<script setup>
import * as Cesium from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'
import {onMounted, ref, onUnmounted} from "vue"
import loudong from './data/loudong.json'
import {
gcj02towgs84
} from './coordTransform.js'
import labelCom from './label/index.vue'
import {CESIUM_TOKEN} from '@/config/config.js'
const FLOOR_HEIGHT = 3;
const labelPositions = ref([])
const selectedEntity = ref(null)
// 添加当前高亮索引
const currentIndex = ref(0)
// 添加定时器引用
let highlightTimer = null
Cesium.Ion.defaultAccessToken = CESIUM_TOKEN
onMounted(async () => {
const viewer = new Cesium.Viewer('cesiumBody', {
infoBox: true,
geocoder: false,
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,
timeline: false,
fullscreenButton: false,
terrainProvider: await Cesium.createWorldTerrainAsync(),
scene3DOnly: true,
shouldAnimate: true
})
viewer.cesiumWidget.creditContainer.style.display = 'none'
viewer.scene.globe.enableLighting = true
viewer.scene.globe.depthTestAgainstTerrain = true
try {
const features = loudong.features.map(feature => {
const coordinates = feature.geometry.coordinates[0].map(coord => {
const [lng, lat] = gcj02towgs84(coord[0], coord[1]);
return [lng, lat];
});
return {
...feature,
geometry: {
...feature.geometry,
coordinates: [coordinates]
}
};
});
const transformedGeoJSON = {
type: "FeatureCollection",
features: features
};
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(113.14572076, 22.99590083, 411.50),
orientation: {
heading: Cesium.Math.toRadians(184.93),
pitch: Cesium.Math.toRadians(-26.38),
roll: 0.00
},
duration: 3
});
const osmBuildings = await Cesium.createOsmBuildingsAsync()
viewer.scene.primitives.add(osmBuildings)
const geojsonOptions = {
clampToGround: true,
stroke: Cesium.Color.WHITE,
fill: Cesium.Color.fromCssColorString('#22d4f5').withAlpha(0.6),
strokeWidth: 2,
};
const dataSource = await Cesium.GeoJsonDataSource.load(transformedGeoJSON, geojsonOptions);
viewer.dataSources.add(dataSource);
const entities = dataSource.entities.values;
entities.forEach(entity => {
if (entity.polygon) {
const floorCount = entity.properties.height?._value || 1;
const buildingHeight = floorCount * FLOOR_HEIGHT;
entity.polygon.extrudedHeight = buildingHeight;
entity.polygon.material = new Cesium.ColorMaterialProperty(
Cesium.Color.fromCssColorString('#22d4f5').withAlpha(0.6)
);
entity.polygon.outline = true;
entity.polygon.outlineColor = Cesium.Color.WHITE;
entity.polygon.classificationType = Cesium.ClassificationType.BOTH;
entity.polygon.heightReference = Cesium.HeightReference.RELATIVE_TO_GROUND;
const positions = entity.polygon.hierarchy.getValue().positions;
const centerCartesian = Cesium.BoundingSphere.fromPoints(positions).center;
const centerCartographic = Cesium.Cartographic.fromCartesian(centerCartesian);
const longitude = Cesium.Math.toDegrees(centerCartographic.longitude);
const latitude = Cesium.Math.toDegrees(centerCartographic.latitude);
labelPositions.value.push({
position: Cesium.Cartesian3.fromDegrees(longitude, latitude, buildingHeight + 5),
name: entity.properties.name._value,
height: buildingHeight + 5,
screenX: 0,
screenY: 0
})
}
});
// 添加自动轮播高亮效果
const highlightNextBuilding = () => {
if (selectedEntity.value) {
selectedEntity.value.polygon.material = new Cesium.ColorMaterialProperty(
Cesium.Color.fromCssColorString('#22d4f5').withAlpha(0.6)
);
}
currentIndex.value = (currentIndex.value + 1) % entities.length;
const entity = entities[currentIndex.value];
entity.polygon.material = new Cesium.ColorMaterialProperty(
Cesium.Color.YELLOW.withAlpha(1)
);
selectedEntity.value = entity;
}
// 启动定时器,每3秒切换一次高亮
highlightTimer = setInterval(highlightNextBuilding, 3000);
viewer.scene.postRender.addEventListener(() => {
labelPositions.value.forEach(item => {
const windowPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene,
item.position
)
if (windowPosition) {
item.screenX = windowPosition.x
item.screenY = windowPosition.y
}
})
})
viewer.screenSpaceEventHandler.setInputAction((movement) => {
const camera = viewer.camera;
const position = camera.position;
const cartographic = Cesium.Cartographic.fromCartesian(position);
console.log('相机位置:', {
heading: Cesium.Math.toDegrees(camera.heading).toFixed(2),
pitch: Cesium.Math.toDegrees(camera.pitch).toFixed(2),
roll: Cesium.Math.toDegrees(camera.roll).toFixed(2),
longitude: Cesium.Math.toDegrees(cartographic.longitude).toFixed(8),
latitude: Cesium.Math.toDegrees(cartographic.latitude).toFixed(8),
height: cartographic.height.toFixed(2)
});
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
// 添加点击事件处理
viewer.screenSpaceEventHandler.setInputAction((movement) => {
const pickedFeature = viewer.scene.pick(movement.position);
// 清除定时器,停止自动轮播
if(highlightTimer) {
clearInterval(highlightTimer);
highlightTimer = null;
}
if (selectedEntity.value) {
// 恢复之前选中实体的样式
selectedEntity.value.polygon.material = new Cesium.ColorMaterialProperty(
Cesium.Color.fromCssColorString('#22d4f5').withAlpha(0.6)
);
}
if (Cesium.defined(pickedFeature) && pickedFeature.id) {
// 设置新选中实体的高亮样式
pickedFeature.id.polygon.material = new Cesium.ColorMaterialProperty(
Cesium.Color.YELLOW.withAlpha(1)
);
selectedEntity.value = pickedFeature.id;
// 更新当前索引
const entities = dataSource.entities.values;
currentIndex.value = entities.indexOf(pickedFeature.id);
console.log('选中建筑:', pickedFeature.id.properties.name?._value);
} else {
selectedEntity.value = null;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
} catch (error) {
console.error('加载失败:', error)
}
})
// 组件卸载时清除定时器
onUnmounted(() => {
if (highlightTimer) {
clearInterval(highlightTimer)
}
})
</script>
最终效果如下
cesium版本
"cesium": "1.114.0",