添加遮罩效果
This commit is contained in:
+9
-3
@@ -7,14 +7,20 @@ import { RouterView } from 'vue-router'
|
|||||||
import { ElLoading } from 'element-plus'
|
import { ElLoading } from 'element-plus'
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import { useViewerStore } from './stores/useViewerStore';
|
import { useViewerStore } from './stores/useViewerStore';
|
||||||
const loadingInstanve = ElLoading.service({
|
|
||||||
|
|
||||||
|
const loadingOption = {
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
text: '正在加载配置相关资源中...'
|
text: '正在加载配置相关资源中...'
|
||||||
})
|
}
|
||||||
|
|
||||||
|
let loadingInstanve = ElLoading.service(loadingOption)
|
||||||
|
|
||||||
watch(() => useViewerStore().getViewerLoadingCompleted(), (val) => {
|
watch(() => useViewerStore().getViewerLoadingCompleted(), (val) => {
|
||||||
if (val == true) {
|
if (val) {
|
||||||
loadingInstanve.close()
|
loadingInstanve.close()
|
||||||
|
} else {
|
||||||
|
loadingInstanve = ElLoading.service(loadingOption)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils';
|
|||||||
import AdministrativeDivision from './AdministrativeDivision.vue';
|
import AdministrativeDivision from './AdministrativeDivision.vue';
|
||||||
import { useViewerStore } from '@/stores/useViewerStore';
|
import { useViewerStore } from '@/stores/useViewerStore';
|
||||||
import { useLoadingInformationStore } from '@/stores/useLoadingInformation';
|
import { useLoadingInformationStore } from '@/stores/useLoadingInformation';
|
||||||
import Xian from '@/assets/json/XiAn.json'
|
import xiAnGeoJSON from '@/assets/json/XiAn.json'
|
||||||
|
import type { GeoJsonFileType } from '@/types/cesium/GeoJsonFileType';
|
||||||
|
import { Color } from 'cesium';
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
// 初始化为false
|
// 初始化为false
|
||||||
@@ -22,10 +24,20 @@ onBeforeMount(() => {
|
|||||||
useLoadingInformationStore().resetStatue()
|
useLoadingInformationStore().resetStatue()
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async() => {
|
||||||
CesiumUtilsSingleton.initCesiumViewer({
|
await CesiumUtilsSingleton.initCesiumViewer({
|
||||||
containerId: 'map-container'
|
containerId: 'map-container',
|
||||||
})
|
mark: {
|
||||||
|
include: true,
|
||||||
|
geoJson: xiAnGeoJSON as GeoJsonFileType,
|
||||||
|
color: Color.BLACK.withAlpha(0.8),
|
||||||
|
border: {
|
||||||
|
width: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},)
|
||||||
|
|
||||||
|
useViewerStore().setViewerLoadingCompleted(true)
|
||||||
|
|
||||||
// 注册全局点击监听器
|
// 注册全局点击监听器
|
||||||
CesiumUtilsSingleton.clickLayer((pickedObject: any) => {
|
CesiumUtilsSingleton.clickLayer((pickedObject: any) => {
|
||||||
@@ -59,8 +71,6 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新完成状态
|
|
||||||
useViewerStore().setViewerLoadingCompleted(true)
|
|
||||||
CesiumUtilsSingleton.viewToTarget(config.defaultPosition as [number, number, number]);
|
CesiumUtilsSingleton.viewToTarget(config.defaultPosition as [number, number, number]);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ZDBjZjAxOS0wMDhhLTRmZjEtYjNmOC1iNmM2ZmY2ZmQ1N2IiLCJpZCI6MjAxMDI1LCJpYXQiOjE3MTAxNTgxNjJ9.mdbJYEzXQkBnHNqpozz7MvZjJ_X9a3JZRGPA-ytGhLI",
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ZDBjZjAxOS0wMDhhLTRmZjEtYjNmOC1iNmM2ZmY2ZmQ1N2IiLCJpZCI6MjAxMDI1LCJpYXQiOjE3MTAxNTgxNjJ9.mdbJYEzXQkBnHNqpozz7MvZjJ_X9a3JZRGPA-ytGhLI",
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjczZTVlMy1kNDEwLTRhZWItYWM0NS1mNjYxMzJjODMwYTQiLCJpZCI6MzIxMzI2LCJpYXQiOjE3NzU2NDU1OTd9._MPcZQsxK1dGPl8IMVhKHV3PIPu4-TaOUgzsUUOP6WE"
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjczZTVlMy1kNDEwLTRhZWItYWM0NS1mNjYxMzJjODMwYTQiLCJpZCI6MzIxMzI2LCJpYXQiOjE3NzU2NDU1OTd9._MPcZQsxK1dGPl8IMVhKHV3PIPu4-TaOUgzsUUOP6WE"
|
||||||
],
|
],
|
||||||
"defaultPosition": [108.948024, 34.263161, 300000],
|
"defaultPosition": [108.948024, 34.263161, 250000],
|
||||||
"prefix": {
|
"prefix": {
|
||||||
"hiddenDangerPointId": "hidden-danger-point-",
|
"hiddenDangerPointId": "hidden-danger-point-",
|
||||||
"riskPointId": "risk-point-"
|
"riskPointId": "risk-point-"
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import type { Color } from "cesium"
|
||||||
|
import type { GeoJsonFileType } from "./GeoJsonFileType"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cesium 公共配置选项
|
* Cesium 公共配置选项
|
||||||
* 用于初始化时统一配置 Viewer 参数
|
* 用于初始化时统一配置 Viewer 参数
|
||||||
@@ -19,4 +22,25 @@ export interface CesiumInitOptions {
|
|||||||
geocoder?: boolean // 搜索(默认:false)
|
geocoder?: boolean // 搜索(默认:false)
|
||||||
|
|
||||||
sceneMode?: number // 初始场景模式(默认:3D,可选:2D=1, COLUMBUS_VIEW=2)
|
sceneMode?: number // 初始场景模式(默认:3D,可选:2D=1, COLUMBUS_VIEW=2)
|
||||||
|
|
||||||
|
// 遮罩配置
|
||||||
|
mark?: {
|
||||||
|
// 是否包含遮罩,默认false
|
||||||
|
include?: boolean
|
||||||
|
// GeoJSON 数据,如果要突出显示某一区域,就传递改值
|
||||||
|
geoJson?: GeoJsonFileType
|
||||||
|
// 孔属于半球,默认东半球
|
||||||
|
belongingHemisphere?: 'east' | 'west'
|
||||||
|
// 遮罩颜色,默认黑色
|
||||||
|
color?: Color
|
||||||
|
// 边框
|
||||||
|
border?: {
|
||||||
|
// 是否显示边框,默认true
|
||||||
|
show?: boolean
|
||||||
|
// 边框颜色,默认白色
|
||||||
|
color?: Color
|
||||||
|
// 边框宽度,默认1
|
||||||
|
width?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export interface GeoJsonFileType {
|
||||||
|
type: "FeatureCollection";
|
||||||
|
features: {
|
||||||
|
geometry: {
|
||||||
|
coordinates: number[][][][];
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@ import type { EntityOptions } from '@/types/cesium/EntityOptions'
|
|||||||
import type { PrimitiveOptions } from '@/types/cesium/PrimitiveOptions'
|
import type { PrimitiveOptions } from '@/types/cesium/PrimitiveOptions'
|
||||||
import type { LayerConfig } from '@/types/cesium/LayerConfig'
|
import type { LayerConfig } from '@/types/cesium/LayerConfig'
|
||||||
import type { CustomizeGeoJsonDataSource, GeoJsonOptions } from '@/types/cesium/GeoJsonOptions'
|
import type { CustomizeGeoJsonDataSource, GeoJsonOptions } from '@/types/cesium/GeoJsonOptions'
|
||||||
import { Viewer, Entity, DataSource, ImageryLayer, Primitive, BillboardCollection, Cartesian3, ScreenSpaceEventHandler, ScreenSpaceEventType, Cartesian2, SceneTransforms } from 'cesium'
|
import { Viewer, Entity, DataSource, ImageryLayer, Primitive, BillboardCollection, Cartesian3, ScreenSpaceEventHandler, ScreenSpaceEventType, Cartesian2, SceneTransforms, Color } from 'cesium'
|
||||||
import { CesiumViewerManager } from './CesiumViewerManager'
|
import { CesiumViewerManager } from './CesiumViewerManager'
|
||||||
import { EntityManager } from './EntityManager'
|
import { EntityManager } from './EntityManager'
|
||||||
import { PrimitiveManager } from './PrimitiveManager'
|
import { PrimitiveManager } from './PrimitiveManager'
|
||||||
import { LayerManager } from './LayerManager'
|
import { LayerManager } from './LayerManager'
|
||||||
import { GeoJsonManager, type ClearType } from './GeoJsonManager'
|
import { GeoJsonManager, type ClearType } from './GeoJsonManager'
|
||||||
import { CameraController } from './CameraController'
|
import { CameraController } from './CameraController'
|
||||||
|
import type { GeoJsonFileType } from '@/types/cesium/GeoJsonFileType'
|
||||||
|
|
||||||
// 导出 ClearType 类型
|
// 导出 ClearType 类型
|
||||||
export type { ClearType }
|
export type { ClearType }
|
||||||
@@ -36,8 +37,8 @@ export class CesiumUtils {
|
|||||||
* @param type - 底图类型:0=影像图,1=矢量图(默认 0)
|
* @param type - 底图类型:0=影像图,1=矢量图(默认 0)
|
||||||
* @param tdMapToken - 天地图 Token 数组(可选)
|
* @param tdMapToken - 天地图 Token 数组(可选)
|
||||||
*/
|
*/
|
||||||
initCesiumViewer(options: CesiumInitOptions, type: number = 0, tdMapToken?: string[]): void {
|
async initCesiumViewer(options: CesiumInitOptions, type: number = 0, tdMapToken?: string[]): Promise<void> {
|
||||||
this.#viewerManager.initCesiumViewer(options, type, tdMapToken)
|
await this.#viewerManager.initCesiumViewer(options, type, tdMapToken)
|
||||||
|
|
||||||
const viewer = this.#viewerManager.getViewer()
|
const viewer = this.#viewerManager.getViewer()
|
||||||
if (viewer) {
|
if (viewer) {
|
||||||
|
|||||||
@@ -4,6 +4,15 @@ import {
|
|||||||
Ion,
|
Ion,
|
||||||
WebMapTileServiceImageryProvider,
|
WebMapTileServiceImageryProvider,
|
||||||
ImageryProvider,
|
ImageryProvider,
|
||||||
|
PolygonHierarchy,
|
||||||
|
Cartesian3,
|
||||||
|
PolygonGeometry,
|
||||||
|
ArcType,
|
||||||
|
GeometryInstance,
|
||||||
|
Color,
|
||||||
|
Material,
|
||||||
|
MaterialAppearance,
|
||||||
|
GroundPrimitive,
|
||||||
} from 'cesium'
|
} from 'cesium'
|
||||||
import type { CesiumInitOptions } from '@/types/cesium/CesiumInitOptions'
|
import type { CesiumInitOptions } from '@/types/cesium/CesiumInitOptions'
|
||||||
import config from '@/config/config.json'
|
import config from '@/config/config.json'
|
||||||
@@ -50,7 +59,6 @@ export class CesiumViewerManager {
|
|||||||
if (!this.#failedTokens.has(nextIndex)) {
|
if (!this.#failedTokens.has(nextIndex)) {
|
||||||
this.#currentTokenIndex = nextIndex
|
this.#currentTokenIndex = nextIndex
|
||||||
Ion.defaultAccessToken = tokens[nextIndex]
|
Ion.defaultAccessToken = tokens[nextIndex]
|
||||||
console.log(`已切换到 Cesium Ion Token #${nextIndex + 1}`)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +73,7 @@ export class CesiumViewerManager {
|
|||||||
* @param type - 底图类型:0=影像图,1=矢量图(默认 0)
|
* @param type - 底图类型:0=影像图,1=矢量图(默认 0)
|
||||||
* @param tdMapToken - 天地图 Token 数组(可选)
|
* @param tdMapToken - 天地图 Token 数组(可选)
|
||||||
*/
|
*/
|
||||||
initCesiumViewer(options: CesiumInitOptions, type: number = 0, tdMapToken?: string[]): void {
|
async initCesiumViewer(options: CesiumInitOptions, type: number = 0, tdMapToken?: string[]): Promise<void> {
|
||||||
const defaultOptions: CesiumInitOptions = {
|
const defaultOptions: CesiumInitOptions = {
|
||||||
containerId: options.containerId,
|
containerId: options.containerId,
|
||||||
shouldAnimate: true,
|
shouldAnimate: true,
|
||||||
@@ -80,9 +88,32 @@ export class CesiumViewerManager {
|
|||||||
sceneModePicker: false,
|
sceneModePicker: false,
|
||||||
geocoder: false,
|
geocoder: false,
|
||||||
sceneMode: SceneMode.SCENE3D,
|
sceneMode: SceneMode.SCENE3D,
|
||||||
|
mark: {
|
||||||
|
include: false,
|
||||||
|
belongingHemisphere: 'east',
|
||||||
|
color: Color.BLACK,
|
||||||
|
border: {
|
||||||
|
show: true,
|
||||||
|
color: Color.WHITE,
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并选项
|
||||||
|
const finalOptions: CesiumInitOptions = {
|
||||||
|
...defaultOptions,
|
||||||
|
...options,
|
||||||
|
mark: options.mark ? {
|
||||||
|
...defaultOptions.mark,
|
||||||
|
...options.mark,
|
||||||
|
border: options.mark.border ? {
|
||||||
|
...defaultOptions.mark!.border,
|
||||||
|
...options.mark.border
|
||||||
|
} : defaultOptions.mark!.border
|
||||||
|
} : defaultOptions.mark
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalOptions = { ...defaultOptions, ...options }
|
|
||||||
const container = document.getElementById(finalOptions.containerId)
|
const container = document.getElementById(finalOptions.containerId)
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -125,6 +156,11 @@ export class CesiumViewerManager {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.#viewer = viewer
|
this.#viewer = viewer
|
||||||
|
|
||||||
|
// 是否突出显示指定区域
|
||||||
|
if (options.mark?.include) {
|
||||||
|
await this.#highlight(finalOptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,4 +220,131 @@ export class CesiumViewerManager {
|
|||||||
return [vectorProvider]
|
return [vectorProvider]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高亮指定区域
|
||||||
|
* @param options - 高亮选项
|
||||||
|
*/
|
||||||
|
async #highlight(options: CesiumInitOptions): Promise<void> {
|
||||||
|
|
||||||
|
if (!this.#viewer) {
|
||||||
|
throw new Error('请先初始化 Cesium Viewer')
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!options.mark || !options.mark.geoJson) {
|
||||||
|
throw new Error('请提供 GeoJSON 数据')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析边界坐标和孔洞位置
|
||||||
|
const parseCoordinates = () => {
|
||||||
|
const holes: PolygonHierarchy[] = [];
|
||||||
|
const boundaryCoords: number[] = [];
|
||||||
|
const polygons = options.mark?.geoJson!.features[0].geometry.coordinates;
|
||||||
|
|
||||||
|
polygons!.forEach((polygon, index) => {
|
||||||
|
const flatCoords: number[] = [];
|
||||||
|
polygon[0].forEach((point) => {
|
||||||
|
flatCoords.push(point[0], point[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 第一个是多边形外边界
|
||||||
|
if (index === 0) {
|
||||||
|
boundaryCoords.push(...flatCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 坐标反转(用于挖孔)
|
||||||
|
const positions = Cartesian3.fromDegreesArray(flatCoords).reverse();
|
||||||
|
holes.push(new PolygonHierarchy(positions));
|
||||||
|
});
|
||||||
|
|
||||||
|
return { holes, boundaryCoords };
|
||||||
|
};
|
||||||
|
|
||||||
|
const { holes, boundaryCoords } = parseCoordinates();
|
||||||
|
|
||||||
|
// 东西半球标准坐标
|
||||||
|
const westPositions = Cartesian3.fromDegreesArray([
|
||||||
|
-0.00001, 85, -0.00001, -85, -180, -85, -180, 85, -0.00001, 85,
|
||||||
|
]);
|
||||||
|
const eastPositions = Cartesian3.fromDegreesArray([
|
||||||
|
0.00001, 85, 0.00001, -85, 180, -85, 180, 85, 0.00001, 85,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 西半球
|
||||||
|
const westOption = {
|
||||||
|
polygonHierarchy: new PolygonHierarchy(westPositions),
|
||||||
|
arcType: ArcType.GEODESIC,
|
||||||
|
};
|
||||||
|
if (options.mark.belongingHemisphere === 'west') {
|
||||||
|
westOption.polygonHierarchy = new PolygonHierarchy(westPositions, holes);
|
||||||
|
}
|
||||||
|
const westGeometry = new PolygonGeometry(westOption);
|
||||||
|
const westInstance = new GeometryInstance({ geometry: westGeometry });
|
||||||
|
|
||||||
|
// 东半球
|
||||||
|
const eastOption = {
|
||||||
|
polygonHierarchy: new PolygonHierarchy(eastPositions),
|
||||||
|
arcType: ArcType.GEODESIC,
|
||||||
|
}
|
||||||
|
if (options.mark.belongingHemisphere === 'east') {
|
||||||
|
eastOption.polygonHierarchy = new PolygonHierarchy(eastPositions, holes);
|
||||||
|
}
|
||||||
|
const eastGeometry = new PolygonGeometry(eastOption);
|
||||||
|
const eastInstance = new GeometryInstance({ geometry: eastGeometry });
|
||||||
|
|
||||||
|
// 添加遮罩
|
||||||
|
const maskMaterial = new Material({
|
||||||
|
fabric: {
|
||||||
|
type: "Color",
|
||||||
|
uniforms: {
|
||||||
|
color: options.mark.color,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const appearance = new MaterialAppearance({
|
||||||
|
material: maskMaterial,
|
||||||
|
closed: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 合并渲染
|
||||||
|
const globalMask = new GroundPrimitive({
|
||||||
|
geometryInstances: [westInstance, eastInstance],
|
||||||
|
appearance: appearance,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#viewer.scene.primitives.add(globalMask);
|
||||||
|
|
||||||
|
// 添加边界线
|
||||||
|
if (options.mark.border?.show) {
|
||||||
|
const boundaryPositions = Cartesian3.fromDegreesArray(boundaryCoords);
|
||||||
|
// 闭合边界线
|
||||||
|
boundaryPositions.push(boundaryPositions[0]);
|
||||||
|
|
||||||
|
this.#viewer.entities.add({
|
||||||
|
id: 'holeLine',
|
||||||
|
polyline: {
|
||||||
|
positions: boundaryPositions,
|
||||||
|
width: options.mark.border.width,
|
||||||
|
material: options.mark.border.color,
|
||||||
|
clampToGround: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待完成渲染
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const removeListener = this.#viewer!.scene.postRender.addEventListener(() => {
|
||||||
|
if (globalMask.ready) {
|
||||||
|
removeListener();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置超时保护,避免无限等待
|
||||||
|
setTimeout(() => {
|
||||||
|
removeListener();
|
||||||
|
resolve();
|
||||||
|
}, 6000);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -6,9 +6,9 @@
|
|||||||
<img :src="mainLogo" alt="西安应急智慧logo" id="main_logo" />
|
<img :src="mainLogo" alt="西安应急智慧logo" id="main_logo" />
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-list">
|
<div class="nav-list">
|
||||||
<router-link v-for="(item, index) in topNavMap" :key="index"
|
<router-link @click="useViewerStore().setViewerLoadingCompleted(false)"
|
||||||
:to="{ name: item.name, query: item.query }" class="nav-item"
|
v-for="(item, index) in topNavMap" :key="index" :to="{ name: item.name, query: item.query }"
|
||||||
:class="{ 'active': isActive(item.query.identification) }">
|
class="nav-item" :class="{ 'active': isActive(item.query.identification) }">
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user