diff --git a/src/App.vue b/src/App.vue index e2884ae..c16d9a9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,13 @@ <template> <div id="map"> <SceneViewer id="scene-viewer"></SceneViewer> + <BottomBar></BottomBar> </div> </template> <script setup lang="ts"> import SceneViewer from './components/map/SceneViewer.vue' +import BottomBar from "@/components/map/BottomBar.vue"; </script> <style> diff --git a/src/components/map/BottomBar.vue b/src/components/map/BottomBar.vue new file mode 100644 index 0000000..2a2324a --- /dev/null +++ b/src/components/map/BottomBar.vue @@ -0,0 +1,93 @@ +<!-- + 文件描述:地图底部信息条 + 创建时间:2024/3/29 8:53 + 创建人:Zhaipeixiu +--> +<script setup lang="ts"> +import { Angle } from '@/utils/map/angle.ts' +import {ScreenSpaceEventHandler, Math, ScreenSpaceEventType} from 'cesium' +import {onMounted, ref} from "vue"; +let nowLatStr: string, nowLonStr: string +let lonlatStr = ref('') +let isDecimal = ref(true) + +onMounted(()=>{ + let _viewer = window.viewer + let canvas = _viewer.scene.canvas + let handler = new ScreenSpaceEventHandler(canvas) + handler.setInputAction((e:any)=> { + //捕获椭球体,将笛卡尔二维平面坐标转为椭球体的笛卡尔三维坐标,返回球体表面的点 + let position: any = _viewer.scene.pickPosition(e.endPosition) + if (!position) { + position = _viewer.scene.camera.pickEllipsoid(e.startPosition, _viewer.scene.globe.ellipsoid) + } + if (position) { + //将笛卡尔三维坐标转为地图坐标(弧度) + let cartographic = _viewer.scene.globe.ellipsoid.cartesianToCartographic(position); + try{ + // 将地图坐标(弧度)转为十进制的度数 + nowLatStr = Math.toDegrees(cartographic.latitude).toFixed(7) // 纬度 + nowLonStr = Math.toDegrees(cartographic.longitude).toFixed(7) // 经度 + let camera_alt = (_viewer.camera.positionCartographic.height / 1000) // 视高 + // 相机低于250、俯仰角小于-80度时才计算海拔高度(否则误差大) + let needElevation: boolean = camera_alt < 250 && (_viewer.camera.pitch < -(Math.PI/180)*80) + let elevStr = needElevation? _viewer.scene.globe.getHeight(cartographic)?.toFixed(2)?? '' : '' // 海拔 + + if(isDecimal.value) { //如果是十进制度 + lonlatStr.value = `经度:${nowLonStr} , 纬度:${nowLatStr} , 海拔(m):${elevStr}` + } + else { + lonlatStr.value = `经度:${Angle.DecimalDegree2DMS(nowLonStr)} , 纬度:${Angle.DecimalDegree2DMS(nowLatStr)} , 海拔(m):${elevStr}` + } + } catch (e) {} + } + }, ScreenSpaceEventType.MOUSE_MOVE) +}) + +function lonlatClick() { + let elevStr = lonlatStr.value.split('海拔')[1] + if(isDecimal.value){ + lonlatStr.value = `经度:${Angle.DecimalDegree2DMS(nowLonStr)} , 纬度:${Angle.DecimalDegree2DMS(nowLatStr)} , 海拔(m)` + elevStr + } + else { + lonlatStr.value = `经度:${nowLonStr} , 纬度:${nowLatStr} , 海拔(m)` + elevStr + } + isDecimal.value = !isDecimal.value +} +</script> + +<template> + <div id="map-footer"> + <button id="lonlatText" @click="lonlatClick"> + {{ lonlatStr }} + </button> + </div> +</template> + +<style scoped> +#map-footer { + position: absolute; + bottom: 1px; + left: 0; + width: 100vw; + height: 2.3rem; + background-color: rgba(47, 53, 60, 0.8); + color: #fff; + font-size: 0.7rem; + text-align: center; + z-index: 1; + padding: 0; + display: flex; + align-items: center; +} +#lonlatText{ + margin-left: 2rem; + background: none; + border: none; + color: #FFFFFF; + font-size: .8rem; +} +#lonlatText:hover{ + cursor: pointer; +} +</style> diff --git a/src/components/map/SceneViewer.vue b/src/components/map/SceneViewer.vue index f4208e5..d41d859 100644 --- a/src/components/map/SceneViewer.vue +++ b/src/components/map/SceneViewer.vue @@ -3,7 +3,7 @@ * @Date: 2024-03-07 14:15:35 * @LastEditors: cbwu * @LastEditTime: 2024-04-02 14:14:36 - * @Description: + * @Description: --> <template> <div id="cesium-viewer" ref="viewerDivRef"></div> diff --git a/src/components/styles/cesium-compass.css b/src/components/styles/cesium-compass.css index 5a3a5b5..b7f7235 100644 --- a/src/components/styles/cesium-compass.css +++ b/src/components/styles/cesium-compass.css @@ -3,19 +3,20 @@ /*比例尺背景*/ .distance-legend { position: absolute; - border-radius: 15px; - padding-left: .5rem; - padding-right: .5rem; + padding-left: 0; + padding-right: 0; right: .1rem; height: 2rem; bottom: .2rem; font-weight: bolder; box-sizing: content-box; + z-index: 2; } /*比例尺文本*/ .distance-legend-label { display: inline-block; font-family: 'Roboto', sans-serif; + padding-left: .5rem; font-size: .9rem; line-height: 1rem; color: #FFFFFF; diff --git a/src/utils/map/TDTProvider.ts b/src/utils/map/TDTProvider.ts index 28279ce..a5cfd82 100644 --- a/src/utils/map/TDTProvider.ts +++ b/src/utils/map/TDTProvider.ts @@ -11,6 +11,8 @@ import { GeographicTilingScheme, WebMercatorTilingScheme, } from 'cesium' +import axios from "axios" + //地图服务枚举类型:_c为墨卡托投影,_w为经纬度投影 enum TDTLayerType { Vec = 'vec', //矢量底图 @@ -86,9 +88,28 @@ export function getTDTTerrainProvider() { terrainUrls.push(url) } - const provider = new window.cesium.GeoTerrainProvider({ + const provider = new Cesium.GeoTerrainProvider({ urls: terrainUrls, }) return provider } + +/** + * 天地图地理编码服务 + * @param word 地名 + */ +export function Geocoder(word: string) { + let url = `http://api.tianditu.gov.cn/geocoder?ds={"keyWord":"${word}"}&tk=` + return axios.get(url + TDT_tk) +} + +/** + * 天地图逆地理编码服务 + * @param lon 经度 + * @param lat 纬度 + */ +export function reverseGeocoder(lon:number, lat: number ) { + let url = `http://api.tianditu.gov.cn/geocoder?postStr={'lon':${lon},'lat':${lat},'ver':1}&type=geocode&tk=` + return axios.get(url + TDT_tk) +} export { TDTLayerType, TDTProjectionType } diff --git a/src/utils/map/angle.ts b/src/utils/map/angle.ts new file mode 100644 index 0000000..c3d48e6 --- /dev/null +++ b/src/utils/map/angle.ts @@ -0,0 +1,30 @@ +class Angle { + constructor() {} + + /** + * 十进制经纬度转度分秒,精确至毫秒 + * @param decimal_var 十进制经纬度 + * @constructor + */ + static DecimalDegree2DMS(decimal_var: number|string){ + if(!decimal_var.toString().includes('.')) + return decimal_var.toString() + '°0\'0\'\'' + let decimalStr = decimal_var.toString().split('.') + let degreeStr = decimalStr[0] + if (decimalStr[1]){ + let minutes = Number(decimalStr[1]) / Math.pow(10,decimalStr[1].length) * 60 + if(!minutes.toString().includes('.')) + return degreeStr + '°'+ minutes.toString() +'\'0\'\'' + let minuteSecondsStr = minutes.toString().split('.') + if (minuteSecondsStr[1]){ + let secondStr = Number(minuteSecondsStr[1]) / Math.pow(10,minuteSecondsStr[1].length) * 60 + return degreeStr + '°'+ minuteSecondsStr[0] +'\'' + secondStr.toFixed(3) + '\'\'' + } + } + return '' + } +} + + + +export { Angle } diff --git a/src/utils/map/coordinate.ts b/src/utils/map/coordinate.ts index d98bf59..d9a8ebc 100644 --- a/src/utils/map/coordinate.ts +++ b/src/utils/map/coordinate.ts @@ -6,8 +6,9 @@ * @Description: 坐标系转化 */ import { Cartesian2, Viewer, Math, Cartographic, Cartesian3 } from 'cesium' + /** - * 屏幕坐标转笛卡尔坐标,注:获取的是地表坐标 + * 屏幕坐标转笛卡尔坐标 * @param viewer * @param windowPosition :屏幕二维坐标 * @returns :笛卡尔坐标 @@ -45,4 +46,5 @@ function cartesian3ToWGS84(pos: Cartesian3) { } return [] } -export { cartesian2ToCartesian3, cartesian2ToWGS84, cartesian3ToWGS84 } + +export { cartesian2ToCartesian3, cartesian2ToWGS84, cartesian3ToWGS84 } diff --git a/src/utils/map/sceneViewer.ts b/src/utils/map/sceneViewer.ts index dc2fb6f..e3f3955 100644 --- a/src/utils/map/sceneViewer.ts +++ b/src/utils/map/sceneViewer.ts @@ -9,9 +9,11 @@ import { Viewer, TileMapServiceImageryProvider, + ScreenSpaceEventType, ImageryLayer, RequestScheduler, SceneMode, + Rectangle, buildModuleUrl, ScreenSpaceEventType, } from 'cesium' @@ -92,12 +94,12 @@ function perfViewer(viewer: Viewer) { } function showNavigator(viewer: Viewer){ - let options: any = {} + const options: any = {} // 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和 Cesium.Rectangle - options.defaultResetView = Cesium.Rectangle.fromDegrees(80, 22, 130, 50) + options.defaultResetView = Rectangle.fromDegrees(80, 22, 130, 50) options.enableCompass = true //罗盘组件 true启用,false禁用 options.enableCompassOuterRing = true //罗盘外环组件 true启用,false禁用 - options.enableZoomControls = true //缩放组件 true启用,false禁用 + options.enableZoomControls = true //缩放组件 true启用,false禁用 options.enableDistanceLegend = true //比例尺组件 true启用,false禁用 new CesiumNavigation(viewer, options) }