UNPKG

vesium

Version:

Vue component and composition-api library for Cesium.

1,427 lines (1,387 loc) 55.9 kB
//#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion const __vueuse_core = __toESM(require("@vueuse/core")); const cesium = __toESM(require("cesium")); const vue = __toESM(require("vue")); //#region createViewer/index.ts /** * @internal */ const CREATE_VIEWER_INJECTION_KEY = Symbol("CREATE_VIEWER_INJECTION_KEY"); /** * @internal */ const CREATE_VIEWER_COLLECTION = /* @__PURE__ */ new WeakMap(); function createViewer(...args) { const viewer = (0, vue.shallowRef)(); const readonlyViewer = (0, vue.shallowReadonly)(viewer); (0, vue.provide)(CREATE_VIEWER_INJECTION_KEY, readonlyViewer); const scope = (0, vue.getCurrentScope)(); if (scope) CREATE_VIEWER_COLLECTION.set(scope, readonlyViewer); const canvas = (0, vue.computed)(() => viewer.value?.canvas); (0, __vueuse_core.useMutationObserver)(document?.body, () => { if (canvas.value && !document?.body.contains(canvas.value)) viewer.value = void 0; }, { childList: true, subtree: true }); (0, vue.watchEffect)((onCleanup) => { const [arg1, arg2] = args; const value = (0, vue.toRaw)((0, vue.toValue)(arg1)); if (value instanceof cesium.Viewer) viewer.value = (0, vue.markRaw)(value); else if (value) { const element = value; const options = arg2; viewer.value = new cesium.Viewer(element, options); onCleanup(() => !viewer.value?.isDestroyed() && viewer.value?.destroy()); } else viewer.value = void 0; }); (0, __vueuse_core.tryOnScopeDispose)(() => { viewer.value = void 0; }); return (0, vue.computed)(() => { return viewer.value?.isDestroyed() ? void 0 : viewer.value; }); } //#endregion //#region utils/arrayDiff.ts /** * 计算两个数组的差异,返回新增和删除的元素 */ function arrayDiff(list, oldList) { const oldListSet = new Set(oldList); const added = list.filter((obj) => !oldListSet.has(obj)); const newListSet = new Set(list); const removed = oldList?.filter((obj) => !newListSet.has(obj)) ?? []; return { added, removed }; } //#endregion //#region utils/canvasCoordToCartesian.ts /** * Convert canvas coordinates to Cartesian coordinates * * @param canvasCoord Canvas coordinates * @param scene Cesium.Scene instance * @param mode optional values are 'pickPosition' | 'globePick' | 'auto' | 'noHeight' @default 'auto' * * `pickPosition`: Use scene.pickPosition for conversion, which can be used for picking models, oblique photography, etc. * However, if depth detection is not enabled (globe.depthTestAgainstTerrain=false), picking terrain or inaccurate issues may occur * * `globePick`: Use camera.getPickRay for conversion, which cannot be used for picking models or oblique photography, * but can be used for picking terrain. If terrain does not exist, the picked elevation is 0 * * `auto`: Automatically determine which picking content to return * * Calculation speed comparison: globePick > auto >= pickPosition */ function canvasCoordToCartesian(canvasCoord, scene, mode = "auto") { if (mode === "pickPosition") return scene.pickPosition(canvasCoord); else if (mode === "globePick") { const ray = scene.camera.getPickRay(canvasCoord); return ray && scene.globe.pick(ray, scene); } else { if (scene.globe.depthTestAgainstTerrain) return scene.pickPosition(canvasCoord); const position1 = scene.pickPosition(canvasCoord); const ray = scene.camera.getPickRay(canvasCoord); const position2 = ray && scene.globe.pick(ray, scene); if (!position1) return position2; const height1 = (position1 && cesium.Ellipsoid.WGS84.cartesianToCartographic(position1).height) ?? 0; const height2 = (position2 && cesium.Ellipsoid.WGS84.cartesianToCartographic(position2).height) ?? 0; return height1 < height2 ? position1 : position2; } } //#endregion //#region utils/cartesianToCanvasCoord.ts /** * Convert Cartesian coordinates to canvas coordinates * * @param position Cartesian coordinates * @param scene Cesium.Scene instance */ function cartesianToCanvasCoord(position, scene) { return scene.cartesianToCanvasCoordinates(position); } //#endregion //#region utils/is.ts const toString = Object.prototype.toString; function isDef(val) { return typeof val !== "undefined"; } function isBoolean(val) { return typeof val === "boolean"; } function isFunction(val) { return typeof val === "function"; } function isNumber(val) { return typeof val === "number"; } function isString(val) { return typeof val === "string"; } function isObject(val) { return toString.call(val) === "[object Object]"; } function isWindow(val) { return typeof window !== "undefined" && toString.call(val) === "[object Window]"; } function isPromise(val) { return !!val && (typeof val === "object" || typeof val === "function") && typeof val.then === "function"; } function isElement(val) { return !!(val && val.nodeName && val.nodeType === 1); } const isArray = Array.isArray; function isBase64(val) { const reg = /^\s*data:([a-z]+\/[\d+.a-z-]+(;[a-z-]+=[\da-z-]+)?)?(;base64)?,([\s\w!$%&'()*+,./:;=?@~-]*?)\s*$/i; return reg.test(val); } function assertError(condition, error) { if (condition) throw new Error(error); } //#endregion //#region utils/cesiumEquals.ts /** * Determines if two Cesium objects are equal. * * This function not only judges whether the instances are equal, * but also judges the equals method in the example. * * @param left The first Cesium object * @param right The second Cesium object * @returns Returns true if the two Cesium objects are equal, otherwise false */ function cesiumEquals(left, right) { return left === right || isFunction(left?.equals) && left.equals(right) || isFunction(right?.equals) && right.equals(left); } //#endregion //#region utils/toCoord.ts /** * Converts coordinates to an array or object in the specified format. * * @param position The coordinate to be converted, which can be a Cartesian3, Cartographic, array, or object. * @param options Conversion options, including conversion type and whether to include altitude information. * @returns The converted coordinate, which may be an array or object. If the input position is empty, undefined is returned. * * @template T Conversion type, optional values are 'Array' or 'Object', @default 'Array'. * @template Alt Whether to include altitude information, default is false */ function toCoord(position, options = {}) { if (!position) return void 0; const { type = "Array", alt = false } = options; let longitude, latitude, height; if (position instanceof cesium.Cartesian3) { const cartographic = cesium.Ellipsoid.WGS84.cartesianToCartographic(position); longitude = cesium.Math.toDegrees(cartographic.longitude); latitude = cesium.Math.toDegrees(cartographic.latitude); height = cartographic.height; } else if (position instanceof cesium.Cartographic) { const cartographic = position; longitude = cesium.Math.toDegrees(cartographic.longitude); latitude = cesium.Math.toDegrees(cartographic.latitude); height = cartographic.height; } else if (Array.isArray(position)) { longitude = cesium.Math.toDegrees(position[0]); latitude = cesium.Math.toDegrees(position[1]); height = position[2]; } else { longitude = position.longitude; latitude = position.latitude; height = position.height; } if (type === "Array") return alt ? [ longitude, latitude, height ] : [longitude, latitude]; else return alt ? { longitude, latitude, height } : { longitude, latitude }; } //#endregion //#region utils/convertDMS.ts /** * Convert degrees to DMS (Degrees Minutes Seconds) format string * * @param degrees The angle value * @param precision The number of decimal places to retain for the seconds, defaults to 3 * @returns A DMS formatted string in the format: degrees° minutes′ seconds″ */ function dmsEncode(degrees, precision = 3) { const str = `${degrees}`; let i = str.indexOf("."); const d = i < 0 ? str : str.slice(0, Math.max(0, i)); let m = "0"; let s = "0"; if (i > 0) { m = `0${str.slice(Math.max(0, i))}`; m = `${+m * 60}`; i = m.indexOf("."); if (i > 0) { s = `0${m.slice(Math.max(0, i))}`; m = m.slice(0, Math.max(0, i)); s = `${+s * 60}`; i = s.indexOf("."); s = s.slice(0, Math.max(0, i + 4)); s = (+s).toFixed(precision); } } return `${Math.abs(+d)}°${+m}′${+s}″`; } /** * Decode a DMS (Degrees Minutes Seconds) formatted string to a decimal angle value * * @param dmsCode DMS formatted string, e.g. "120°30′45″N" * @returns The decoded decimal angle value, or 0 if decoding fails */ function dmsDecode(dmsCode) { const [dd, msStr] = dmsCode.split("°") ?? []; const [mm, sStr] = msStr?.split("′") ?? []; const ss = sStr?.split("″")[0]; const d = Number(dd) || 0; const m = (Number(mm) || 0) / 60; const s = (Number(ss) || 0) / 60 / 60; const degrees = d + m + s; if (degrees === 0) return 0; else { let res = degrees; if ([ "W", "w", "S", "s" ].includes(dmsCode[dmsCode.length - 1])) res = -res; return res; } } /** * Convert latitude and longitude coordinates to degrees-minutes-seconds format * * @param position The latitude and longitude coordinates * @param precision The number of decimal places to retain for 'seconds', default is 3 * @returns Returns the coordinates in degrees-minutes-seconds format, or undefined if the conversion fails */ function degreesToDms(position, precision = 3) { const coord = toCoord(position, { alt: true }); if (!coord) return; const [longitude, latitude, height] = coord; const x = dmsEncode(longitude, precision); const y = dmsEncode(latitude, precision); return [ `${x}${longitude > 0 ? "E" : "W"}`, `${y}${latitude > 0 ? "N" : "S"}`, height ]; } /** * Convert DMS (Degrees Minutes Seconds) format to decimal degrees for latitude and longitude coordinates * * @param dms The latitude or longitude coordinate in DMS format * @returns Returns the coordinate in decimal degrees format, or undefined if the conversion fails */ function dmsToDegrees(dms) { const [x, y, height] = dms; const longitude = dmsDecode(x); const latitude = dmsDecode(y); return [ longitude, latitude, Number(height) || 0 ]; } //#endregion //#region utils/isCesiumConstant.ts /** * Determines if the Cesium property is a constant. * * @param value Cesium property */ function isCesiumConstant(value) { return !(0, cesium.defined)(value) || !!value.isConstant; } //#endregion //#region utils/material.ts /** * Only as a type fix for `Cesium.Material` */ var CesiumMaterial = class extends cesium.Material { constructor(options) { super(options); } }; /** * Get material from cache, alias of `Material._materialCache.getMaterial` */ function getMaterialCache(type) { return cesium.Material._materialCache.getMaterial(type); } /** * Add material to Cesium's material cache, alias of `Material._materialCache.addMaterial` */ function addMaterialCache(type, material) { return cesium.Material._materialCache.addMaterial(type, material); } //#endregion //#region utils/pick.ts /** * Analyze the result of Cesium's `scene.pick` and convert it to an array format */ function resolvePick(pick = {}) { const { primitive, id, primitiveCollection, collection } = pick; const entityCollection = id && id.entityCollection || null; const dataSource = entityCollection && entityCollection.owner || null; const ids = Array.isArray(id) ? id : [id].filter(Boolean); return [ ...ids, primitive, primitiveCollection, collection, entityCollection, dataSource ].filter((e) => !!e); } /** * Determine if the given array of graphics is hit by Cesium's `scene.pick` * * @param pick The `scene.pick` object used for matching * @param graphic An array of graphics to check for hits */ function pickHitGraphic(pick, graphic) { if (!Array.isArray(graphic) || !graphic.length) return false; const elements = resolvePick(pick); if (!elements.length) return false; return elements.some((element) => graphic.includes(element)); } //#endregion //#region utils/property.ts /** * Is Cesium.Property * @param value - The target object */ function isProperty(value) { return value && isFunction(value.getValue); } /** * Converts a value that may be a Property into its target value, @see {toProperty} for the reverse operation * ```typescript * toPropertyValue('val') //=> 'val' * toPropertyValue(new ConstantProperty('val')) //=> 'val' * toPropertyValue(new CallbackProperty(()=>'val')) //=> 'val' * ``` * * @param value - The value to convert */ function toPropertyValue(value, time) { return isProperty(value) ? value.getValue(time) : value; } /** * Converts a value that may be a Property into a Property object, @see {toPropertyValue} for the reverse operation * * @param value - The property value or getter to convert, can be undefined or null * @param isConstant - The second parameter for converting to CallbackProperty * @returns Returns the converted Property object, if value is undefined or null, returns undefined */ function toProperty(value, isConstant = false) { return isProperty(value) ? value : isFunction(value) ? new cesium.CallbackProperty(value, isConstant) : new cesium.ConstantProperty(value); } /** * Create a Cesium property key * * @param scope The host object * @param field The property name * @param maybeProperty Optional property or getter * @param readonly Whether the property is read-only */ function createPropertyField(scope, field, maybeProperty, readonly$2) { let removeOwnerListener; const ownerBinding = (value) => { removeOwnerListener?.(); if ((0, cesium.defined)(value?.definitionChanged)) removeOwnerListener = value?.definitionChanged?.addEventListener(() => { scope.definitionChanged.raiseEvent(scope, field, value, value); }); }; const privateField = `_${field}`; const property = toProperty(maybeProperty); scope[privateField] = property; ownerBinding(property); if (readonly$2) Object.defineProperty(scope, field, { get() { return scope[privateField]; } }); else Object.defineProperty(scope, field, { get() { return scope[privateField]; }, set(value) { const previous = scope[privateField]; if (scope[privateField] !== value) { scope[privateField] = value; ownerBinding(value); if ((0, cesium.defined)(scope.definitionChanged)) scope.definitionChanged.raiseEvent(scope, field, value, previous); } } }); } function createCesiumAttribute(scope, key, value, options = {}) { const allowToProperty = !!options.toProperty; const shallowClone = !!options.shallowClone; const changedEventKey = options.changedEventKey || "definitionChanged"; const changedEvent = Reflect.get(scope, changedEventKey); const privateKey = `_${String(key)}`; const attribute = allowToProperty ? toProperty(value) : value; Reflect.set(scope, privateKey, attribute); const obj = { get() { const value$1 = Reflect.get(scope, privateKey); if (shallowClone) return Array.isArray(value$1) ? [...value$1] : { ...value$1 }; else return value$1; } }; let previousListener; const serial = (property, previous) => { previousListener?.(); previousListener = property?.definitionChanged?.addEventListener(() => { changedEvent?.raiseEvent.bind(changedEvent)(scope, key, property, previous); }); }; if (!options.readonly) { if (allowToProperty && isProperty(value)) serial(value); obj.set = (value$1) => { if (allowToProperty && !isProperty(value$1)) throw new Error(`The value of ${String(key)} must be a Cesium.Property object`); const previous = Reflect.get(scope, privateKey); if (previous !== value$1) { Reflect.set(scope, privateKey, value$1); changedEvent?.raiseEvent.bind(changedEvent)(scope, key, value$1, previous); if (allowToProperty) serial(value$1); } }; } Object.defineProperty(scope, key, obj); } function createCesiumProperty(scope, key, value, options = {}) { return createCesiumAttribute(scope, key, value, { ...options, toProperty: true }); } //#endregion //#region utils/throttle.ts /** * Throttle function, which limits the frequency of execution of the function * * @param callback raw function * @param delay Throttled delay duration (ms) * @param trailing Trigger callback function after last call @default true * @param leading Trigger the callback function immediately on the first call @default false * @returns Throttle function */ function throttle(callback, delay = 100, trailing = true, leading = false) { const restList = []; let tracked = false; const trigger = async () => { await (0, __vueuse_core.promiseTimeout)(delay); tracked = false; if (leading) try { callback(...restList[0]); } catch (error) { console.error(error); } if (trailing && (!leading || restList.length > 1)) try { callback(...restList[restList.length - 1]); } catch (error) { console.error(error); } restList.length = 0; }; return (...rest) => { if (restList.length < 2) restList.push(rest); else restList[1] = rest; if (!tracked) { tracked = true; trigger(); } }; } //#endregion //#region utils/toCartesian3.ts /** * Converts position to a coordinate point in the Cartesian coordinate system * * @param position Position information, which can be a Cartesian coordinate point (Cartesian3), a geographic coordinate point (Cartographic), an array, or an object containing WGS84 latitude, longitude, and height information * @returns The converted Cartesian coordinate point. If the input parameter is invalid, undefined is returned */ function toCartesian3(position) { if (!position) return void 0; if (position instanceof cesium.Cartesian3) return position.clone(); else if (position instanceof cesium.Cartographic) return cesium.Ellipsoid.WGS84.cartographicToCartesian(position); else if (Array.isArray(position)) return cesium.Cartesian3.fromDegrees(position[0], position[1], position[2]); else return cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, position.height); } //#endregion //#region utils/toCartographic.ts /** * Converts a position to a Cartographic coordinate point * * @param position Position information, which can be a Cartesian3 coordinate point, a Cartographic coordinate point, an array, or an object containing WGS84 longitude, latitude, and height information * @returns The converted Cartographic coordinate point, or undefined if the input parameter is invalid */ function toCartographic(position) { if (!position) return void 0; if (position instanceof cesium.Cartesian3) return cesium.Ellipsoid.WGS84.cartesianToCartographic(position); else if (position instanceof cesium.Cartographic) return position.clone(); else if (Array.isArray(position)) return cesium.Cartographic.fromDegrees(position[0], position[1], position[2]); else return cesium.Cartographic.fromDegrees(position.longitude, position.latitude, position.height); } //#endregion //#region utils/tryRun.ts /** * Safely execute the provided function without throwing errors, * essentially a simple wrapper around a `try...catch...` block */ function tryRun(fn) { return (...args) => { try { return fn?.(...args); } catch (error) { console.error(error); } }; } //#endregion //#region toPromiseValue/index.ts /** * Similar to Vue's built-in `toValue`, but capable of handling asynchronous functions, thus returning a `await value`. * * Used in conjunction with VueUse's `computedAsync`. * * @param source The source value, which can be a reactive reference or an asynchronous getter. * @param options Conversion options * * @example * ```ts * * const data = computedAsync(async ()=> { * return await toPromiseValue(promiseRef) * }) * * ``` */ async function toPromiseValue(source, options = {}) { try { const { raw = true } = options; let value; if (isFunction(source)) value = await source(); else { const result = (0, vue.toValue)(source); value = isPromise(result) ? await result : result; } return raw ? (0, vue.toRaw)(value) : value; } catch (error) { console.error(error); throw error; } } //#endregion //#region useCesiumEventListener/index.ts /** * Easily use the `addEventListener` in `Cesium.Event` instances, * when the dependent data changes or the component is unmounted, * the listener function will automatically reload or destroy. */ function useCesiumEventListener(event, listener, options = {}) { const isActive = (0, vue.toRef)(options.isActive ?? true); const cleanup = (0, vue.watchEffect)((onCleanup) => { const _event = (0, vue.toValue)(event); const events = Array.isArray(_event) ? _event : [_event]; if (events) { if (events.length && isActive.value) { const stopFns = events.map((event$1) => { const e = (0, vue.toValue)(event$1); return e?.addEventListener(listener, e); }); onCleanup(() => stopFns.forEach((stop) => stop?.())); } } }); (0, __vueuse_core.tryOnScopeDispose)(cleanup.stop); return cleanup.stop; } //#endregion //#region useViewer/index.ts /** * Obtain the `Viewer` instance injected through `createViewer` in the current component or its ancestor components. * * note: * - If `createViewer` and `useViewer` are called in the same component, the `Viewer` instance injected by `createViewer` will be used preferentially. * - When calling `createViewer` and `useViewer` in the same component, `createViewer` should be called before `useViewer`. */ function useViewer() { const scope = (0, vue.getCurrentScope)(); const instanceViewer = scope ? CREATE_VIEWER_COLLECTION.get(scope) : void 0; if (instanceViewer) return instanceViewer; else { const injectViewer = (0, vue.inject)(CREATE_VIEWER_INJECTION_KEY); if (!injectViewer) throw new Error("The `Viewer` instance injected by `createViewer` was not found in the current component or its ancestor components. Have you called `createViewer`?"); return injectViewer; } } //#endregion //#region useCameraState/index.ts /** * Reactive Cesium Camera state */ function useCameraState(options = {}) { let getCamera = options.camera; if (!getCamera) { const viewer = useViewer(); getCamera = () => viewer.value?.scene.camera; } const camera = (0, vue.computed)(() => (0, vue.toValue)(getCamera)); const event = (0, vue.computed)(() => { const eventField = (0, vue.toValue)(options.event) || "changed"; return camera.value?.[eventField]; }); const changedSymbol = (0, __vueuse_core.refThrottled)((0, vue.shallowRef)(Symbol("camera change")), options.delay ?? 8, true, false); const setChangedSymbol = () => { changedSymbol.value = Symbol("camera change"); }; (0, vue.watch)(camera, () => setChangedSymbol()); useCesiumEventListener(event, () => setChangedSymbol()); return { camera, position: (0, vue.computed)(() => changedSymbol.value ? camera.value?.position?.clone() : void 0), direction: (0, vue.computed)(() => changedSymbol.value ? camera.value?.direction?.clone() : void 0), up: (0, vue.computed)(() => changedSymbol.value ? camera.value?.up?.clone() : void 0), right: (0, vue.computed)(() => changedSymbol.value ? camera.value?.right?.clone() : void 0), positionCartographic: (0, vue.computed)(() => changedSymbol.value ? camera.value?.positionCartographic?.clone() : void 0), positionWC: (0, vue.computed)(() => changedSymbol.value ? camera.value?.positionWC?.clone() : void 0), directionWC: (0, vue.computed)(() => changedSymbol.value ? camera.value?.directionWC?.clone() : void 0), upWC: (0, vue.computed)(() => changedSymbol.value ? camera.value?.directionWC?.clone() : void 0), rightWC: (0, vue.computed)(() => changedSymbol.value ? camera.value?.directionWC?.clone() : void 0), viewRectangle: (0, vue.computed)(() => changedSymbol.value ? camera.value?.computeViewRectangle() : void 0), heading: (0, vue.computed)(() => changedSymbol.value ? camera.value?.heading : void 0), pitch: (0, vue.computed)(() => changedSymbol.value ? camera.value?.pitch : void 0), roll: (0, vue.computed)(() => changedSymbol.value ? camera.value?.roll : void 0), level: (0, vue.computed)(() => changedSymbol.value && camera.value?.positionCartographic?.height ? computeLevel(camera.value.positionCartographic.height) : void 0) }; } const A = 40487.57; const B = 7096758e-11; const C = 91610.74; const D = -40467.74; /** * Compute the camera level at a given height. */ function computeLevel(height) { return D + (A - D) / (1 + (height / C) ** B); } //#endregion //#region useCesiumFps/index.ts /** * Reactive get the frame rate of Cesium */ function useCesiumFps(options = {}) { const { delay = 100 } = options; const viewer = useViewer(); const p = (0, vue.shallowRef)(performance.now()); useCesiumEventListener(() => viewer.value?.scene.postRender, () => p.value = performance.now()); const interval = (0, vue.ref)(0); (0, __vueuse_core.watchThrottled)(p, (value, oldValue) => { interval.value = value - oldValue; }, { throttle: delay }); const fps = (0, vue.computed)(() => { return 1e3 / interval.value; }); return { interval: (0, vue.readonly)(interval), fps }; } //#endregion //#region useCollectionScope/index.ts /** * Scope the SideEffects of Cesium-related `Collection` and automatically remove them when unmounted. * - note: This is a basic function that is intended to be called by other lower-level function * @param addFn - add SideEffect function. eg.`entites.add` * @param removeFn - Clean SideEffect function. eg.`entities.remove` * @param removeScopeArgs - The parameters to pass for `removeScope` triggered when the component is unmounted */ function useCollectionScope(addFn, removeFn, removeScopeArgs) { const scope = (0, vue.shallowReactive)(/* @__PURE__ */ new Set()); const add = (instance, ...args) => { const result = addFn(instance, ...args); if (isPromise(result)) return new Promise((resolve, reject) => { result.then((i) => { scope.add(i); resolve(i); }).catch((error) => reject(error)); }); else { scope.add(result); return result; } }; const remove = (instance, ...args) => { scope.delete(instance); return removeFn(instance, ...args); }; const removeWhere = (predicate, ...args) => { scope.forEach((instance) => { if (predicate(instance)) remove(instance, ...args); }); }; const removeScope = (...args) => { scope.forEach((instance) => { remove(instance, ...args); }); }; (0, __vueuse_core.tryOnScopeDispose)(() => removeScope(...removeScopeArgs)); return { scope: (0, vue.shallowReadonly)(scope), add, remove, removeWhere, removeScope }; } //#endregion //#region useDataSource/index.ts function useDataSource(dataSources, options = {}) { const { destroyOnRemove, collection, isActive = true, evaluating } = options; const result = (0, __vueuse_core.computedAsync)(() => toPromiseValue(dataSources), void 0, { evaluating }); const viewer = useViewer(); (0, vue.watchEffect)((onCleanup) => { const _isActive = (0, vue.toValue)(isActive); if (_isActive) { const list = Array.isArray(result.value) ? [...result.value] : [result.value]; const _collection = collection ?? viewer.value?.dataSources; list.forEach((item) => item && _collection?.add(item)); onCleanup(() => { const destroy = (0, vue.toValue)(destroyOnRemove); !_collection?.isDestroyed() && list.forEach((dataSource) => dataSource && _collection?.remove(dataSource, destroy)); }); } }); return result; } //#endregion //#region useDataSourceScope/index.ts /** * // Scope the SideEffects of `DataSourceCollection` operations and automatically remove them when unmounted */ function useDataSourceScope(options = {}) { const { collection: _collection, destroyOnRemove } = options; const viewer = useViewer(); const collection = (0, vue.computed)(() => { return (0, vue.toValue)(_collection) ?? viewer.value?.dataSources; }); const addFn = (dataSource) => { if (!collection.value) throw new Error("collection is not defined"); return collection.value.add(dataSource); }; const removeFn = (dataSource, destroy) => { return !!collection.value?.remove(dataSource, destroy); }; const { scope, add, remove, removeWhere, removeScope } = useCollectionScope(addFn, removeFn, [destroyOnRemove]); return { scope, add, remove, removeWhere, removeScope }; } //#endregion //#region useElementOverlay/index.ts /** * Cesium HtmlElement Overlay */ function useElementOverlay(target, position, options = {}) { const { referenceWindow, horizontal = "center", vertical = "bottom", offset = { x: 0, y: 0 } } = options; const cartesian3 = (0, vue.computed)(() => toCartesian3((0, vue.toValue)(position))); const viewer = useViewer(); const coord = (0, vue.shallowRef)(); useCesiumEventListener(() => viewer.value?.scene.postRender, () => { if (!viewer.value?.scene) return; if (!cartesian3.value) coord.value = void 0; else { const reslut = cartesianToCanvasCoord(cartesian3.value, viewer.value.scene); coord.value = !cesium.Cartesian2.equals(reslut, coord.value) ? reslut : coord.value; } }); const canvasBounding = (0, __vueuse_core.useElementBounding)(() => viewer.value?.canvas.parentElement); const targetBounding = (0, __vueuse_core.useElementBounding)(target); const finalOffset = (0, vue.computed)(() => { const _offset = (0, vue.toValue)(offset); let x$1 = _offset?.x ?? 0; const _horizontal = (0, vue.toValue)(horizontal); if (_horizontal === "center") x$1 -= targetBounding.width.value / 2; else if (_horizontal === "right") x$1 -= targetBounding.width.value; let y$1 = _offset?.y ?? 0; const _vertical = (0, vue.toValue)(vertical); if (_vertical === "center") y$1 -= targetBounding.height.value / 2; else if (_vertical === "bottom") y$1 -= targetBounding.height.value; return { x: x$1, y: y$1 }; }); const location = (0, vue.computed)(() => { const data = { x: coord.value?.x ?? 0, y: coord.value?.y ?? 0 }; if ((0, vue.toValue)(referenceWindow)) { data.x += canvasBounding.x.value; data.y += canvasBounding.y.value; } return { x: finalOffset.value.x + data.x, y: finalOffset.value.y + data.y }; }); const x = (0, vue.computed)(() => location.value.x); const y = (0, vue.computed)(() => location.value.y); const style = (0, vue.computed)(() => ({ left: `${x.value?.toFixed(2)}px`, top: `${y.value?.toFixed(2)}px` })); (0, vue.watchEffect)(() => { const element = (0, vue.toValue)(target); if (element && (0, vue.toValue)(options.applyStyle ?? true)) { element.style?.setProperty?.("left", style.value.left); element.style?.setProperty?.("top", style.value.top); } }); return { x, y, style }; } //#endregion //#region useEntity/index.ts function useEntity(data, options = {}) { const { collection, isActive = true, evaluating } = options; const result = (0, __vueuse_core.computedAsync)(() => toPromiseValue(data), [], { evaluating }); const viewer = useViewer(); (0, vue.watchEffect)((onCleanup) => { const _isActive = (0, vue.toValue)(isActive); if (_isActive) { const list = Array.isArray(result.value) ? [...result.value] : [result.value]; const _collection = collection ?? viewer.value?.entities; list.forEach((item) => item && _collection?.add(item)); onCleanup(() => { list.forEach((item) => item && _collection?.remove(item)); }); } }); return result; } //#endregion //#region useEntityScope/index.ts /** * Make `add` and `remove` operations of `EntityCollection` scoped, * automatically remove `Entity` instance when component is unmounted. */ function useEntityScope(options = {}) { const { collection: _collection } = options; const viewer = useViewer(); const collection = (0, vue.computed)(() => { return (0, vue.toValue)(_collection) ?? viewer.value?.entities; }); const addFn = (entity) => { if (!collection.value) throw new Error("collection is not defined"); if (!collection.value.contains(entity)) collection.value.add(entity); return entity; }; const removeFn = (entity) => { return !!collection.value?.remove(entity); }; const { scope, add, remove, removeWhere, removeScope } = useCollectionScope(addFn, removeFn, []); return { scope, add, remove, removeWhere, removeScope }; } //#endregion //#region useScenePick/index.ts const pickCache = /* @__PURE__ */ new WeakMap(); /** * Uses the `scene.pick` function in Cesium's Scene object to perform screen point picking, * return a computed property containing the pick result, or undefined if no object is picked. * * @param windowPosition The screen coordinates of the pick point. */ function useScenePick(windowPosition, options = {}) { const { width = 3, height = 3, throttled = 8 } = options; const isActive = (0, vue.toRef)(options.isActive ?? true); const viewer = useViewer(); const position = (0, __vueuse_core.refThrottled)((0, vue.computed)(() => (0, vue.toValue)(windowPosition)?.clone()), throttled, false, true); const pick = (0, vue.shallowRef)(); (0, vue.watchEffect)(() => { if (viewer.value && position.value && isActive.value) { const cache = pickCache.get(viewer.value); if (cache && cache[0].equals(position.value)) pick.value = cache[1]; else { pickCache.set(viewer.value, [position.value.clone(), pick.value]); pick.value = viewer.value?.scene.pick(position.value, (0, vue.toValue)(width), (0, vue.toValue)(height)); } } }); return pick; } //#endregion //#region useScreenSpaceEventHandler/index.ts /** * Easily use the `ScreenSpaceEventHandler`, * when the dependent data changes or the component is unmounted, * the listener function will automatically reload or destroy. * * @param type Types of mouse event * @param inputAction Callback function for listening */ function useScreenSpaceEventHandler(type, inputAction, options = {}) { const { modifier } = options; const viewer = useViewer(); const isActive = (0, vue.toRef)(options.isActive ?? true); const handler = (0, vue.computed)(() => { if (viewer.value?.cesiumWidget?.canvas) return new cesium.ScreenSpaceEventHandler(viewer.value.cesiumWidget.canvas); }); const cleanup1 = (0, vue.watch)(handler, (_value, previous) => { viewer.value?.cesiumWidget && previous?.destroy(); }); const cleanup2 = (0, vue.watchEffect)((onCleanup) => { const typeValue = (0, vue.toValue)(type); const modifierValue = (0, vue.toValue)(modifier); const handlerValue = (0, vue.toValue)(handler); if (!handlerValue || !isActive.value || !inputAction) return; if (isDef(typeValue)) { handlerValue.setInputAction(inputAction, typeValue, modifierValue); onCleanup(() => handlerValue.removeInputAction(typeValue, modifierValue)); } }); const stop = () => { cleanup1(); cleanup2(); }; (0, __vueuse_core.tryOnScopeDispose)(stop); return stop; } //#endregion //#region useGraphicEvent/useDrag.ts /** * Use graphic drag events with ease, and remove listener automatically on unmounted. */ function useDrag(listener) { const position = (0, vue.shallowRef)(); const pick = useScenePick(position); const motionEvent = (0, vue.shallowRef)(); const dragging = (0, vue.ref)(false); const viewer = useViewer(); const cameraLocked = (0, vue.ref)(false); (0, vue.watch)(cameraLocked, (cameraLocked$1) => { viewer.value && (viewer.value.scene.screenSpaceCameraController.enableRotate = !cameraLocked$1); }); const lockCamera = () => { cameraLocked.value = true; }; const execute = (pick$1, startPosition, endPosition) => { listener({ event: { startPosition: startPosition.clone(), endPosition: endPosition.clone() }, pick: pick$1, dragging: dragging.value, lockCamera }); (0, vue.nextTick)(() => { if (!dragging.value && cameraLocked.value) cameraLocked.value = false; }); }; const stopLeftDownWatch = useScreenSpaceEventHandler(cesium.ScreenSpaceEventType.LEFT_DOWN, (event) => { dragging.value = true; position.value = event.position.clone(); }); const stopMouseMoveWatch = useScreenSpaceEventHandler(cesium.ScreenSpaceEventType.MOUSE_MOVE, throttle(({ startPosition, endPosition }) => { motionEvent.value = { startPosition: motionEvent.value?.endPosition.clone() || startPosition.clone(), endPosition: endPosition.clone() }; }, 8, false, true)); (0, vue.watch)([pick, motionEvent], ([pick$1, motionEvent$1]) => { if (pick$1 && motionEvent$1) { const { startPosition, endPosition } = motionEvent$1; dragging.value && execute(pick$1, startPosition, endPosition); } }); const stopLeftUpWatch = useScreenSpaceEventHandler(cesium.ScreenSpaceEventType.LEFT_UP, (event) => { dragging.value = false; if (pick.value && motionEvent.value) execute(pick.value, motionEvent.value.endPosition, event.position); position.value = void 0; motionEvent.value = void 0; }); const stop = () => { stopLeftDownWatch(); stopMouseMoveWatch(); stopLeftUpWatch(); }; (0, __vueuse_core.tryOnScopeDispose)(stop); return stop; } //#endregion //#region useGraphicEvent/useHover.ts /** * Use graphic hover events with ease, and remove listener automatically on unmounted. */ function useHover(listener) { const motionEvent = (0, vue.shallowRef)(); const pick = useScenePick(() => motionEvent.value?.endPosition); const execute = (pick$1, startPosition, endPosition, hovering) => { listener({ event: { startPosition: startPosition.clone(), endPosition: endPosition.clone() }, pick: pick$1, hovering }); }; useScreenSpaceEventHandler(cesium.ScreenSpaceEventType.MOUSE_MOVE, ({ startPosition, endPosition }) => { if (!startPosition.equals(motionEvent.value?.startPosition) || !endPosition.equals(motionEvent.value?.endPosition)) motionEvent.value = { startPosition: startPosition.clone(), endPosition: endPosition.clone() }; }); (0, vue.watch)([pick, motionEvent], ([pick$1, motionEvent$1]) => { if (pick$1 && motionEvent$1) { const { startPosition, endPosition } = motionEvent$1; execute(pick$1, startPosition, endPosition, true); } }); (0, vue.watch)(pick, (pick$1, prevPick) => { if (prevPick && motionEvent.value) { const { startPosition, endPosition } = motionEvent.value; execute(prevPick, startPosition, endPosition, false); } }); } //#endregion //#region useGraphicEvent/usePositioned.ts /** * @internal */ const EVENT_TYPE_RECORD = { LEFT_DOWN: cesium.ScreenSpaceEventType.LEFT_DOWN, LEFT_UP: cesium.ScreenSpaceEventType.LEFT_UP, LEFT_CLICK: cesium.ScreenSpaceEventType.LEFT_CLICK, LEFT_DOUBLE_CLICK: cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, RIGHT_DOWN: cesium.ScreenSpaceEventType.RIGHT_DOWN, RIGHT_UP: cesium.ScreenSpaceEventType.RIGHT_UP, RIGHT_CLICK: cesium.ScreenSpaceEventType.RIGHT_CLICK, MIDDLE_DOWN: cesium.ScreenSpaceEventType.MIDDLE_DOWN, MIDDLE_UP: cesium.ScreenSpaceEventType.MIDDLE_UP, MIDDLE_CLICK: cesium.ScreenSpaceEventType.MIDDLE_CLICK }; function usePositioned(type, listener) { const screenEvent = EVENT_TYPE_RECORD[type]; const viewer = useViewer(); useScreenSpaceEventHandler(screenEvent, (event) => { const position = event.position; const pick = viewer.value?.scene.pick(position); pick && position && listener({ event: { position }, pick }); }); } //#endregion //#region useGraphicEvent/index.ts const GLOBAL_GRAPHIC_SYMBOL = Symbol("GLOBAL_GRAPHIC_SYMBOL"); const POSITIONED_EVENT_TYPES = [ "LEFT_DOWN", "LEFT_UP", "LEFT_CLICK", "LEFT_DOUBLE_CLICK", "RIGHT_DOWN", "RIGHT_UP", "RIGHT_CLICK", "MIDDLE_DOWN", "MIDDLE_UP", "MIDDLE_CLICK" ]; /** * Handle graphic event listeners and cursor styles for Cesium graphics. * You don't need to overly worry about memory leaks from the function, as it automatically cleans up internally. */ function useGraphicEvent() { const collection = /* @__PURE__ */ new WeakMap(); const cursorCollection = /* @__PURE__ */ new WeakMap(); const dragCursorCollection = /* @__PURE__ */ new WeakMap(); const removeGraphicEvent = (graphic, type, listener) => { const _graphic = graphic === "global" ? GLOBAL_GRAPHIC_SYMBOL : graphic; collection?.get(_graphic)?.get(type)?.delete(listener); cursorCollection?.get(_graphic)?.get(type)?.delete(listener); if (collection?.get(_graphic)?.get(type)?.size === 0) collection.get(_graphic).delete(type); if (collection.get(_graphic)?.size === 0) collection.delete(_graphic); cursorCollection?.get(_graphic)?.get(type)?.delete(listener); if (cursorCollection?.get(_graphic)?.get(type)?.size === 0) cursorCollection?.get(_graphic)?.delete(type); if (cursorCollection?.get(_graphic)?.size === 0) cursorCollection?.delete(_graphic); dragCursorCollection?.get(_graphic)?.get(type)?.delete(listener); if (dragCursorCollection?.get(_graphic)?.get(type)?.size === 0) dragCursorCollection?.get(_graphic)?.delete(type); if (dragCursorCollection?.get(_graphic)?.size === 0) dragCursorCollection?.delete(_graphic); }; const addGraphicEvent = (graphic, type, listener, options = {}) => { const _graphic = graphic === "global" ? GLOBAL_GRAPHIC_SYMBOL : graphic; collection.get(_graphic) ?? collection.set(_graphic, /* @__PURE__ */ new Map()); const eventTypeMap = collection.get(_graphic); eventTypeMap.get(type) ?? eventTypeMap.set(type, /* @__PURE__ */ new Set()); const listeners = eventTypeMap.get(type); listeners.add(listener); let { cursor = "pointer", dragCursor } = options; if (isDef(cursor)) { const _cursor = isFunction(cursor) ? cursor : () => cursor; cursorCollection.get(_graphic) ?? cursorCollection.set(_graphic, /* @__PURE__ */ new Map()); cursorCollection.get(_graphic).get(type) ?? cursorCollection.get(_graphic).set(type, /* @__PURE__ */ new Map()); cursorCollection.get(_graphic).get(type).set(listener, _cursor); } if (type === "DRAG") dragCursor ??= (event) => event?.dragging ? "crosshair" : void 0; if (isDef(dragCursor)) { const _dragCursor = isFunction(dragCursor) ? dragCursor : () => dragCursor; dragCursorCollection.get(_graphic) ?? dragCursorCollection.set(_graphic, /* @__PURE__ */ new Map()); dragCursorCollection.get(_graphic).get(type) ?? dragCursorCollection.get(_graphic).set(type, /* @__PURE__ */ new Map()); dragCursorCollection.get(_graphic).get(type).set(listener, _dragCursor); } return () => removeGraphicEvent(graphic, type, listener); }; const clearGraphicEvent = (graphic, type) => { const _graphic = graphic === "global" ? GLOBAL_GRAPHIC_SYMBOL : graphic; if (type === "all") { collection.delete(_graphic); cursorCollection.delete(_graphic); dragCursorCollection.delete(_graphic); return; } collection.get(_graphic)?.delete(type); if (collection.get(_graphic)?.size === 0) collection.delete(_graphic); cursorCollection?.get(_graphic)?.delete(type); dragCursorCollection?.get(_graphic)?.delete(type); if (cursorCollection?.get(_graphic)?.size === 0) cursorCollection?.delete(_graphic); if (dragCursorCollection?.get(_graphic)?.size === 0) dragCursorCollection?.delete(_graphic); }; for (const type of POSITIONED_EVENT_TYPES) usePositioned(type, (event) => { const graphics = resolvePick(event.pick); graphics.concat(GLOBAL_GRAPHIC_SYMBOL).forEach((graphic) => { collection.get(graphic)?.get(type)?.forEach((fn) => tryRun(fn)?.(event)); }); }); const dragging = (0, vue.ref)(false); const viewer = useViewer(); useHover((event) => { const graphics = resolvePick(event.pick).concat(GLOBAL_GRAPHIC_SYMBOL); graphics.forEach((graphic) => { collection.get(graphic)?.get("HOVER")?.forEach((fn) => tryRun(fn)?.(event)); if (!dragging.value) cursorCollection.get(graphic)?.forEach((map) => { map.forEach((fn) => { const cursor = event.hovering ? tryRun(fn)(event) : ""; viewer.value?.canvas.style?.setProperty("cursor", cursor); }); }); }); }); useDrag((event) => { const graphics = resolvePick(event.pick).concat(GLOBAL_GRAPHIC_SYMBOL); dragging.value = event.dragging; graphics.forEach((graphic) => { collection.get(graphic)?.get("DRAG")?.forEach((fn) => tryRun(fn)(event)); dragCursorCollection.get(graphic)?.forEach((map) => { map.forEach((fn) => { const cursor = event.dragging ? tryRun(fn)(event) : ""; viewer.value?.canvas.style?.setProperty("cursor", cursor); }); }); }); }); return { addGraphicEvent, removeGraphicEvent, clearGraphicEvent }; } //#endregion //#region useImageryLayer/index.ts function useImageryLayer(data, options = {}) { const { destroyOnRemove, collection, isActive = true, evaluating } = options; const result = (0, __vueuse_core.computedAsync)(() => toPromiseValue(data), [], { evaluating }); const viewer = useViewer(); (0, vue.watchEffect)((onCleanup) => { const _isActive = (0, vue.toValue)(isActive); if (_isActive) { const list = Array.isArray(result.value) ? [...result.value] : [result.value]; const _collection = collection ?? viewer.value?.imageryLayers; if (collection?.isDestroyed()) return; list.forEach((item) => { if (!item) { console.warn("ImageryLayer is undefined"); return; } if (item?.isDestroyed()) { console.warn("ImageryLayer is destroyed"); return; } _collection?.add(item); }); onCleanup(() => { const destroy = (0, vue.toValue)(destroyOnRemove); list.forEach((item) => item && _collection?.remove(item, destroy)); }); } }); return result; } //#endregion //#region useImageryLayerScope/index.ts /** * Make `add` and `remove` operations of `ImageryLayerCollection` scoped, * automatically remove `ImageryLayer` instance when component is unmounted. */ function useImageryLayerScope(options = {}) { const { collection: _collection, destroyOnRemove } = options; const viewer = useViewer(); const collection = (0, vue.computed)(() => { return (0, vue.toValue)(_collection) ?? viewer.value?.imageryLayers; }); const addFn = (imageryLayer, index) => { if (!collection.value) throw new Error("collection is not defined"); collection.value.add(imageryLayer, index); return imageryLayer; }; const removeFn = (imageryLayer, destroy) => { return !!collection.value?.remove(imageryLayer, destroy); }; const { scope, add, remove, removeWhere, removeScope } = useCollectionScope(addFn, removeFn, [destroyOnRemove]); return { scope, add, remove, removeWhere, removeScope }; } //#endregion //#region usePostProcessStage/index.ts function usePostProcessStage(data, options = {}) { const { collection, isActive = true, evaluating } = options; const result = (0, __vueuse_core.computedAsync)(() => toPromiseValue(data), void 0, { evaluating }); const viewer = useViewer(); (0, vue.watchEffect)((onCleanup) => { if (!viewer.value) return; const _isActive = (0, vue.toValue)(isActive); if (_isActive) { const list = Array.isArray(result.value) ? [...result.value] : [result.value]; const _collection = collection ?? viewer.value.scene.postProcessStages; list.forEach((item) => item && _collection.add(item)); onCleanup(() => { list.forEach((item) => item && _collection.remove(item)); }); } }); return result; } //#endregion //#region usePostProcessStageScope/index.ts /** * Make `add` and `remove` operations of `PostProcessStageCollection` scoped, * automatically remove `PostProcessStage` instance when component is unmounted. */ function usePostProcessStageScope(options = {}) { const { collection: _collection } = options; const viewer = useViewer(); const collection = (0, vue.computed)(() => { return (0, vue.toValue)(_collection) ?? viewer.value?.postProcessStages; }); const addFn = (postProcessStage) => { if (!collection.value) throw new Error("collection is not defined"); return collection.value.add(postProcessStage); }; const removeFn = (postProcessStage) => { return !!collection.value?.remove(postProcessStage); }; const { scope, add, remove, removeWhere, removeScope } = useCollectionScope(addFn, removeFn, []); return { scope, add, remove, removeWhere, removeScope }; } //#endregion //#region usePrimitive/index.ts function usePrimitive(data, options = {}) { const { collection, isActive = true, evaluating } = options; const result = (0, __vueuse_core.computedAsync)(() => toPromiseValue(data), void 0, { evaluating }); const viewer = useViewer(); (0, vue.watchEffect)((onCleanup) => { const _isActive = (0, vue.toValue)(isActive); if (_isActive) { const list = Array.isArray(result.value) ? [...result.value] : [result.value]; const _collection = collection === "ground" ? viewer.value?.scene.groundPrimitives : collection ?? viewer.value?.scene.primitives; list.forEach((item) => item && _collection?.add(item)); onCleanup(() => { !_collection?.isDestroyed() && list.forEach((item) => item && _collection?.remove(item)); }); } }); return result; } //#endregion //#region usePrimitiveScope/index.ts /** * Make `add` and `remove` operations of `PrimitiveCollection` scoped, * automatically remove `Primitive` instance when component is unmounted. */ function usePrimitiveScope(options = {}) { const { collection: _collection } = options; const viewer = useViewer(); const collection = (0, vue.computed)(() => { return (0, vue.toValue)(_collection) ?? viewer.value?.scene.primitives; }); const addFn = (primitive) => { if (!collection.value) throw new Error("collection is not defined"); return collection.value.add(primitive); }; const removeFn = (primitive) => { return !!collection.value?.re