<template>
<div id="cesiumContainer" class="cesiumContainer"></div>
<!-- 新增:坐标系选择器(不影响原有功能) -->
<div class="coordinate-selector">
<span>坐标系:</span>
<el-radio-group v-model="coordinateSystem" size="small">
<el-radio-button label="gcj02">高德/火星坐标</el-radio-button>
<el-radio-button label="bd09">百度坐标</el-radio-button>
</el-radio-group>
</div>
<!-- 原有点击事件已注释,保留代码以便后续恢复 -->
<!-- <div ref="miniMapContainer" class="mini-map" @click="handleMiniMapClick"> -->
<div ref="miniMapContainer" class="mini-map">
<div class="location-indicator" :style="indicatorStyle">
<!-- 新增四条准星延伸线 -->
<div class="ext-line ext-line-top"></div>
<div class="ext-line ext-line-right"></div>
<div class="ext-line ext-line-bottom"></div>
<div class="ext-line ext-line-left"></div>
</div>
<div class="corner corner-top-left"></div>
<div class="corner corner-top-right"></div>
<div class="corner corner-bottom-left"></div>
<div class="corner corner-bottom-right"></div>
<div class="focus-effect"></div>
<div class="pulse"></div>
</div>
<Search @location-selected="handleLocationSelected" />
<LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" />
</template>
<style>
/* 原有样式完全不变 */
</style>
<script setup lang="ts">
// 原有导入 + 新增坐标转换工具类导入
import { computed, onUnmounted, onMounted, reactive, ref } from "vue";
import LocationBar from "./location-bar.vue";
import Search from "./search.vue";
import initMap from "./init";
import { throttle } from "lodash";
import { loadRipplePoints, createMultipleRippleCircles } from "./circle.js";
import { $prototype } from "../../main.ts";
import markerImage from "@/assets/images/building.png";
import { imageDark } from "naive-ui/es/image/styles";
// 新增:导入坐标转换工具类
import { CoordinateTransformer } from "./coordinate-transformer";
// 修复类型定义
type Rectangle = any;
// 原有状态 + 新增坐标系选择状态
const miniMapContainer = ref<HTMLElement>();
let viewIndicator: Rectangle;
const currentPosition = reactive({
longitude: 113.361538,
latitude: 27.339318,
});
const ZHUZHOU_EXTENT = {
west: 112.5,
east: 114.5,
south: 26.0,
north: 28.0,
};
const rippleEntities = ref<any[]>([]);
const heightThreshold = 80000;
const indicatorStyle = ref({
left: "50%",
top: "50%",
display: "block",
});
const loaded = ref(false);
const useDmsFormat = ref(false);
const overviewViewer = ref(null);
let currentMarker: any = null;
// 新增:坐标系选择状态(默认高德GCJ-02)
const coordinateSystem = ref("gcj02"); // 'gcj02' | 'bd09'
// 原有方法完全不变(波纹可见性更新)
const updateRippleVisibility = throttle(() => {
if (!$prototype.$map || rippleEntities.value.length === 0) return;
let shouldShow = false;
const cartographic = $prototype.$map.camera.positionCartographic;
if (cartographic) {
const cameraHeight = cartographic.height;
shouldShow = cameraHeight > heightThreshold;
}
rippleEntities.value.forEach((entity) => {
//entity.show = shouldShow;
entity.show = false;
});
}, 200);
// 原有方法完全不变(指示器位置更新)
const updateIndicatorPosition = () => {
if (!$prototype.$map) return;
const camera = $prototype.$map.camera;
const rect = camera.computeViewRectangle();
if (!rect) return;
const center = Cesium.Rectangle.center(rect);
const lon = Cesium.Math.toDegrees(center.longitude);
const lat = Cesium.Math.toDegrees(center.latitude);
const constrainedLon = Math.max(
ZHUZHOU_EXTENT.west,
Math.min(ZHUZHOU_EXTENT.east, lon)
);
const constrainedLat = Math.max(
ZHUZHOU_EXTENT.south,
Math.min(ZHUZHOU_EXTENT.north, lat)
);
const lonPercent =
((constrainedLon - ZHUZHOU_EXTENT.west) /
(ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) *
100;
const latPercent =
100 -
((constrainedLat - ZHUZHOU_EXTENT.south) /
(ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) *
100;
indicatorStyle.value = {
left: `${lonPercent}%`,
top: `${latPercent}%`,
display: "block",
};
};
// 原有方法完全不变(鹰眼地图更新)
const updateOverview = () => {
if (!$prototype.$map || !overviewViewer.value) return;
const rectangle = $prototype.$map.camera.computeViewRectangle();
if (!rectangle) return;
updateIndicatorPosition();
// 添加视口矩形更新逻辑
if (viewIndicator) {
viewIndicator.rectangle.coordinates = rectangle;
}
};
// 原有方法完全不变(初始化鹰眼地图)
const initMiniMap = () => {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM";
if (!miniMapContainer.value) return;
const worldProvider = new Cesium.UrlTemplateImageryProvider({
url:
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
minimumLevel: 0,
maximumLevel: 19,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
});
const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({
url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png",
minimumLevel: 1,
maximumLevel: 17,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
});
overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
homeButton: false,
timeline: false,
navigationHelpButton: false,
animation: false,
scene3DOnly: true,
selectionIndicator: false,
infoBox: false,
imageryProvider: worldProvider,
terrainProvider: undefined,
mapProjection: new Cesium.WebMercatorProjection(),
skyBox: false,
skyAtmosphere: false,
});
// 禁用所有相机交互控制(新增功能代码)
const cameraController = overviewViewer.value.scene.screenSpaceCameraController;
cameraController.enableTranslate = false;
cameraController.enableZoom = false;
cameraController.enableRotate = false;
cameraController.enableTilt = false;
cameraController.enableLook = false;
const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider(
zhuzhouProvider
);
overviewViewer.value.camera.setView({
destination: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
),
});
const toolbar = overviewViewer.value.container.getElementsByClassName(
"cesium-viewer-toolbar"
)[0];
if (toolbar) toolbar.style.display = "none";
overviewViewer.value.cesiumWidget.creditContainer.style.display = "none";
initRectangle();
};
// 原有方法完全不变(初始化视图指示器)
function initRectangle() {
viewIndicator = overviewViewer.value.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
),
material: Cesium.Color.RED.withAlpha(0.3),
outline: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 2,
},
});
}
// 原有方法完全不变(添加演示图形)
function addDemoGraphics() {
const chinaBoundary = $prototype.$map.dataSources.add(
Cesium.GeoJsonDataSource.load("/shp_zz.geojson", {
stroke: Cesium.Color.WHITE,
fill: false,
clampToGround: true,
describe: null,
})
);
chinaBoundary.then((dataSource) => {
const entities = dataSource.entities.values;
for (let entity of entities) {
if (entity.polyline) {
entity.polyline.fill = false;
}
}
});
}
// 原有方法完全不变(飞行到默认位置)
function flyToDes() {
const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0);
$prototype.$map.camera.flyTo({
destination: new Cesium.Cartesian3(
-2432812.6687511606,
5559483.804371395,
2832009.419525571
),
orientation: {
heading: 6.283185307179421,
pitch: -1.0472145569408116,
roll: 6.2831853071795205,
},
complete: function () {},
});
}
// 原有方法完全不变(监听相机变化)
const setupCameraListener = () => {
$prototype.$map.camera.changed.addEventListener(updateOverview);
};
// 原有方法完全不变(鹰眼地图点击处理)
const handleMiniMapClick = (event: MouseEvent) => {
if (!miniMapContainer.value || !overviewViewer.value) return;
const rect = miniMapContainer.value.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const xPercent = (x / rect.width) * 100;
const yPercent = (y / rect.height) * 100;
const lon =
ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west);
const lat =
ZHUZHOU_EXTENT.north -
(yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south);
$prototype.$map.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000),
});
};
// 原有生命周期方法完全不变
onMounted(() => {
initMap();
loaded.value = true;
addDemoGraphics();
flyToDes();
setTimeout(() => {
initMiniMap();
setupCameraListener();
updateOverview();
}, 1000);
(async () => {
try {
const ripplePoints = await loadRipplePoints();
rippleEntities.value = createMultipleRippleCircles($prototype.$map, ripplePoints);
$prototype.$map.camera.changed.addEventListener(updateRippleVisibility);
updateRippleVisibility();
} catch (error) {
console.error("加载波纹圆失败:", error);
}
})();
});
// 原有方法完全不变(创建标记)
const createMarker = (wgsLocation) => {
if (currentMarker) {
$prototype.$map.entities.remove(currentMarker);
}
console.log("标记图片加载状态:", markerImage);
console.log("创建标记位置:", wgsLocation);
currentMarker = $prototype.$map.entities.add({
position: Cesium.Cartesian3.fromDegrees(wgsLocation.lng, wgsLocation.lat, 10),
label: {
text: wgsLocation.name + "(标记位置)",
font: "18px sans-serif",
fillColor: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.TOP,
pixelOffset: new Cesium.Cartesian2(0, 20),
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
show: true,
},
// 关键:用 billboard 替换 point
billboard: {
image: new URL("@/assets/images/building.png", import.meta.url).href, // 本地图标
width: 32, // 图标宽度(像素)
height: 32, // 图标高度(像素)
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 让图标尖点对地
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
scale: 1.0,
},
});
console.log("标记实体已添加:", currentMarker);
$prototype.$map.scene.requestRender();
return currentMarker;
};
// 核心修改:使用工具类实现动态坐标系转换
const handleLocationSelected = (location: { lng: number; lat: number; name: string }) => {
if (!$prototype.$map) return;
// 根据选择的坐标系进行转换
let wgsLocation;
if (coordinateSystem.value === "gcj02") {
// 高德/火星坐标转WGS84
wgsLocation = CoordinateTransformer.gcj02ToWgs84(location.lng, location.lat);
} else {
// 百度坐标转WGS84
wgsLocation = CoordinateTransformer.bd09ToWgs84(location.lng, location.lat);
}
console.log(`转换前(${coordinateSystem.value})坐标:`, location.lng, location.lat);
console.log("转换后(WGS84)坐标:", wgsLocation.lng, wgsLocation.lat);
// 使用转换后的坐标执行原有逻辑
const destination = Cesium.Cartesian3.fromDegrees(
wgsLocation.lng,
wgsLocation.lat,
3000
);
$prototype.$map.camera.flyTo({
destination,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0,
},
duration: 2,
complete: () => {
createMarker({ ...location, ...wgsLocation });
},
});
};
// 原有方法完全不变(组件销毁清理)
onUnmounted(() => {
if ($prototype.$map) {
$prototype.$map.destroy();
$prototype.$map = null;
}
if ($prototype.$map) {
$prototype.$map.camera.changed.removeEventListener(updateRippleVisibility);
}
console.log("组件销毁");
});
const emit = defineEmits(["onload", "onclick"]);
const initMars3d = async (option: any) => {
emit("onclick", true);
emit("onload", $prototype.$map);
};
</script>
<style lang="less">
/**cesium 工具按钮栏*/
.cesium-viewer-toolbar {
top: auto !important;
bottom: 35px !important;
left: 12px !important;
right: auto !important;
}
.cesium-toolbar-button img {
height: 100%;
}
.cesium-viewer-toolbar > .cesium-toolbar-button,
.cesium-navigationHelpButton-wrapper,
.cesium-viewer-geocoderContainer {
margin-bottom: 5px;
float: left;
clear: both;
text-align: center;
}
.cesium-button {
background-color: rgba(23, 49, 71, 0.8);
color: #e6e6e6;
fill: #e6e6e6;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
line-height: 32px;
}
.cesium-button:hover {
background: #3ea6ff;
}
/**cesium 底图切换面板*/
.cesium-baseLayerPicker-dropDown {
bottom: 0;
left: 40px;
max-height: 700px;
margin-bottom: 5px;
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 帮助面板*/
.cesium-navigation-help {
top: auto;
bottom: 0;
left: 40px;
transform-origin: left bottom;
background: none;
background-color: rgba(23, 49, 71, 0.8);
.cesium-navigation-help-instructions {
background: none;
}
.cesium-navigation-button {
background: none;
}
.cesium-navigation-button-selected,
.cesium-navigation-button-unselected:hover {
background: rgba(0, 138, 255, 0.2);
}
}
/**cesium 二维三维切换*/
.cesium-sceneModePicker-wrapper {
width: auto;
}
.cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon {
float: right;
margin: 0 3px;
}
/**cesium POI查询输入框*/
.cesium-viewer-geocoderContainer .search-results {
left: 0;
right: 40px;
width: auto;
z-index: 9999;
}
.cesium-geocoder-searchButton {
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input {
background-color: rgba(63, 72, 84, 0.7);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input:focus {
background-color: rgba(63, 72, 84, 0.9);
}
.cesium-viewer-geocoderContainer .search-results {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium info信息框*/
.cesium-infoBox {
top: 50px;
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-infoBox-title {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 任务栏的FPS信息*/
.cesium-performanceDisplay-defaultContainer {
top: auto;
bottom: 35px;
right: 50px;
}
.cesium-performanceDisplay-ms,
.cesium-performanceDisplay-fps {
color: #fff;
}
/**cesium tileset调试信息面板*/
.cesium-viewer-cesiumInspectorContainer {
top: 10px;
left: 10px;
right: auto;
}
.cesium-cesiumInspector {
background-color: rgba(23, 49, 71, 0.8);
}
/**覆盖mars3d内部控件的颜色等样式*/
.mars3d-compass .mars3d-compass-outer {
fill: rgba(23, 49, 71, 0.8);
}
.mars3d-contextmenu-ul,
.mars3d-sub-menu {
background-color: rgba(23, 49, 71, 0.8);
> li > a:hover,
> li > a:focus,
> li > .active {
background-color: #3ea6ff;
}
> .active > a,
> .active > a:hover,
> .active > a:focus {
background-color: #3ea6ff;
}
}
/* Popup样式*/
.mars3d-popup-color {
color: #ffffff;
}
.mars3d-popup-background {
background: rgba(23, 49, 71, 0.8);
}
.mars3d-popup-content {
margin: 15px;
}
.mars3d-template-content label {
padding-right: 6px;
}
.mars3d-template-titile {
border-bottom: 1px solid #3ea6ff;
}
.mars3d-template-titile a {
font-size: 16px;
}
.mars3d-tooltip {
background: rgba(23, 49, 71, 0.8);
border: 1px solid rgba(23, 49, 71, 0.8);
}
.mars3d-popup-btn-custom {
padding: 3px 10px;
border: 1px solid #209ffd;
background: #209ffd1c;
}
.mars-dialog .mars-dialog__content {
height: 100%;
width: 100%;
overflow: auto;
padding: 5px;
}
.image {
border: solid 2px #fff;
}
.content {
height: 90%;
padding-top: 10px;
overflow-x: auto;
overflow-y: auto;
}
.content-text {
padding: 0 10px;
text-indent: 30px;
font-size: 17px;
}
.details-video {
width: 100%;
height: 760px;
background-color: #000;
}
:where(.css-lt97qq9).ant-space {
display: inline-flex;
}
:where(.css-lt97qq9).ant-space-align-center {
align-items: center;
}
:where(.css-lt97qq9).ant-image .ant-image-mask {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
:where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0 4px;
}
:where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] {
box-sizing: border-box;
}
:where(.css-lt97qq9).ant-image .ant-image-img {
width: 100%;
height: auto;
vertical-align: middle;
}
</style>
<style scoped>
.mini-map-container {
position: relative;
width: 100%;
height: 100%;
}
.main-viewer {
width: 100%;
height: 100%;
}
.mini-map {
position: absolute;
right: 3vw;
bottom: 6vh;
width: 12vw;
height: 17vh;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 999;
cursor: pointer;
overflow: hidden;
}
.location-indicator {
position: absolute;
width: 19px;
height: 19px;
transform: translate(-50%, -50%);
z-index: 100;
/* border: 2px solid red;
border-radius: 2px; */
}
/* 原十字准星伪元素保持不变 */
.location-indicator::before,
.location-indicator::after {
content: "";
position: absolute;
background-color: red;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.location-indicator::before {
width: 14px;
height: 2px;
}
.location-indicator::after {
width: 2px;
height: 14px;
}
.view-rectangle {
position: absolute;
border: 2px solid #00f2fe;
z-index: 99;
pointer-events: none;
box-shadow: 0 0 20px rgba(0, 242, 254, 0.7);
}
/* 相机聚焦样式 - 四个角折角 */
.corner {
position: absolute;
width: 25px;
height: 25px;
z-index: 100;
pointer-events: none;
}
.corner::before,
.corner::after {
content: "";
position: absolute;
background: #00f2fe;
box-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.corner-top-left {
top: -2px;
left: -2px;
}
.corner-top-left::before {
top: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-top-left::after {
top: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-top-right {
top: -2px;
right: -2px;
}
.corner-top-right::before {
top: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-top-right::after {
top: 0;
right: 0;
width: 3px;
height: 15px;
}
.corner-bottom-left {
bottom: -2px;
left: -2px;
}
.corner-bottom-left::before {
bottom: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-bottom-left::after {
bottom: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-bottom-right {
bottom: -2px;
right: -2px;
}
.corner-bottom-right::before {
bottom: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-bottom-right::after {
bottom: 0;
right: 0;
width: 3px;
height: 15px;
}
.camera-icon {
position: absolute;
top: 10px;
right: 10px;
color: #00f2fe;
font-size: 24px;
z-index: 100;
text-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.focus-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 98;
border: 2px solid rgba(0, 242, 254, 0.2);
border-radius: 5px;
box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1);
}
.pulse {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(0, 242, 254, 0.7);
box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7);
animation: pulse 2s infinite;
}
::v-deep.cesium-viewer-toolbar {
display: none;
}
</style>
检查live-map代码,其中为什么这个红色十字定位位置会不对,会呆在左上方,虽然移动后会变正确,但是刷新页面显示时就是有时候会不对,会影响观感,你阅读live-map.vue代码中鹰眼地图部分代码,尝试寻找原因修改,然后尽可能不更改其他任何无关代码,减少对代码的修改
最新发布