|
@@ -3,8 +3,7 @@
|
|
<!-- 自定义弹框组件 -->
|
|
<!-- 自定义弹框组件 -->
|
|
<div v-if="selectedPoint" class="custom-popup" :style="{
|
|
<div v-if="selectedPoint" class="custom-popup" :style="{
|
|
left: `${popupPosition.x}px`,
|
|
left: `${popupPosition.x}px`,
|
|
- top: `${popupPosition.y}px`,
|
|
|
|
- display: selectedPoint ? 'block' : 'none'
|
|
|
|
|
|
+ top: `${popupPosition.y}px`
|
|
}">
|
|
}">
|
|
<div class="popup-content">
|
|
<div class="popup-content">
|
|
<h3>{{ selectedPoint.STNM || '未知点' }}</h3>
|
|
<h3>{{ selectedPoint.STNM || '未知点' }}</h3>
|
|
@@ -16,7 +15,7 @@
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
-import { ref, onMounted, onUnmounted, getCurrentInstance } from 'vue'
|
|
|
|
|
|
+import { ref, onMounted, onUnmounted } from 'vue'
|
|
import * as Cesium from 'cesium';
|
|
import * as Cesium from 'cesium';
|
|
import "cesium/Build/CesiumUnminified/Widgets/widgets.css";
|
|
import "cesium/Build/CesiumUnminified/Widgets/widgets.css";
|
|
import JYLData from '@/assets/Data/THJYL.json'
|
|
import JYLData from '@/assets/Data/THJYL.json'
|
|
@@ -25,53 +24,46 @@ const TDTTK = "d9e7aa2ad204aba6aeedea6f5ab48ed9";
|
|
const selectedPoint = ref(null);
|
|
const selectedPoint = ref(null);
|
|
const popupPosition = ref({ x: 0, y: 0 });
|
|
const popupPosition = ref({ x: 0, y: 0 });
|
|
let handler = null;
|
|
let handler = null;
|
|
|
|
+let viewer = null;
|
|
|
|
+
|
|
|
|
+// 计算页面缩放比例(与autofit配置一致)
|
|
|
|
+const getScaleRatio = () => {
|
|
|
|
+ const designWidth = 1920;
|
|
|
|
+ const designHeight = 1080;
|
|
|
|
+ return Math.min(window.innerWidth / designWidth, window.innerHeight / designHeight);
|
|
|
|
+};
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
- const viewer = new Cesium.Viewer('cesiumContainer', {
|
|
|
|
|
|
+ viewer = new Cesium.Viewer('cesiumContainer', {
|
|
timeline: false,
|
|
timeline: false,
|
|
baseLayer: false,
|
|
baseLayer: false,
|
|
- geocoder: false, // 隐藏搜索框
|
|
|
|
- homeButton: false, // 隐藏Home按钮
|
|
|
|
- sceneModePicker: false, // 隐藏场景模式选择器
|
|
|
|
- navigationHelpButton: false, // 隐藏导航帮助按钮
|
|
|
|
- animation: false, // 隐藏动画控件
|
|
|
|
- fullscreenButton: false, // 隐藏全屏按钮
|
|
|
|
- vrButton: false, // 隐藏VR按钮
|
|
|
|
|
|
+ geocoder: false,
|
|
|
|
+ homeButton: false,
|
|
|
|
+ sceneModePicker: false,
|
|
|
|
+ navigationHelpButton: false,
|
|
|
|
+ animation: false,
|
|
|
|
+ fullscreenButton: false,
|
|
|
|
+ vrButton: false,
|
|
selectionIndicator: false,
|
|
selectionIndicator: false,
|
|
infoBox: false,
|
|
infoBox: false,
|
|
});
|
|
});
|
|
|
|
|
|
- // 定义距离显示条件和缩放属性
|
|
|
|
- const distanceDisplayCondition = new Cesium.DistanceDisplayCondition(
|
|
|
|
- 0, // 最小显示距离(0表示始终显示)
|
|
|
|
- 10000000 // 最大显示距离(1000公里外不显示)
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const pointNearFarScalar = new Cesium.NearFarScalar(
|
|
|
|
- 10000, 1.0, // 近点距离和缩放比例
|
|
|
|
- 1000000, 0.3 // 远点距离和缩放比例
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const labelNearFarScalar = new Cesium.NearFarScalar(
|
|
|
|
- 10000, 1.0, // 近点距离和缩放比例
|
|
|
|
- 400000, 0 // 远点距离和缩放比例(50公里外完全隐藏)
|
|
|
|
- );
|
|
|
|
|
|
+ // 定义距离显示条件和缩放属性(保持不变)
|
|
|
|
+ const distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0, 10000000);
|
|
|
|
+ const pointNearFarScalar = new Cesium.NearFarScalar(10000, 1.0, 1000000, 0.3);
|
|
|
|
+ const labelNearFarScalar = new Cesium.NearFarScalar(10000, 1.0, 400000, 0);
|
|
|
|
|
|
- // 存储实体与数据的映射
|
|
|
|
|
|
+ // 存储实体与数据的映射(保持不变)
|
|
const entityDataMap = new Map();
|
|
const entityDataMap = new Map();
|
|
-
|
|
|
|
- // 遍历数据并添加点和标签
|
|
|
|
JYLData.forEach((item) => {
|
|
JYLData.forEach((item) => {
|
|
const position = Cesium.Cartesian3.fromDegrees(
|
|
const position = Cesium.Cartesian3.fromDegrees(
|
|
- parseFloat(item.LGTD), // 经度
|
|
|
|
- parseFloat(item.LTTD) // 纬度
|
|
|
|
|
|
+ parseFloat(item.LGTD),
|
|
|
|
+ parseFloat(item.LTTD)
|
|
);
|
|
);
|
|
-
|
|
|
|
- // 创建组合了图标和标签的实体
|
|
|
|
const entity = viewer.entities.add({
|
|
const entity = viewer.entities.add({
|
|
position: position,
|
|
position: position,
|
|
billboard: {
|
|
billboard: {
|
|
- image: '/src/assets/icon/blue.png', // 图标路径
|
|
|
|
|
|
+ image: '/src/assets/icon/blue.png',
|
|
scale: 0.4,
|
|
scale: 0.4,
|
|
color: Cesium.Color.YELLOW,
|
|
color: Cesium.Color.YELLOW,
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
@@ -97,18 +89,15 @@ onMounted(() => {
|
|
distanceDisplayCondition: distanceDisplayCondition,
|
|
distanceDisplayCondition: distanceDisplayCondition,
|
|
scaleByDistance: labelNearFarScalar
|
|
scaleByDistance: labelNearFarScalar
|
|
},
|
|
},
|
|
- // 添加一个唯一ID用于识别
|
|
|
|
id: `point-${item.STCD || item.LGTD + '-' + item.LTTD}`,
|
|
id: `point-${item.STCD || item.LGTD + '-' + item.LTTD}`,
|
|
properties: {
|
|
properties: {
|
|
- data: item // 直接将数据附加到实体属性
|
|
|
|
|
|
+ data: item
|
|
}
|
|
}
|
|
});
|
|
});
|
|
-
|
|
|
|
- // 存储实体与数据的映射关系
|
|
|
|
entityDataMap.set(entity.id, item);
|
|
entityDataMap.set(entity.id, item);
|
|
});
|
|
});
|
|
|
|
|
|
- // 天地图影像
|
|
|
|
|
|
+ // 天地图图层(保持不变)
|
|
const tdtLayer = new Cesium.WebMapTileServiceImageryProvider({
|
|
const tdtLayer = new Cesium.WebMapTileServiceImageryProvider({
|
|
url: `http://t0.tianditu.com/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=${TDTTK}`,
|
|
url: `http://t0.tianditu.com/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=${TDTTK}`,
|
|
layer: "tdt",
|
|
layer: "tdt",
|
|
@@ -120,7 +109,6 @@ onMounted(() => {
|
|
});
|
|
});
|
|
viewer.imageryLayers.addImageryProvider(tdtLayer);
|
|
viewer.imageryLayers.addImageryProvider(tdtLayer);
|
|
|
|
|
|
- // 天地图注记
|
|
|
|
const tdtAnnotionLayer = new Cesium.WebMapTileServiceImageryProvider({
|
|
const tdtAnnotionLayer = new Cesium.WebMapTileServiceImageryProvider({
|
|
url: `http://t0.tianditu.com/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=${TDTTK}`,
|
|
url: `http://t0.tianditu.com/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=${TDTTK}`,
|
|
layer: "tdtAnno",
|
|
layer: "tdtAnno",
|
|
@@ -130,11 +118,10 @@ onMounted(() => {
|
|
maximumLevel: 18,
|
|
maximumLevel: 18,
|
|
show: false,
|
|
show: false,
|
|
});
|
|
});
|
|
|
|
+ viewer.imageryLayers.addImageryProvider(tdtAnnotionLayer);
|
|
|
|
|
|
- // 移除默认水印
|
|
|
|
|
|
+ // 初始化视图(保持不变)
|
|
viewer.cesiumWidget.creditContainer.style.display = "none";
|
|
viewer.cesiumWidget.creditContainer.style.display = "none";
|
|
-
|
|
|
|
- // 设置初始镜头位置(太湖)
|
|
|
|
viewer.camera.setView({
|
|
viewer.camera.setView({
|
|
destination: Cesium.Cartesian3.fromDegrees(120.169103, 31.226174, 500000),
|
|
destination: Cesium.Cartesian3.fromDegrees(120.169103, 31.226174, 500000),
|
|
orientation: {
|
|
orientation: {
|
|
@@ -143,64 +130,57 @@ onMounted(() => {
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
- viewer.imageryLayers.addImageryProvider(tdtAnnotionLayer);
|
|
|
|
-
|
|
|
|
- // 添加点击事件处理
|
|
|
|
- handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
|
|
|
|
-
|
|
|
|
-// 在点击处理函数中增强错误处理
|
|
|
|
-handler.setInputAction((click) => {
|
|
|
|
- const pickedObject = viewer.scene.pick(click.position);
|
|
|
|
-
|
|
|
|
- console.log('Picked object:', pickedObject); // 调试用,检查拾取对象
|
|
|
|
-
|
|
|
|
- if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
|
|
|
|
- const entityId = pickedObject.id.id;
|
|
|
|
- const data = entityDataMap.get(entityId) || pickedObject.id.properties?.data?.getValue();
|
|
|
|
-
|
|
|
|
- console.log('Entity ID:', entityId); // 调试用,检查实体ID
|
|
|
|
- console.log('Entity data:', data); // 调试用,检查数据
|
|
|
|
-
|
|
|
|
- if (data) {
|
|
|
|
- // 显示弹框
|
|
|
|
- selectedPoint.value = data;
|
|
|
|
- popupPosition.value = {
|
|
|
|
- x: click.position.x - 100,
|
|
|
|
- y: click.position.y - 150
|
|
|
|
- };
|
|
|
|
|
|
+ // 点击事件处理(核心修改)
|
|
|
|
+ handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
|
+ handler.setInputAction((click) => {
|
|
|
|
+ const scaleRatio = getScaleRatio();
|
|
|
|
+ const correctedX = click.position.x / scaleRatio;
|
|
|
|
+ const correctedY = click.position.y / scaleRatio;
|
|
|
|
+ const pickedObject = viewer.scene.pick(new Cesium.Cartesian2(correctedX, correctedY));
|
|
|
|
+
|
|
|
|
+ if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
|
|
|
|
+ const entityId = pickedObject.id.id;
|
|
|
|
+ const data = entityDataMap.get(entityId) || pickedObject.id.properties?.data?.getValue();
|
|
|
|
|
|
- // 聚焦到选中的点
|
|
|
|
- viewer.flyTo(pickedObject.id, {
|
|
|
|
- offset: new Cesium.HeadingPitchRange(0, -0.5, 1500)
|
|
|
|
- });
|
|
|
|
|
|
+ if (data) {
|
|
|
|
+ selectedPoint.value = data;
|
|
|
|
+ // 关键:计算图标屏幕坐标,固定显示在图标上方
|
|
|
|
+ const entityPosition = viewer.scene.cartesianToCanvasCoordinates(pickedObject.id.position._value);
|
|
|
|
+ if (entityPosition) {
|
|
|
|
+ popupPosition.value = {
|
|
|
|
+ // 水平居中对齐图标
|
|
|
|
+ x: (entityPosition.x / scaleRatio) - 100,
|
|
|
|
+ // 固定显示在图标上方(距离图标底部10px)
|
|
|
|
+ y: (entityPosition.y / scaleRatio) - 130 // 130 = 弹框高度 + 间距
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ viewer.flyTo(pickedObject.id, {
|
|
|
|
+ offset: new Cesium.HeadingPitchRange(0, -0.5, 1500)
|
|
|
|
+ });
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
- console.warn('No data found for clicked entity');
|
|
|
|
|
|
+ selectedPoint.value = null;
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- // 点击空白区域
|
|
|
|
- selectedPoint.value = null;
|
|
|
|
- }
|
|
|
|
-}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
|
|
|
- // 清理函数
|
|
|
|
|
|
+ // 清理函数(移除鼠标移动监听)
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
if (handler) {
|
|
if (handler) {
|
|
handler.destroy();
|
|
handler.destroy();
|
|
handler = null;
|
|
handler = null;
|
|
}
|
|
}
|
|
-
|
|
|
|
if (viewer && !viewer.isDestroyed()) {
|
|
if (viewer && !viewer.isDestroyed()) {
|
|
viewer.destroy();
|
|
viewer.destroy();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
-})
|
|
|
|
|
|
+});
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
.custom-popup {
|
|
.custom-popup {
|
|
position: absolute;
|
|
position: absolute;
|
|
z-index: 1000;
|
|
z-index: 1000;
|
|
- display: none;
|
|
|
|
|
|
+ display: block; /* 始终显示(由v-if控制显隐) */
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -215,7 +195,7 @@ handler.setInputAction((click) => {
|
|
|
|
|
|
.popup-arrow {
|
|
.popup-arrow {
|
|
position: absolute;
|
|
position: absolute;
|
|
- bottom: -10px;
|
|
|
|
|
|
+ bottom: -10px; /* 指向图标 */
|
|
left: 50%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
transform: translateX(-50%);
|
|
width: 0;
|
|
width: 0;
|
|
@@ -225,6 +205,7 @@ handler.setInputAction((click) => {
|
|
border-top: 10px solid white;
|
|
border-top: 10px solid white;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* 其他样式保持不变 */
|
|
.popup-content h3 {
|
|
.popup-content h3 {
|
|
margin-top: 0;
|
|
margin-top: 0;
|
|
margin-bottom: 8px;
|
|
margin-bottom: 8px;
|