| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, onUnmounted, ref, watch } from "vue" |
| | | import { createPoint, initHandler, initView, addTileset, addTerrain } from "@/utils/map.js" |
| | | import { loadAreaPolygon } from "@/utils/area.js" |
| | | import { loadAreaPolygonAll } from "@/utils/area_all.js" |
| | | import { isVisibleDistance } from "@/utils/customEntity" |
| | | import { getDistrictCount, getDistrictCountByCity } from "@/api/index" |
| | | import { useRoute } from "vue-router" |
| | | const route = useRoute() |
| | | let handler = null |
| | | function initMap () { |
| | | window.Cesium = SmartEarth.Cesium |
| | | window.earthCtrl = new SmartEarth.EarthCtrl("gis-view") |
| | | import { onMounted, onUnmounted, ref, watch } from "vue"; |
| | | import { |
| | | createPoint, |
| | | initHandler, |
| | | initView, |
| | | addTileset, |
| | | addTerrain, |
| | | } from "@/utils/map.js"; |
| | | import { loadAreaPolygon } from "@/utils/area.js"; |
| | | import { loadAreaPolygonAll } from "@/utils/area_all.js"; |
| | | import { isVisibleDistance } from "@/utils/customEntity"; |
| | | import { getDistrictCount, getDistrictCountByCity } from "@/api/index"; |
| | | import { useRoute } from "vue-router"; |
| | | import { EventBus } from "@/eventBus"; // 引入事件总线 |
| | | |
| | | const route = useRoute(); |
| | | let handler = null; |
| | | function initMap() { |
| | | window.Cesium = SmartEarth.Cesium; |
| | | window.earthCtrl = new SmartEarth.EarthCtrl("gis-view"); |
| | | window.viewer = earthCtrl.viewer; |
| | | |
| | | // 1. 设置初始时间 |
| | | const date = new Date(2025, 3, 11, 12, 0, 0, 0); |
| | | const julianDate = SmartEarth.Cesium.JulianDate.fromDate(date); |
| | | // earthCtrl.viewer.clock.currentTime = julianDate; |
| | | |
| | | // 2. 配置时钟选项,禁止自动推进时间 |
| | | earthCtrl.viewer.clockViewModel.shouldAnimate = false; // 禁用动画 |
| | | earthCtrl.viewer.clockViewModel.clockRange = |
| | | SmartEarth.Cesium.ClockRange.CLAMPED; // 限制时间范围 |
| | | earthCtrl.viewer.clockViewModel.multiplier = 0; // 设置时间推进速度为0 |
| | | |
| | | // 3. 设置当前时间并锁定 |
| | | earthCtrl.viewer.clock.currentTime = julianDate; |
| | | |
| | | //显示fps |
| | | earthCtrl.showFPS = true |
| | | earthCtrl.showFPS = true; |
| | | earthCtrl.factory.createImageryLayer({ |
| | | sourceType: "mapworld", |
| | | url: "https://t0.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=3ec79cf7a9dcc6bb18411a5414b148cb", |
| | | url: "http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=7eb11c0c503429878691ac917238f87f", |
| | | layers: "tdtBasicLayer", |
| | | style: "default", |
| | | format: "image/jpeg", |
| | | maximumLevel: 18, |
| | | layer: "", |
| | | tileMatrixSetID: "", |
| | | }) |
| | | }); |
| | | // 关闭地形深度检测 |
| | | // viewer.scene.globe.depthTestAgainstTerrain = false; |
| | | } |
| | | |
| | | function addCityPolygon () { |
| | | const url = `/json/110000.geo.json` |
| | | const MULTIPOLYGON_COORDS = ref([]); |
| | | let previousEntities = []; // 用于存储之前创建的实体 |
| | | const flyToHeight = ref(null); |
| | | |
| | | // 监听新建方案选择的区域范围 |
| | | EventBus.on("select-geom", ({ geom, flyHeight }) => { |
| | | flyToHeight.value = flyHeight; |
| | | const coordsStr = geom |
| | | .replace("MULTIPOLYGON(((", "") // 去掉开头 |
| | | .replace(")))", ""); // 去掉结尾 |
| | | |
| | | // 分割成 ["lon lat", "lon lat", ...] |
| | | const coordPairs = coordsStr.split(","); |
| | | |
| | | // 转换为 [[[lon, lat], [lon, lat], ...]] 格式 |
| | | MULTIPOLYGON_COORDS.value = [ |
| | | coordPairs.map((pair) => { |
| | | const [lon, lat] = pair.trim().split(" ").map(Number); |
| | | return [lon, lat]; |
| | | }), |
| | | ]; |
| | | |
| | | // 检查是否是空多边形,如果是空,则去掉面片颜色,并飞回初始位置 |
| | | if (geom === "MULTIPOLYGON EMPTY") { |
| | | clearPreviousEntities(); |
| | | flyToHomeView(); |
| | | return; // 不执行后续操作 |
| | | } |
| | | |
| | | // 清除之前的所有实体 |
| | | clearPreviousEntities(); |
| | | // 选中区域标色 |
| | | addCustomPolygon(); |
| | | }); |
| | | |
| | | // 清除之前的所有实体 |
| | | function clearPreviousEntities() { |
| | | previousEntities.forEach((entity) => { |
| | | viewer.entities.remove(entity); |
| | | }); |
| | | previousEntities = []; |
| | | } |
| | | |
| | | // 计算选中区域中心点 |
| | | function calculateCenter(coordinates) { |
| | | const lons = coordinates.flat().map((coord) => coord[0]); |
| | | const lats = coordinates.flat().map((coord) => coord[1]); |
| | | return { |
| | | lon: (Math.min(...lons) + Math.max(...lons)) / 2, |
| | | lat: (Math.min(...lats) + Math.max(...lats)) / 2, |
| | | }; |
| | | } |
| | | |
| | | function convertToGeoJson(coords) { |
| | | return { |
| | | type: "Feature", |
| | | id: 0, |
| | | bbox: calculateCenter(MULTIPOLYGON_COORDS.value), // 可选 |
| | | properties: { |
| | | center: calculateCenter(MULTIPOLYGON_COORDS.value), // 第一个点作为 center |
| | | centroid: MULTIPOLYGON_COORDS.value[0][0], // 可选 |
| | | level: "district", |
| | | code: 123456, |
| | | districtCount: 0, |
| | | }, |
| | | geometry: { |
| | | type: "MultiPolygon", |
| | | coordinates: [coords], // 包装成 MultiPolygon |
| | | }, |
| | | }; |
| | | } |
| | | |
| | | function addCustomPolygon() { |
| | | // 将 MULTIPOLYGON_COORDS.value 转换为 GeoJSON 格式 |
| | | const geoJson = convertToGeoJson(MULTIPOLYGON_COORDS.value); |
| | | const center = geoJson.properties.center; |
| | | |
| | | // 创建多边形实体 |
| | | const polygonEntity = viewer.entities.add({ |
| | | // name: "自定义区域", |
| | | polygon: { |
| | | hierarchy: Cesium.Cartesian3.fromDegreesArray( |
| | | geoJson.geometry.coordinates[0][0].flat() |
| | | ), |
| | | material: Cesium.Color.RED.withAlpha(0.3), // 半透明红色填充 |
| | | outline: true, |
| | | outlineColor: Cesium.Color.RED, // 红色边框 |
| | | outlineWidth: 5, |
| | | clampToGround: true, // 贴地显示 |
| | | }, |
| | | }); |
| | | previousEntities.push(polygonEntity); |
| | | |
| | | // 飞向中心点 |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees( |
| | | center.lon, |
| | | center.lat, |
| | | flyToHeight.value |
| | | ), // 提高到 100000米高度 |
| | | orientation: { |
| | | heading: Cesium.Math.toRadians(0), // 正北方向 |
| | | pitch: Cesium.Math.toRadians(-90), // 向下倾斜90度(垂直俯视) |
| | | roll: 0.0, |
| | | }, |
| | | }); |
| | | |
| | | console.log( |
| | | flyToHeight.value, |
| | | "flyToHeight.value flyToHeight.value flyToHeight.value " |
| | | ); |
| | | } |
| | | |
| | | EventBus.on("close-selectArea", () => { |
| | | clearPreviousEntities(); |
| | | flyToHomeView(); |
| | | }); |
| | | |
| | | function addCityPolygon() { |
| | | const url = `/json/110000.geo.json`; |
| | | let wallLayer = earthCtrl.factory.createTrailWallLayer({ |
| | | url: "/json/110000.geojson", |
| | | color: "LIGHTSTEELBLUE", //颜色 |
| | | height: 2000, //高度 |
| | | speed: 3, |
| | | }) |
| | | }); |
| | | const dataSourcePromise = Cesium.GeoJsonDataSource.load(url, { |
| | | clampToGround: true, |
| | | }) |
| | | }); |
| | | return dataSourcePromise.then(function (dataSource) { |
| | | viewer.dataSources.add(dataSource) |
| | | const polygonEntity = dataSource.entities.values |
| | | console.log("polygonEntity", polygonEntity) |
| | | const distanceDisplayCondition = new Cesium.DistanceDisplayCondition(1000, 50000000) |
| | | polygonEntity.forEach(entity => { |
| | | viewer.dataSources.add(dataSource); |
| | | // 所有的数据 |
| | | const polygonEntity = dataSource.entities.values; |
| | | const distanceDisplayCondition = new Cesium.DistanceDisplayCondition( |
| | | 1000, |
| | | 50000000 |
| | | ); |
| | | polygonEntity.forEach((entity) => { |
| | | // console.log("entity", entity) |
| | | |
| | | entity.polygon.material = new Cesium.ColorMaterialProperty( |
| | |
| | | // minimumAlpha: 0.2, |
| | | // maximumAlpha: 0.9, |
| | | // }) |
| | | ) |
| | | ); |
| | | |
| | | entity.polygon.distanceDisplayCondition = distanceDisplayCondition |
| | | entity.polygon.distanceDisplayCondition = distanceDisplayCondition; |
| | | |
| | | const properties = entity.properties |
| | | const center = properties.centroid.getValue() |
| | | const fullname = properties.fullname.getValue() |
| | | const districtCount = properties.districtCount.getValue() || 0 |
| | | const properties = entity.properties; |
| | | const center = properties.centroid.getValue(); |
| | | const fullname = properties.fullname.getValue(); |
| | | const districtCount = properties.districtCount.getValue() || 0; |
| | | |
| | | const position = Cesium.Cartesian3.fromDegrees(center[0], center[1]) |
| | | const positions = entity.polygon.hierarchy._value.positions |
| | | const position = Cesium.Cartesian3.fromDegrees(center[0], center[1]); |
| | | const positions = entity.polygon.hierarchy._value.positions; |
| | | |
| | | entity.position = position |
| | | entity.position = position; |
| | | // 判断是否为东城区或西城区 |
| | | let labelText = fullname || "默认标签" |
| | | let labelText = fullname || "默认标签"; |
| | | if (fullname === "东城区" || fullname === "西城区") { |
| | | // 将文本拆分为竖列 |
| | | labelText = fullname.split("").join("\n") |
| | | labelText = fullname.split("").join("\n"); |
| | | } |
| | | entity.label = { |
| | | // 文本。支持显式换行符“ \ n” |
| | |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | // 是否显示 |
| | | show: true, |
| | | } |
| | | }; |
| | | entity.polyline = { |
| | | positions: positions, |
| | | width: 5, |
| | |
| | | ), |
| | | clampToGround: true, |
| | | distanceDisplayCondition: distanceDisplayCondition, |
| | | } |
| | | }; |
| | | |
| | | viewer.entities.add(entity) |
| | | }) |
| | | viewer.entities.add(entity); |
| | | }); |
| | | |
| | | // 获取 GeoJSON 中的第一个 Polygon feature |
| | | }) |
| | | }); |
| | | } |
| | | |
| | | function flyToHomeView () { |
| | | function flyToHomeView() { |
| | | const view = { |
| | | destination: { |
| | | x: -2355432.569004413, |
| | |
| | | roll: 0.00031421159527500464, |
| | | heading: 6.140424766644804, |
| | | }, |
| | | } |
| | | viewer.scene.camera.flyTo(view) |
| | | }; |
| | | viewer.scene.camera.flyTo(view); |
| | | } |
| | | |
| | | let htmlEntityList = [] |
| | | function initDistrictCount () { |
| | | getDistrictCount().then(res => { |
| | | res.data.forEach(item => { |
| | | const { districtName, count, lat, lon } = item |
| | | item.name = `${item.districtName}\n${item.count}` |
| | | item.longitude = item.lon |
| | | item.latitude = item.lat |
| | | item.showBillboard = false |
| | | item.showLabel = true |
| | | let htmlEntityList = []; |
| | | function initDistrictCount() { |
| | | getDistrictCount().then((res) => { |
| | | res.data.forEach((item) => { |
| | | const { districtName, count, lat, lon } = item; |
| | | item.name = `${item.districtName}\n${item.count}`; |
| | | item.longitude = item.lon; |
| | | item.latitude = item.lat; |
| | | item.showBillboard = false; |
| | | item.showLabel = true; |
| | | item.label = { |
| | | text: item.name, |
| | | backgroundColor: SmartEarth.Cesium.Color.SKYBLUE.withAlpha(0.8), |
| | | font: "14pt Source Han Sans CN", |
| | | fillColor: SmartEarth.Cesium.Color.WHITE, |
| | | showBackground: true, |
| | | } |
| | | }; |
| | | // createPoint(item) |
| | | const html = earthCtrl.view.createScreenDialog({ |
| | | html: ` |
| | |
| | | lon: item.lon, |
| | | lat: item.lat, |
| | | height: 0, |
| | | }) |
| | | html.maxVisibleDistance = 69000 |
| | | html.minVisibleDistance = 20000 |
| | | }); |
| | | html.maxVisibleDistance = 69000; |
| | | html.minVisibleDistance = 20000; |
| | | html.element.addEventListener("click", () => { |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, 12000), |
| | | destination: Cesium.Cartesian3.fromDegrees( |
| | | item.longitude, |
| | | item.latitude, |
| | | 12000 |
| | | ), |
| | | orientation: { |
| | | pitch: Cesium.Math.toRadians(-90), |
| | | heading: Cesium.Math.toRadians(0), |
| | | roll: 0, |
| | | }, |
| | | duration: 2, |
| | | }) |
| | | }) |
| | | }); |
| | | }); |
| | | |
| | | htmlEntityList.push(html) |
| | | }) |
| | | }) |
| | | htmlEntityList.push(html); |
| | | }); |
| | | }); |
| | | } |
| | | function initDistrictCountByCity () { |
| | | getDistrictCountByCity().then(res => { |
| | | res.data.forEach(item => { |
| | | const { districtName, count, lat, lon } = item |
| | | item.name = `${item.districtName}\n${item.count}` |
| | | item.longitude = item.lon |
| | | item.latitude = item.lat |
| | | item.showBillboard = false |
| | | item.showLabel = true |
| | | function initDistrictCountByCity() { |
| | | getDistrictCountByCity().then((res) => { |
| | | res.data.forEach((item) => { |
| | | const { districtName, count, lat, lon } = item; |
| | | item.name = `${item.districtName}\n${item.count}`; |
| | | item.longitude = item.lon; |
| | | item.latitude = item.lat; |
| | | item.showBillboard = false; |
| | | item.showLabel = true; |
| | | item.label = { |
| | | text: item.name, |
| | | backgroundColor: SmartEarth.Cesium.Color.SKYBLUE.withAlpha(0.8), |
| | | font: "14pt Source Han Sans CN", |
| | | fillColor: SmartEarth.Cesium.Color.WHITE, |
| | | showBackground: true, |
| | | } |
| | | }; |
| | | // createPoint(item) |
| | | const html = earthCtrl.view.createScreenDialog({ |
| | | html: ` |
| | |
| | | lon: item.lon, |
| | | lat: item.lat, |
| | | height: 0, |
| | | }) |
| | | html.maxVisibleDistance = 50000000 |
| | | html.minVisibleDistance = 70000 |
| | | }); |
| | | html.maxVisibleDistance = 50000000; |
| | | html.minVisibleDistance = 70000; |
| | | html.element.addEventListener("click", () => { |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, 45000), |
| | | destination: Cesium.Cartesian3.fromDegrees( |
| | | item.longitude, |
| | | item.latitude, |
| | | 45000 |
| | | ), |
| | | orientation: { |
| | | pitch: Cesium.Math.toRadians(-90), |
| | | heading: Cesium.Math.toRadians(0), |
| | | roll: 0, |
| | | }, |
| | | duration: 2, |
| | | }) |
| | | }) |
| | | }); |
| | | }); |
| | | |
| | | htmlEntityList.push(html) |
| | | }) |
| | | }) |
| | | htmlEntityList.push(html); |
| | | }); |
| | | }); |
| | | } |
| | | watch( |
| | | () => route.fullPath, |
| | | val => { |
| | | (val) => { |
| | | if (val != "/") { |
| | | // clusterLayer.dataSource.show = false |
| | | htmlEntityList.forEach(item => { |
| | | item.show = false |
| | | }) |
| | | removeCameraChange() |
| | | htmlEntityList.forEach((item) => { |
| | | item.show = false; |
| | | }); |
| | | removeCameraChange(); |
| | | } else { |
| | | handleCameraChange() |
| | | handleCameraChange(); |
| | | // clusterLayer.dataSource.show = true |
| | | htmlEntityList.forEach(item => { |
| | | item.show = isVisibleDistance(item.minVisibleDistance, item.maxVisibleDistance) |
| | | }) |
| | | htmlEntityList.forEach((item) => { |
| | | item.show = isVisibleDistance( |
| | | item.minVisibleDistance, |
| | | item.maxVisibleDistance |
| | | ); |
| | | }); |
| | | } |
| | | } |
| | | ) |
| | | function handleCameraChange () { |
| | | viewer.camera.changed.addEventListener(toggleHtmlLayerByVisibleDistance) |
| | | ); |
| | | function handleCameraChange() { |
| | | viewer.camera.changed.addEventListener(toggleHtmlLayerByVisibleDistance); |
| | | } |
| | | function removeCameraChange () { |
| | | viewer.camera.changed.removeEventListener(toggleHtmlLayerByVisibleDistance) |
| | | function removeCameraChange() { |
| | | viewer.camera.changed.removeEventListener(toggleHtmlLayerByVisibleDistance); |
| | | } |
| | | let cameraChangeTimer = null |
| | | let cameraChangeTimer = null; |
| | | |
| | | function toggleHtmlLayerByVisibleDistance () { |
| | | function toggleHtmlLayerByVisibleDistance() { |
| | | if (cameraChangeTimer) { |
| | | clearTimeout(cameraChangeTimer) |
| | | cameraChangeTimer = null |
| | | return |
| | | clearTimeout(cameraChangeTimer); |
| | | cameraChangeTimer = null; |
| | | return; |
| | | } |
| | | cameraChangeTimer = setTimeout(() => { |
| | | htmlEntityList.forEach(item => { |
| | | item.show = isVisibleDistance(item.minVisibleDistance, item.maxVisibleDistance) |
| | | }) |
| | | }, 100) |
| | | htmlEntityList.forEach((item) => { |
| | | item.show = isVisibleDistance( |
| | | item.minVisibleDistance, |
| | | item.maxVisibleDistance |
| | | ); |
| | | }); |
| | | }, 100); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | initMap() |
| | | addCityPolygon() |
| | | initHandler() |
| | | initMap(); |
| | | // 在你的初始化代码中调用这个函数 |
| | | addCityPolygon(); |
| | | initHandler(); |
| | | // initView() |
| | | loadAreaPolygon("/json/nsl_area.geojson") |
| | | loadAreaPolygonAll("/json/geometry.json", true) |
| | | flyToHomeView() |
| | | initDistrictCount() |
| | | initDistrictCountByCity() |
| | | handleCameraChange() |
| | | loadAreaPolygon("/json/nsl_area.geojson"); |
| | | loadAreaPolygonAll("/json/geometry.json", true); |
| | | flyToHomeView(); |
| | | initDistrictCount(); |
| | | initDistrictCountByCity(); |
| | | handleCameraChange(); |
| | | // 设置 billboard 点击事件 |
| | | }) |
| | | }); |
| | | onUnmounted(() => { |
| | | removeCameraChange() |
| | | }) |
| | | removeCameraChange(); |
| | | EventBus.off("select-geom"); |
| | | EventBus.off("close-selectArea"); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |