【cesium】基于vue-cesium开发地理空间分析应用
Cesium 是一个开源的 JavaScript 库,专注于创建高性能的 3D 地理空间可视化应用,在使用原生cesium开发应用时,我们需要了解学习官方文档提供的大量api,学习记忆的难度相对较高,开发量也比较大,接下来推荐一个vue组件化的cesium开发框架vue for cesium
安装依赖:
pnpm add vue-cesium
项目main.js中导入vue-cesium
import VueCesium from 'vue-cesium'
import 'vue-cesium/dist/index.css'
const app = createApp(App);
app.use(VueCesium)
页面
<template><div class="cesium"><vc-config-provider :locale="locale"><vc-viewer @ready="ready"><vc-navigation ref="navigation" :offset="[35, 35]"></vc-navigation><!--视域分析--><vc-analysesref="analysesRef"position="top-left":main-fab-opts="mainFabOpts":offset="[10, 30]":editable="editable":viewshedAnalysisOpts="viewshedAnalysisOpts"@draw-evt="drawViewEvt"@active-evt="activeViewEvt"@editor-evt="editorViewEvt"@mouse-evt="mouseViewEvt"@ready="analysesReadyDefault"></vc-analyses><!--量算工具--><!-- 修改定位 和 位置偏移 --><vc-measurements@draw-evt="drawEvt"@active-evt="activeEvt"@editor-evt="editorEvt"@mouse-evt="mouseEvt"ref="measurementsRef"position="bottom":clamp-to-ground="clampToGround":main-fab-opts="measurementFabOptions1":offset="[10, 65]":editable="editable"@ready="drawingsReadyDefault":point-measurement-opts="pointMeasurementOpts":area-measurement-opts="areaMeasurementOpts"@clear-evt="clearEvt"></vc-measurements><!--热力图--><vc-overlay-heatmapv-if="data.length"ref="heatmap":data="data":rectangle="rectangle":max="max":min="min":show="show":options="options"@ready="onHeatmapReady"type="primitive":segments="segments"></vc-overlay-heatmap><!--3Dtiles--><vc-primitive-tileseturl="https://zouyaoji.top/vue-cesium/SampleData/Cesium3DTiles/Tilesets/dayanta/tileset.json"@ready="onTilesetReady"></vc-primitive-tileset><!--影像图层--><vc-layer-imagery :sort-order="1"><vc-imagery-providerimageryProvider=""map-style="img_c"token="436ce7e50d27eede2f2929307e6b33c0"ref="provider"></vc-imagery-provider></vc-layer-imagery><vc-layer-imagery :sort-order="20"><vc-imagery-provider-tianditumap-style="cva_c"token="436ce7e50d27eede2f2929307e6b33c0"></vc-imagery-provider-tianditu></vc-layer-imagery><!-- <vc-layer-imagery :sort-order="10"><vc-imagery-provider-tianditumap-style="img_c"token="436ce7e50d27eede2f2929307e6b33c0"ref="provider"></vc-imagery-provider-tianditu></vc-layer-imagery> --><vc-layer-imagery:alpha="alpha":brightness="brightness":contrast="contrast":sort-order="10"><vc-imagery-provider-amap:map-style="mapStyle":ltype="ltype":projection-transforms="projectionTransforms":minimumLevel="0":maximumLevel="18"ref="provider"></vc-imagery-provider-amap></vc-layer-imagery></vc-viewer></vc-config-provider><div class="map_control" style="display: flex; flex-direction: column; max-width: 250px"><span class="demonstration">透明度</span><a-sliderv-model="alpha":min="0":max="1":step="0.01":default-value="1":style="{ width: '200px' }"/><span class="demonstration">亮度</span><a-sliderv-model="brightness":min="0":max="5":step="0.01":default-value="1":style="{ width: '200px' }"/><span class="demonstration">对比度</span><a-sliderv-model="contrast":min="0":max="5":step="0.01":default-value="1":style="{ width: '200px' }"/><span class="demonstration">切换风格</span><a-select v-model="mapStyle" placeholder="请选择"><a-optionv-for="item in mapStyleOptions":key="item.value":label="item.label":value="item.value"></a-option></a-select><span class="demonstration" v-if="mapStyle !== '6'">切换类型</span><a-select v-model="ltype" placeholder="请选择" v-if="mapStyle !== '6'"><a-optionv-for="item in ltypeOptions":key="item.value":label="item.label":value="item.value"></a-option></a-select></div></div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import { VcViewer, DistanceUnits, AngleUnits } from 'vue-cesium'
import zhHans from 'vue-cesium/es/locale/lang/zh-hans'const locale = ref(zhHans)const data = ref([])
const segments = ref([[10, '#4A90C3'],[20, '#81AAAC'],[40, '#B2C899'],[60, '#E50084'],[100, '#F8DE6D'],[150, '#EFA451'],[200, '#E46C38'],[346, '#D53127']
])const options = ref({backgroundColor: 'rgba(0,0,0,0)',opacity: 0.8,radius: 10,maxOpacity: 0.6,minOpacity: 0.3,blur: 0.75
})
const show = ref(true)
const max = ref(346.05413818359375)
const min = ref(0.5259535908699036)
const rectangle = ref([0, 0, 0, 0])
const heatmap = ref(null)const ready = ({ Cesium, viewer }) => {Cesium.Resource.fetchJson({url: 'https://zouyaoji.top/vue-cesium/SampleData/heatmap/pop.json'}).then((res) => {rectangle.value = res.boundsmin.value = res.minmax.value = res.maxdata.value = res.datalet ctx = document.getElementsByTagName('canvas')if (ctx && ctx.length) {ctx[0].style.width = '100%'ctx[0].style.height = '100%'}})
}
const onHeatmapReady = ({ Cesium, viewer, cesiumObject }) => {heatmap.value.childRef.value.creatingPromise.then(({ Cesium, viewer, cesiumObject }) => {console.log(cesiumObject)if (cesiumObject instanceof Cesium.GroundPrimitive) {const geometry = cesiumObject.geometryInstances.geometry.constructor.createGeometry(cesiumObject.geometryInstances.geometry)viewer.scene.camera.flyToBoundingSphere(geometry.boundingSphere)} else if (cesiumObject instanceof Cesium.Entity) {viewer.flyTo(cesiumObject)} else {viewer.camera.flyTo({ destination: cesiumObject.imageryProvider.rectangle })}})
}// 量算
const drawing = ref(false)
const restoreCursorMove = ref('auto')
const clampToGround = ref(false)
const measurementFabOptions1 = ref({direction: 'right'
})
const editable = ref(false)
const { proxy } = getCurrentInstance()
const pointMeasurementOpts = ref({preRenderDatas: [[108.9602, 34.21895, 500]],pointOpts: {color: 'red'}
})const areaMeasurementOpts = ref({preRenderDatas: [[[108.95808, 34.21955, 30],[108.95948, 34.22039, 20],[108.9595, 34.21914, 25]],[[108.955, 34.21857],[108.95573, 34.21856],[108.95573, 34.21761],[108.95499, 34.21761]]],// showAngleLabel: false,// showDistanceLabel: false,areaFormatter: (value, defaultUnits, defaultLocale, defaultDecimals) => {return `${(value * 0.0015).toFixed(4)} 亩`}
})
const drawingsReadyDefault = ({ Cesium, viewer, cesiumObject }) => {console.log('绘制选项参数:', cesiumObject)window.vm = proxy
}const drawEvt = (e, viewer) => {console.log(e)const restoreCursor = getComputedStyle(viewer.canvas).cursorif (e.finished) {drawing.value = falseif (e.type === 'move') {viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value}`)}} else {drawing.value = trueif (e.type === 'move') {viewer.canvas.setAttribute('style', 'cursor: move')}if (e.type === 'new') {viewer.canvas.setAttribute('style', 'cursor: crosshair')}}
}const activeEvt = (e, viewer) => {console.log(e)viewer.canvas.setAttribute('style', `cursor: ${e.isActive ? 'crosshair' : 'auto'}`)if (!e.isActive) {drawing.value = falserestoreCursorMove.value = 'auto'}
}const editorEvt = (e, viewer) => {console.log(e)if (e.type === 'move') {const restoreCursor = getComputedStyle(viewer.canvas).cursorviewer.canvas.setAttribute('style', 'cursor: move')drawing.value = true}
}const mouseEvt = (e, viewer) => {console.log(e)const restoreCursor = getComputedStyle(viewer.canvas).cursorif (!drawing.value) {if (e.type === 'onmouseover') {restoreCursorMove.value = restoreCursorviewer.canvas.setAttribute('style', 'cursor: pointer')} else {viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value || 'auto'}`)}}
}const clearEvt = (e, viewer) => {console.log(e)
}// 影像控制
const alpha = ref(1)
const brightness = ref(1)
const contrast = ref(1)
const mapStyleOptions = ref([{value: '6',label: '卫星影像'},{value: '7',label: '道路图'},{value: '8',label: '道路图(背景透明)'}
])
const ltypeOptions = [{value: '0',label: '默认'},{value: '4',label: '只有注记'},{value: '11',label: '只有道路'}
]
const mapStyle = ref('6')
const ltype = ref('0')//视域分析
const mainFabOpts = ref({direction: 'right'
})
const viewshedAnalysisOpts = ref({viewshedOpts: {offsetHeight: 5}
})
const onTilesetReady = ({ cesiumObject: tileset, viewer }) => {viewer.zoomTo(tileset)viewer.scene.globe.depthTestAgainstTerrain = truerestoreCursorMove.value = 'auto'drawing.value = falsewindow.viewer = viewer
}
const analysesReadyDefault = ({ Cesium, viewer, cesiumObject }) => {window.vm = proxy
}const drawViewEvt = (e, viewer) => {const restoreCursor = getComputedStyle(viewer.canvas).cursorif (e.finished) {if (e.type === 'move') {viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value}`)}drawing.value = false} else {drawing.value = trueif (e.type === 'move') {viewer.canvas.setAttribute('style', 'cursor: move')}if (e.type === 'new') {viewer.canvas.setAttribute('style', 'cursor: crosshair')}}
}const activeViewEvt = (e, viewer) => {console.log(e)viewer.canvas.setAttribute('style', `cursor: ${e.isActive ? 'crosshair' : 'auto'}`)if (!e.isActive) {drawing.value = falserestoreCursorMove.value = 'auto'}
}const editorViewEvt = (e, viewer) => {if (e.type === 'move') {viewer.canvas.setAttribute('style', 'cursor: move')drawing.value = true} else {viewer.canvas.setAttribute('style', 'cursor: auto')}
}
const mouseViewEvt = (e, viewer) => {const restoreCursor = getComputedStyle(viewer.canvas).cursorif (!drawing.value) {console.log(e)if (e.type === 'onmouseover') {restoreCursorMove.value = restoreCursorviewer.canvas.setAttribute('style', 'cursor: pointer')} else {viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value || 'auto'}`)}}
}
</script>
<style lang="less" scoped>
.cesium {width: 100%;height: 100%;background: #fff;position: relative;
}
.map_control {position: absolute;top: 100px;left: 10px;padding: 20px;border-radius: 5px;background: rgba(255, 255, 255, 0.8);
}
.demonstration {margin: 10px 0;font-weight: 600;
}
</style>
效果图: