123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- <template>
- <div id="cesiumContainer" style="height: 100%;width: 100%;"></div>
- <!-- 自定义弹框组件 -->
- <div v-if="selectedPoint" class="custom-popup" :style="{
- left: `${popupPosition.x}px`,
- top: `${popupPosition.y}px`
- }">
- <div class="popup-content">
- <h3>{{ selectedPoint.STNM || '未知点' }}</h3>
- <p><strong>经度:</strong> {{ selectedPoint.LGTD }}</p>
- <p><strong>纬度:</strong> {{ selectedPoint.LTTD }}</p>
- <div class="popup-arrow"></div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onUnmounted } from 'vue'
- import * as Cesium from 'cesium';
- import "cesium/Build/CesiumUnminified/Widgets/widgets.css";
- import JYLData from '@/assets/Data/THJYL.json'
- const TDTTK = "d9e7aa2ad204aba6aeedea6f5ab48ed9";
- const selectedPoint = ref(null);
- const popupPosition = ref({ x: 0, y: 0 });
- 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(async () => {
- viewer = new Cesium.Viewer('cesiumContainer', {
- timeline: false,
- baseLayer: false,
- geocoder: false,
- homeButton: false,
- sceneModePicker: false,
- navigationHelpButton: false,
- animation: false,
- fullscreenButton: false,
- vrButton: false,
- selectionIndicator: false,
- infoBox: false,
- //加载地形效果
- terrainProvider: await Cesium.createWorldTerrainAsync({
- url: 'http://10.8.11.98:9003/terrain/GlobeDEM/layer.json', // 注意修正URL中的locallhost为localhost
- requestVertexNormals: true,
- // requestWaterMask: true,
- })
- });
- try {
- // 1. 请求并解析TMS元数据XML
- const xmlUrl = 'http://10.8.11.98:9003/image/tms/HTHDOM/tilemapresource.xml';
- const response = await fetch(xmlUrl);
- const xmlText = await response.text();
- const parser = new DOMParser();
- const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
- // 2. 从XML中提取关键参数
- const tileFormat = xmlDoc.querySelector('TileFormat').getAttribute('extension'); // 如"png"
- const maxLevel = xmlDoc.querySelectorAll('TileSet').length - 1; // 最大层级(假设层级从0开始)
- const srs = xmlDoc.querySelector('SRS').textContent; // 如"EPSG:3857"
- // 3. 加载TMS影像
- const tmsImagery = new Cesium.UrlTemplateImageryProvider({
- url: `http://10.8.11.98:9003/image/tms/HTHDOM/{z}/{x}/{y}.${tileFormat}`,
- tileWidth: 256, // 从XML的TileFormat中获取width
- tileHeight: 256, // 从XML的TileFormat中获取height
- maximumLevel: maxLevel,
- projection: srs === 'EPSG:3857' ? Cesium.WebMercatorProjection : Cesium.GeographicProjection,
- // 核心:TMS行号反转(解决上下颠倒)
- urlTemplateFunction: (x, y, level) => {
- const tmsY = Math.pow(2, level) - 1 - y; // 反转行号
- return `http://10.8.11.98:9003/image/tms/HTHDOM//${level}/${x}/${tmsY}.${tileFormat}`;
- }
- });
- viewer.imageryLayers.addImageryProvider(tmsImagery);
- console.log('TMS影像加载成功');
- }
- catch (error) {
- console.error('加载TMS影像失败:', error);
- // 检查XML URL是否正确(可能拼写错误,如localhost是否少写字母)
- if (error.message.includes('404')) {
- console.warn('请确认XML路径正确:', xmlUrl);
- }
- }
- // 多块倾斜摄影瓦片集的URL列表(根据实际路径修改)
- const tilesetUrls = [
- "http://localhost:9003/model/TSQ1234/tileset.json",
- "http://localhost:9003/model/SY123/tileset.json",
- "http://10.8.11.98:9003/model/tvhy8PnM8/tileset.json",
- "http://10.8.11.98:9003/model/t9or0ZtaT/tileset.json"
- ];
- // 存储加载成功的瓦片集(可选,用于后续管理)
- const loadedTilesets = [];
- // 批量加载瓦片集(不调整视角)
- async function loadMultipleTilesets() {
- try {
- // 遍历URL列表,并行加载所有瓦片集
- const promises = tilesetUrls.map(async (url, index) => {
- try {
- const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
- maximumScreenSpaceError: 32, // 细节层级控制
- dynamicScreenSpaceError: true,
- skipLevelOfDetail: true,
- maximumConcurrentRequests: 5,
- tileCacheSize: 100
- });
- // 添加到场景
- viewer.scene.primitives.add(tileset);
- loadedTilesets.push(tileset);
- console.log(`第${index + 1}块瓦片集加载完成`);
- return tileset;
- } catch (error) {
- console.error(`第${index + 1}块瓦片集加载失败:`, error);
- return null; // 单块失败不影响其他块
- }
- });
- // 等待所有瓦片集加载(无论成功与否)
- await Promise.all(promises);
- console.log("所有瓦片集加载操作已完成");
- } catch (error) {
- console.error("批量加载逻辑出错:", error);
- }
- }
- // 执行加载(加载后保持原有视口)
- loadMultipleTilesets();
- // 定义距离显示条件和缩放属性(保持不变)
- 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();
- JYLData.forEach((item) => {
- const position = Cesium.Cartesian3.fromDegrees(
- parseFloat(item.LGTD),
- parseFloat(item.LTTD)
- );
- const entity = viewer.entities.add({
- position: position,
- billboard: {
- image: '/src/assets/icon/blue.png',
- scale: 0.4,
- color: Cesium.Color.YELLOW,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- distanceDisplayCondition: distanceDisplayCondition,
- scaleByDistance: pointNearFarScalar
- },
- label: {
- text: item.STNM || '未知点',
- font: '25px 微软雅黑',
- fillColor: Cesium.Color.WHITE,
- backgroundColor: new Cesium.Color(0.1, 0.1, 0.1, 0.7),
- backgroundPadding: new Cesium.Cartesian2(8, 4),
- showBackground: true,
- cornerRadius: 4,
- outlineColor: Cesium.Color.BLACK,
- outlineWidth: 1,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- pixelOffset: new Cesium.Cartesian2(0, -32),
- scale: 1.0,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- distanceDisplayCondition: distanceDisplayCondition,
- scaleByDistance: labelNearFarScalar
- },
- id: `point-${item.STCD || item.LGTD + '-' + item.LTTD}`,
- properties: {
- data: item
- }
- });
- entityDataMap.set(entity.id, item);
- });
- // 天地图图层(保持不变)
- const tdtLayer = new Cesium.WebMapTileServiceImageryProvider({
- url: `https://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",
- style: "default",
- format: "image/jpeg",
- tileMatrixSetID: "w",
- maximumLevel: 16,
- show: true,
- });
- viewer.imageryLayers.addImageryProvider(tdtLayer);
- 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}`,
- layer: "tdtAnno",
- style: "default",
- format: "image/jpeg",
- tileMatrixSetID: "w",
- maximumLevel: 18,
- show: false,
- });
- viewer.imageryLayers.addImageryProvider(tdtAnnotionLayer);
- // 初始化视图(保持不变)
- viewer.cesiumWidget.creditContainer.style.display = "none";
- viewer.camera.setView({
- destination: Cesium.Cartesian3.fromDegrees(120.169103, 31.226174, 500000),
- orientation: {
- heading: Cesium.Math.toRadians(0),
- pitch: Cesium.Math.toRadians(-90),
- },
- });
- // 点击事件处理(核心修改)
- 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();
- 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 {
- selectedPoint.value = null;
- }
- }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
- // 清理函数(移除鼠标移动监听)
- onUnmounted(() => {
- if (handler) {
- handler.destroy();
- handler = null;
- }
- if (viewer && !viewer.isDestroyed()) {
- viewer.destroy();
- }
- });
- });
- </script>
- <style scoped>
- .custom-popup {
- position: absolute;
- z-index: 1000;
- display: block;
- /* 始终显示(由v-if控制显隐) */
- pointer-events: none;
- }
- .popup-content {
- background-color: white;
- border-radius: 5px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
- padding: 10px 15px;
- width: 200px;
- pointer-events: all;
- }
- .popup-arrow {
- position: absolute;
- bottom: -10px;
- /* 指向图标 */
- left: 50%;
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 10px solid transparent;
- border-right: 10px solid transparent;
- border-top: 10px solid white;
- }
- /* 其他样式保持不变 */
- .popup-content h3 {
- margin-top: 0;
- margin-bottom: 8px;
- color: #333;
- }
- .popup-content p {
- margin: 5px 0;
- color: #666;
- font-size: 14px;
- }
- </style>
|