UNPKG

vue-maplibre-gl

Version:
1 lines 110 kB
{"version":3,"file":"vue-maplibre-gl.cjs","sources":["../src/defaults.ts","../src/lib/language.ts","../src/lib/map.lib.ts","../src/lib/mapRegistry.ts","../src/components/map.component.ts","../src/components/controls/attribution.control.ts","../src/components/controls/fullscreen.control.ts","../src/components/controls/frameRate.control.ts","../src/components/controls/geolocation.control.ts","../src/components/controls/navigation.control.ts","../src/components/controls/scale.control.ts","../src/components/controls/styleSwitch.control.ts","../src/components/marker.component.ts","../src/lib/source.lib.ts","../src/lib/sourceLayer.registry.ts","../src/composable/useSource.ts","../src/components/sources/canvas.source.ts","../src/components/sources/geojson.source.ts","../src/components/sources/image.source.ts","../src/components/sources/raster.source.ts","../src/components/sources/rasterDem.source.ts","../src/components/sources/vector.source.ts","../src/components/sources/video.source.ts","../src/lib/layer.lib.ts","../src/composable/useDisposableLayer.ts","../src/components/layers/background.layer.ts","../src/components/layers/circle.layer.ts","../src/components/layers/fill.layer.ts","../src/components/layers/fillExtrusion.layer.ts","../src/components/layers/heatmap.layer.ts","../src/components/layers/hillshade.layer.ts","../src/components/layers/line.layer.ts","../src/components/layers/raster.layer.ts","../src/components/layers/smybol.layer.ts","../src/main.ts"],"sourcesContent":["import type { ValidLanguages } from '@/types';\nimport type { MapOptions as MaplibreMapOptions } from 'maplibre-gl';\nimport { reactive } from 'vue';\n\nexport type MapOptions = Omit<MaplibreMapOptions, 'container' | 'style'> & { style: object | string, language?: ValidLanguages };\n\nexport const defaults = reactive<MapOptions>({\n\tstyle : 'https://demotiles.maplibre.org/style.json',\n\tcenter : [ 0, 0 ],\n\tzoom : 1,\n\ttrackResize: false\n});\n","import type { ValidLanguages } from '@/types.ts';\nimport { type LayerSpecification, Map as MaplibreMap } from 'maplibre-gl';\n\nexport function setPrimaryLanguage(map: MaplibreMap, lang: ValidLanguages | undefined) {\n\n\tconst style = map.getStyle();\n\tif (!style?.layers) return;\n\n\tconst langField = lang ? `name:${lang}` : 'name';\n\n\tfunction replaceTextField(expr: any): any {\n\t\tif (typeof expr === 'string') {\n\t\t\t// \"{name}\" oder \"{name:xx}\" → coalesce verwenden\n\t\t\tif (/\\{name(:\\S+)?\\}/.test(expr)) {\n\t\t\t\treturn [ 'coalesce', [ 'get', langField ], [ 'get', 'name' ] ];\n\t\t\t}\n\t\t\treturn expr;\n\t\t} else if (Array.isArray(expr)) {\n\t\t\tconst op = expr[ 0 ];\n\t\t\tif (op === 'get') {\n\t\t\t\tif (typeof expr[ 1 ] === 'string' && expr[ 1 ].startsWith('name')) {\n\t\t\t\t\treturn [ 'coalesce', [ 'get', langField ], [ 'get', 'name' ] ];\n\t\t\t\t}\n\t\t\t\treturn expr;\n\t\t\t} else if (op === 'concat' || op === 'format' || op === 'case') {\n\t\t\t\t// rekursiv alle Unter-Elemente bearbeiten\n\t\t\t\treturn expr.map(replaceTextField);\n\t\t\t} else {\n\t\t\t\t// generisch: rekursiv alles bearbeiten\n\t\t\t\treturn expr.map(replaceTextField);\n\t\t\t}\n\t\t}\n\t\treturn expr;\n\t}\n\n\tstyle.layers.forEach((layer: LayerSpecification) => {\n\t\tif (layer.type !== 'symbol') return;\n\t\tconst tf = map.getLayoutProperty(layer.id, 'text-field');\n\t\tif (!tf) return;\n\n\t\tconst newTf = replaceTextField(tf);\n\t\tif (newTf) {\n\t\t\tmap.setLayoutProperty(layer.id, 'text-field', newTf);\n\t\t}\n\t});\n\n}\n","import type { MglMap } from '@/components';\nimport type { MglEvent } from '@/types';\nimport type { Map, MapOptions, MarkerOptions } from 'maplibre-gl';\n\nexport type MapEventHandler = (e: any) => void;\n\nexport class MapLib {\n\n\tstatic readonly MAP_OPTION_KEYS: Array<keyof MapOptions | 'mapStyle'> = [\n\t\t'attributionControl', 'bearing', 'bearingSnap', 'bounds', 'boxZoom', 'cancelPendingTileRequestsWhileZooming', 'canvasContextAttributes', 'center',\n\t\t'centerClampedToGround', 'clickTolerance', 'collectResourceTiming', 'cooperativeGestures', 'crossSourceCollisions', 'doubleClickZoom', 'dragPan',\n\t\t'dragRotate', 'elevation', 'fadeDuration', 'fitBoundsOptions', 'hash', 'interactive', 'keyboard', 'locale', 'localIdeographFontFamily',\n\t\t'logoPosition', 'maplibreLogo', 'maxBounds', 'maxCanvasSize', 'maxPitch', 'maxTileCacheSize', 'maxTileCacheZoomLevels', 'maxZoom', 'minPitch', 'minZoom',\n\t\t'pitch', 'pitchWithRotate', 'pixelRatio', 'refreshExpiredTiles', 'renderWorldCopies', 'roll', 'rollEnabled', 'scrollZoom', 'touchPitch',\n\t\t'touchZoomRotate', 'trackResize', 'transformCameraUpdate', 'transformRequest', 'validateStyle', 'zoom',\n\t\t'mapStyle'\n\t];\n\n\tstatic readonly MARKER_OPTION_KEYS: Array<keyof MarkerOptions> = [\n\t\t'element', 'offset', 'anchor', 'color', 'draggable', 'clickTolerance', 'rotation', 'rotationAlignment', 'pitchAlignment', 'scale'\n\t];\n\n\tstatic readonly MAP_EVENT_TYPES = [\n\t\t'boxzoomcancel', 'boxzoomend', 'boxzoomstart', 'click', 'contextmenu', 'cooperativegestureprevented', 'data', 'dataabort', 'dataloading', 'dblclick',\n\t\t'drag', 'dragend', 'dragstart', 'error', 'idle', 'load', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'move', 'moveend', 'movestart',\n\t\t'pitch', 'pitchend', 'pitchstart', 'projectiontransition', 'remove', 'render', 'resize', 'rotate', 'rotateend', 'rotatestart', 'sourcedata',\n\t\t'sourcedataabort', 'sourcedataloading', 'styledata', 'styledataloading', 'styleimagemissing', 'terrain', 'tiledataloading', 'touchcancel', 'touchend',\n\t\t'touchmove', 'touchstart', 'webglcontextlost', 'webglcontextrestored', 'wheel', 'zoom', 'zoomend', 'zoomstart'\n\t];\n\n\tstatic createEventHandler(component: InstanceType<typeof MglMap>, map: Map, ctx: {\n\t\temit: (t: string, payload: any) => void\n\t}, eventName: string): MapEventHandler {\n\t\treturn (payload = {}) => ctx.emit(eventName, { type: payload.type, map, component, event: payload } as MglEvent);\n\t}\n\n}\n","import type { MglMap } from '@/components';\nimport type { ValidLanguages } from '@/types';\nimport type { Map as MaplibreMap } from 'maplibre-gl';\nimport { reactive, type ShallowRef } from 'vue';\n\nexport interface MapInstance {\n\tcomponent?: InstanceType<typeof MglMap>;\n\tmap?: MaplibreMap;\n\tisMounted: boolean;\n\tisLoaded: boolean;\n\tlanguage: ValidLanguages | undefined;\n}\n\nconst instances = new Map<symbol | string, MapInstance>(),\n\t defaultKey = Symbol('default');\n\n// useMap returns reactive version of MapInstance\nexport function useMap(key: symbol | string = defaultKey): MapInstance {\n\tlet component = instances.get(key);\n\tif (!component) {\n\t\tcomponent = reactive({ isLoaded: false, isMounted: false, language: undefined });\n\t\tinstances.set(key, component);\n\t}\n\treturn component;\n}\n\nexport function registerMap(instance: InstanceType<typeof MglMap>, map: ShallowRef<MaplibreMap | undefined>, key: symbol | string = defaultKey): MapInstance {\n\tlet component = instances.get(key);\n\tif (!component) {\n\t\tcomponent = reactive({ isLoaded: false, isMounted: false, language: undefined });\n\t\tinstances.set(key, component);\n\t}\n\n\tcomponent.component = instance;\n\tcomponent.map = map.value;\n\tcomponent.isLoaded = map.value?.loaded() || false;\n\tcomponent.isMounted = false;\n\n\treturn component;\n}\n","import { defaults } from '@/defaults';\nimport { debounce } from '@/lib/debounce';\nimport { setPrimaryLanguage } from '@/lib/language.ts';\nimport { MapLib } from '@/lib/map.lib';\nimport { registerMap } from '@/lib/mapRegistry';\nimport {\n\tcomponentIdSymbol,\n\temitterSymbol,\n\ttype FitBoundsOptions,\n\tfitBoundsOptionsSymbol,\n\tisInitializedSymbol,\n\tisLoadedSymbol,\n\tmapSymbol,\n\ttype MglEvents,\n\tsourceIdSymbol,\n\ttype ValidLanguages\n} from '@/types';\nimport type { ProjectionSpecification } from '@maplibre/maplibre-gl-style-spec';\nimport { Map as MaplibreMap, type MapOptions, type StyleSpecification } from 'maplibre-gl';\nimport mitt from 'mitt';\nimport {\n\tdefineComponent,\n\tgetCurrentInstance,\n\th,\n\tmarkRaw,\n\tnextTick,\n\tonBeforeUnmount,\n\tonMounted,\n\ttype PropType,\n\tprovide,\n\tref,\n\tshallowRef,\n\ttype SlotsType,\n\tunref,\n\twatch\n} from 'vue';\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglMap',\n\tprops: {\n\t\twidth : { type: [ Number, String ] as PropType<number | string>, default: '100%' },\n\t\theight : { type: [ Number, String ] as PropType<number | string>, default: '100%' },\n\t\tattributionControl: { type: [ Boolean, Object ] as PropType<MapOptions['attributionControl']>, default: () => defaults.attributionControl },\n\t\tbearing : { type: Number as PropType<MapOptions['bearing']>, default: () => defaults.bearing },\n\t\tbearingSnap : { type: Number as PropType<MapOptions['bearingSnap']>, default: () => defaults.bearingSnap },\n\t\tbounds : { type: [ Array, Object ] as PropType<MapOptions['bounds']>, default: () => defaults.bounds },\n\t\tboxZoom : { type: Boolean as PropType<MapOptions['boxZoom']>, default: () => defaults.boxZoom },\n\n\t\tcancelPendingTileRequestsWhileZooming: {\n\t\t\ttype: Boolean as PropType<MapOptions['cancelPendingTileRequestsWhileZooming']>, default: () => defaults.cancelPendingTileRequestsWhileZooming\n\t\t},\n\n\t\tcanvasContextAttributes: { type: Object as PropType<MapOptions['canvasContextAttributes']>, default: () => defaults.canvasContextAttributes },\n\t\tcenter : { type: [ Array, Object ] as PropType<MapOptions['center']>, default: () => defaults.center },\n\t\tcenterClampedToGround : { type: Boolean as PropType<MapOptions['centerClampedToGround']>, default: () => defaults.centerClampedToGround },\n\t\tclickTolerance : { type: Number as PropType<MapOptions['clickTolerance']>, default: () => defaults.clickTolerance },\n\t\tcollectResourceTiming : { type: Boolean as PropType<MapOptions['collectResourceTiming']>, default: () => defaults.collectResourceTiming },\n\t\tcooperativeGestures : { type: [ Boolean, Object ] as PropType<MapOptions['cooperativeGestures']>, default: () => defaults.cooperativeGestures },\n\t\tcrossSourceCollisions : { type: Boolean as PropType<MapOptions['crossSourceCollisions']>, default: () => defaults.crossSourceCollisions },\n\t\tdoubleClickZoom : { type: Boolean as PropType<MapOptions['doubleClickZoom']>, default: () => defaults.doubleClickZoom },\n\t\tdragPan : { type: Boolean as PropType<MapOptions['dragPan']>, default: () => defaults.dragPan },\n\t\tdragRotate : { type: Boolean as PropType<MapOptions['dragRotate']>, default: () => defaults.dragRotate },\n\t\televation : { type: Number as PropType<MapOptions['elevation']>, default: () => defaults.elevation },\n\t\tfadeDuration : { type: Number as PropType<MapOptions['fadeDuration']>, default: () => defaults.fadeDuration },\n\t\tfitBoundsOptions : { type: Object as PropType<FitBoundsOptions>, default: () => defaults.fitBoundsOptions },\n\t\thash : { type: [ Boolean, String ] as PropType<MapOptions['hash']>, default: () => defaults.hash },\n\t\tinteractive : { type: Boolean as PropType<MapOptions['interactive']>, default: () => defaults.interactive },\n\t\tkeyboard : { type: Boolean as PropType<MapOptions['keyboard']>, default: () => defaults.keyboard },\n\t\tlanguage : { type: String as PropType<ValidLanguages | undefined>, default: () => defaults.language || undefined },\n\t\tlocale : { type: Object as PropType<MapOptions['locale']>, default: () => defaults.locale },\n\n\t\tlocalIdeographFontFamily: {\n\t\t\ttype: String as PropType<MapOptions['localIdeographFontFamily']>, default: () => defaults.localIdeographFontFamily\n\t\t},\n\n\t\tlogoPosition: { type: [ String ] as PropType<MapOptions['logoPosition']>, default: () => defaults.logoPosition },\n\t\tmapKey : { type: [ String, Symbol ] as PropType<string | symbol> },\n\t\tmaplibreLogo: { type: Boolean as PropType<MapOptions['maplibreLogo']>, default: () => defaults.maplibreLogo },\n\t\t// StyleSpecification triggers TS7056, so users must handle typings themselves\n\t\tmapStyle : { type: [ String, Object ] as PropType<object | string>, default: () => defaults.style },\n\t\tmaxBounds : { type: [ Array, Object ] as PropType<MapOptions['maxBounds']>, default: () => defaults.maxBounds },\n\t\tmaxCanvasSize : { type: Array as unknown as PropType<MapOptions['maxCanvasSize']>, default: () => defaults.maxCanvasSize },\n\t\tmaxPitch : { type: Number as PropType<MapOptions['maxPitch']>, default: () => defaults.maxPitch },\n\t\tmaxTileCacheSize : { type: Number as PropType<number>, default: () => defaults.maxTileCacheSize },\n\t\tmaxTileCacheZoomLevels: { type: Number as PropType<MapOptions['maxTileCacheZoomLevels']>, default: () => defaults.maxTileCacheZoomLevels },\n\t\tmaxZoom : { type: Number as PropType<MapOptions['maxZoom']>, default: () => defaults.maxZoom },\n\t\tminPitch : { type: Number as PropType<MapOptions['minPitch']>, default: () => defaults.minPitch },\n\t\tminZoom : { type: Number as PropType<MapOptions['minZoom']>, default: () => defaults.minZoom },\n\t\tpitch : { type: Number as PropType<MapOptions['pitch']>, default: () => defaults.pitch },\n\t\tpitchWithRotate : { type: Boolean as PropType<MapOptions['pitchWithRotate']>, default: () => defaults.pitchWithRotate },\n\t\tpixelRatio : { type: Number as PropType<MapOptions['pixelRatio']>, default: () => defaults.pixelRatio },\n\t\trefreshExpiredTiles : { type: Boolean as PropType<MapOptions['refreshExpiredTiles']>, default: () => defaults.refreshExpiredTiles },\n\t\trenderWorldCopies : { type: Boolean as PropType<MapOptions['renderWorldCopies']>, default: () => defaults.renderWorldCopies },\n\t\troll : { type: Number as PropType<MapOptions['roll']>, default: () => defaults.roll },\n\t\trollEnabled : { typed: Boolean as PropType<MapOptions['rollEnabled']>, default: () => defaults.rollEnabled },\n\t\tscrollZoom : { type: Boolean as PropType<MapOptions['scrollZoom']>, default: () => defaults.scrollZoom },\n\t\ttouchPitch : { type: Boolean as PropType<MapOptions['touchPitch']>, default: () => defaults.touchPitch },\n\t\ttouchZoomRotate : { type: Boolean as PropType<MapOptions['touchZoomRotate']>, default: () => defaults.touchZoomRotate },\n\t\ttrackResize : { type: Boolean as PropType<MapOptions['trackResize']>, default: () => defaults.trackResize },\n\t\ttransformCameraUpdate : { type: Function as PropType<NonNullable<MapOptions['transformCameraUpdate']>>, default: defaults.transformCameraUpdate },\n\t\ttransformRequest : { type: Function as PropType<NonNullable<MapOptions['transformRequest']>>, default: defaults.transformRequest },\n\t\tvalidateStyle : { type: Boolean as PropType<MapOptions['validateStyle']>, default: () => defaults.validateStyle },\n\t\tzoom : { type: Number as PropType<MapOptions['zoom']>, default: () => defaults.zoom },\n\t\tprojection : { type: Object as PropType<ProjectionSpecification> }\n\t},\n\temits: [\n\t\t'map:boxzoomcancel', 'map:boxzoomend', 'map:boxzoomstart', 'map:click', 'map:contextmenu', 'map:cooperativegestureprevented', 'map:data',\n\t\t'map:dataabort', 'map:dataloading', 'map:dblclick', 'map:drag', 'map:dragend', 'map:dragstart', 'map:error', 'map:idle', 'map:load', 'map:mousedown',\n\t\t'map:mousemove', 'map:mouseout', 'map:mouseover', 'map:mouseup', 'map:move', 'map:moveend', 'map:movestart', 'map:pitch', 'map:pitchend',\n\t\t'map:pitchstart', 'map:projectiontransition', 'map:remove', 'map:render', 'map:resize', 'map:rotate', 'map:rotateend', 'map:rotatestart',\n\t\t'map:sourcedata', 'map:sourcedataabort', 'map:sourcedataloading', 'map:styledata', 'map:styledataloading', 'map:styleimagemissing', 'map:terrain',\n\t\t'map:tiledataloading', 'map:touchcancel', 'map:touchend', 'map:touchmove', 'map:touchstart', 'map:webglcontextlost', 'map:webglcontextrestored',\n\t\t'map:wheel', 'map:zoom', 'map:zoomend', 'map:zoomstart'\n\t],\n\tslots: Object as SlotsType<{ default: {} }>,\n\tsetup(props, ctx) {\n\n\t\tconst component = markRaw(getCurrentInstance()!),\n\t\t\t container = shallowRef<HTMLDivElement>(),\n\t\t\t map = shallowRef<MaplibreMap>(),\n\t\t\t isInitialized = ref(false),\n\t\t\t isLoaded = ref(false),\n\t\t\t isStyleReady = ref(false),\n\t\t\t boundMapEvents = new Map<string, Function>(),\n\t\t\t emitter = mitt<MglEvents>(),\n\t\t\t registryItem = registerMap(component as any, map, props.mapKey);\n\n\t\tlet resizeObserver: ResizeObserver | undefined;\n\n\t\tprovide(mapSymbol, map);\n\t\tprovide(isLoadedSymbol, isLoaded);\n\t\tprovide(isInitializedSymbol, isInitialized);\n\t\tprovide(componentIdSymbol, component.uid);\n\t\tprovide(sourceIdSymbol, '');\n\t\tprovide(emitterSymbol, emitter);\n\t\tprovide(fitBoundsOptionsSymbol, props.fitBoundsOptions);\n\n\t\t/*\n\t\t * bind prop watchers\n\t\t */\n\t\twatch(() => props.bearing, v => v && map.value?.setBearing(v));\n\t\twatch(() => props.bounds, v => v && map.value?.fitBounds(v, props.fitBoundsOptions?.useOnBoundsUpdate ? props.fitBoundsOptions : undefined));\n\t\twatch(() => props.center, v => v && map.value?.setCenter(v));\n\t\twatch(() => props.maxBounds, v => v && map.value?.setMaxBounds(v));\n\t\twatch(() => props.maxPitch, v => v && map.value?.setMaxPitch(v));\n\t\twatch(() => props.maxZoom, v => v && map.value?.setMaxZoom(v));\n\t\twatch(() => props.minPitch, v => v && map.value?.setMinPitch(v));\n\t\twatch(() => props.minZoom, v => v && map.value?.setMinZoom(v));\n\t\twatch(() => props.pitch, v => v && map.value?.setPitch(v));\n\t\twatch(() => props.renderWorldCopies, v => v && map.value?.setRenderWorldCopies(v));\n\t\twatch(() => props.mapStyle, v => v && map.value?.setStyle(v as StyleSpecification | string));\n\t\twatch(() => props.transformRequest, v => v && map.value?.setTransformRequest(v));\n\t\twatch(() => props.zoom, v => v && map.value?.setZoom(v));\n\t\twatch(() => props.projection, v => v && map.value?.setProjection(v));\n\n\t\tif (props.language) {\n\t\t\tregistryItem.language = props.language;\n\t\t}\n\n\t\twatch(() => props.language, (v) => {\n\t\t\tif (isStyleReady.value && map.value && registryItem.language !== (v || undefined)) {\n\t\t\t\tsetPrimaryLanguage(map.value, v);\n\t\t\t\tregistryItem.language = v || undefined;\n\t\t\t}\n\t\t});\n\t\twatch(() => registryItem.language, (v) => {\n\t\t\tif (isStyleReady.value && map.value) {\n\t\t\t\tsetPrimaryLanguage(map.value, v);\n\t\t\t}\n\t\t});\n\n\t\tfunction onStyleReady() {\n\t\t\tisStyleReady.value = true;\n\t\t\tif (props.projection) {\n\t\t\t\tmap.value!.setProjection(props.projection);\n\t\t\t}\n\t\t}\n\n\t\tfunction onStyleLoad() {\n\t\t\tsetPrimaryLanguage(map.value!, registryItem.language);\n\t\t}\n\n\t\tfunction initialize() {\n\n\t\t\tregistryItem.isMounted = true;\n\n\t\t\t// build options\n\t\t\tconst opts: MapOptions = Object.keys(props)\n\t\t\t\t\t\t\t\t\t\t .filter(opt => (props as any)[ opt ] !== undefined && MapLib.MAP_OPTION_KEYS.indexOf(opt as keyof MapOptions) !== -1)\n\t\t\t\t\t\t\t\t\t\t .reduce<MapOptions>((obj, opt) => {\n\t\t\t\t\t\t\t\t\t\t\t (obj as any)[ opt === 'mapStyle' ? 'style' : opt ] = unref((props as any)[ opt ]);\n\t\t\t\t\t\t\t\t\t\t\t return obj;\n\t\t\t\t\t\t\t\t\t\t }, { container: container.value as HTMLDivElement } as any);\n\n\t\t\t// init map\n\t\t\tmap.value = markRaw(new MaplibreMap(opts));\n\t\t\tregistryItem.map = map.value;\n\t\t\tisInitialized.value = true;\n\t\t\tboundMapEvents.set('__load', () => {\n\t\t\t\tisLoaded.value = true;\n\t\t\t\tregistryItem.isLoaded = true;\n\t\t\t});\n\t\t\tmap.value.once('styledata', onStyleReady);\n\t\t\tmap.value.on('load', boundMapEvents.get('__load') as any);\n\t\t\tmap.value.on('style.load', onStyleLoad);\n\n\t\t\t// bind events\n\t\t\tif (component.vnode.props) {\n\t\t\t\tfor (let i = 0, len = MapLib.MAP_EVENT_TYPES.length; i < len; i++) {\n\t\t\t\t\tif (component.vnode.props[ 'onMap:' + MapLib.MAP_EVENT_TYPES[ i ] ]) {\n\t\t\t\t\t\tconst handler = MapLib.createEventHandler(component as any, map.value, ctx as any, 'map:' + MapLib.MAP_EVENT_TYPES[ i ]);\n\t\t\t\t\t\tboundMapEvents.set(MapLib.MAP_EVENT_TYPES[ i ], handler);\n\t\t\t\t\t\tmap.value.on(MapLib.MAP_EVENT_TYPES[ i ], handler);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// automatic re-initialization of map on CONTEXT_LOST_WEBGL\n\t\t\tmap.value.getCanvas().addEventListener('webglcontextlost', restart);\n\n\t\t}\n\n\t\tasync function dispose() {\n\n\t\t\tregistryItem.isMounted = false;\n\t\t\tregistryItem.isLoaded = false;\n\t\t\tisLoaded.value = false;\n\n\t\t\tif (map.value) {\n\t\t\t\t// unbind events\n\t\t\t\tmap.value.off('style.load', onStyleLoad);\n\t\t\t\tmap.value.getCanvas().removeEventListener('webglcontextlost', restart);\n\t\t\t\tmap.value._controls.forEach((control) => {\n\t\t\t\t\tmap.value!.removeControl(control);\n\t\t\t\t});\n\t\t\t\tisInitialized.value = false;\n\t\t\t\tboundMapEvents.forEach((func, en) => {\n\t\t\t\t\tmap.value!.off(en.startsWith('__') ? en.substring(2) : en, func as any);\n\t\t\t\t});\n\t\t\t\t// destroy map\n\t\t\t\tmap.value.remove();\n\t\t\t}\n\n\t\t}\n\n\t\tfunction restart() {\n\t\t\tdispose();\n\t\t\tnextTick(initialize);\n\t\t}\n\n\t\t/*\n\t\t * init map\n\t\t */\n\t\tonMounted(() => {\n\n\t\t\tinitialize();\n\n\t\t\t// bind resize observer\n\t\t\tif (map.value) {\n\t\t\t\tresizeObserver = new ResizeObserver(debounce(map.value.resize.bind(map.value), 100));\n\t\t\t\tresizeObserver.observe(container.value as HTMLDivElement);\n\t\t\t}\n\n\t\t});\n\n\t\t/*\n\t\t * Dispose component\n\t\t */\n\t\tonBeforeUnmount(() => {\n\n\t\t\t// unbind resize observer\n\t\t\tif (resizeObserver !== undefined) {\n\t\t\t\tresizeObserver.disconnect();\n\t\t\t\tresizeObserver = undefined;\n\t\t\t}\n\n\t\t\tdispose();\n\n\t\t});\n\n\t\tctx.expose({ map });\n\n\t\treturn () => h(\n\t\t\t'div',\n\t\t\t{\n\t\t\t\t'class': 'mgl-container',\n\t\t\t\tstyle : { height: props.height, width: props.width }\n\t\t\t},\n\t\t\t[\n\t\t\t\th('div', { ref: container, 'class': 'mgl-wrapper' }),\n\t\t\t\tisInitialized.value && ctx.slots.default ? ctx.slots.default({}) : undefined\n\t\t\t]\n\t\t);\n\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport { AttributionControl } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType } from 'vue';\n\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglAttributionControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tcompact : Boolean as PropType<boolean>,\n\t\tcustomAttribution: [ String, Array ] as PropType<string | string[]>\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new AttributionControl({ compact: props.compact, customAttribution: props.customAttribution });\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => isInitialized.value && map.value!.removeControl(control));\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport { FullscreenControl } from 'maplibre-gl';\nimport { defineComponent, inject, nextTick, onBeforeUnmount, type PropType } from 'vue';\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglFullscreenControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tdefault : Position.TOP_RIGHT,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tcontainer: {\n\t\t\ttype : Object as PropType<HTMLElement>,\n\t\t\tdefault: null\n\t\t},\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new FullscreenControl({ container: props.container || undefined });\n\n\t\t// fire map.resize just a 2nd time\n\t\tfunction triggerResize() {\n\t\t\tnextTick(() => map.value?.resize());\n\t\t}\n\n\t\tcontrol.on('fullscreenstart', triggerResize);\n\t\tcontrol.on('fullscreenend', triggerResize);\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => {\n\t\t\tcontrol.off('fullscreenstart', triggerResize);\n\t\t\tcontrol.off('fullscreenend', triggerResize);\n\t\t\tisInitialized.value && map.value?.removeControl(control);\n\t\t});\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport type { IControl, Map as MMap } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType } from 'vue';\n\nexport class FrameRateControl implements IControl {\n\n\tprivate frames = 0;\n\tprivate totalTime = 0;\n\tprivate totalFrames = 0;\n\n\tprivate time: number | null = null;\n\tprivate map?: MMap;\n\tprivate container?: HTMLDivElement;\n\tprivate readOutput?: HTMLDivElement;\n\tprivate canvas?: HTMLCanvasElement;\n\n\tprivate eventHandlers = new Map<string, Function>();\n\n\tconstructor(private background = 'rgba(0,0,0,0.9)',\n\t\t\t\tprivate barWidth = 4 * window.devicePixelRatio,\n\t\t\t\tprivate color = '#7cf859',\n\t\t\t\tprivate font = 'Monaco, Consolas, Courier, monospace',\n\t\t\t\tprivate graphHeight = 60 * window.devicePixelRatio,\n\t\t\t\tprivate graphWidth = 90 * window.devicePixelRatio,\n\t\t\t\tprivate graphTop = 0,\n\t\t\t\tprivate graphRight = 5 * window.devicePixelRatio,\n\t\t\t\tprivate width = 100 * window.devicePixelRatio) {\n\t}\n\n\tgetDefaultPosition(): Position {\n\t\treturn Position.TOP_RIGHT;\n\t}\n\n\tonAdd(map: MMap): HTMLElement {\n\t\tthis.map = map;\n\n\t\tconst el = (this.container = document.createElement('div'));\n\t\tel.className = 'maplibregl-ctrl maplibregl-ctrl-fps';\n\n\t\tel.style.backgroundColor = this.background;\n\t\tel.style.borderRadius = '6px';\n\n\t\tthis.readOutput = document.createElement('div');\n\t\tthis.readOutput.style.color = this.color;\n\t\tthis.readOutput.style.fontFamily = this.font;\n\t\tthis.readOutput.style.padding = '0 5px 5px';\n\t\tthis.readOutput.style.fontSize = '9px';\n\t\tthis.readOutput.style.fontWeight = 'bold';\n\t\tthis.readOutput.textContent = 'Waiting…';\n\n\t\tthis.canvas = document.createElement('canvas');\n\t\tthis.canvas.className = 'maplibregl-ctrl-canvas';\n\t\tthis.canvas.width = this.width;\n\t\tthis.canvas.height = this.graphHeight;\n\t\tthis.canvas.style.cssText = `width: ${this.width / window.devicePixelRatio}px; height: ${this.graphHeight / window.devicePixelRatio}px;`;\n\n\t\tel.appendChild(this.readOutput);\n\t\tel.appendChild(this.canvas);\n\n\t\tthis.eventHandlers.set('movestart', this.onMoveStart.bind(this));\n\t\tthis.eventHandlers.set('moveend', this.onMoveEnd.bind(this));\n\t\tthis.map.on('movestart', this.eventHandlers.get('movestart') as any);\n\t\tthis.map.on('moveend', this.eventHandlers.get('moveend') as any);\n\t\treturn this.container;\n\t}\n\n\tonRemove(): void {\n\t\tthis.map!.off('movestart', this.eventHandlers.get('movestart') as any);\n\t\tthis.map!.off('moveend', this.eventHandlers.get('moveend') as any);\n\t\tthis.eventHandlers.clear();\n\t\tthis.container!.parentNode!.removeChild(this.container!);\n\t\tthis.map = undefined;\n\t}\n\n\tonMoveStart() {\n\t\tthis.frames = 0;\n\t\tthis.time = performance.now();\n\t\tthis.eventHandlers.set('render', this.onRender.bind(this));\n\t\tthis.map!.on('render', this.eventHandlers.get('render') as any);\n\t}\n\n\tonMoveEnd() {\n\t\tconst now = performance.now();\n\t\tthis.updateGraph(this.getFPS(now));\n\t\tthis.frames = 0;\n\t\tthis.time = null;\n\t\tthis.map!.off('render', this.eventHandlers.get('render') as any);\n\t}\n\n\tonRender() {\n\t\tif (this.time) {\n\t\t\tthis.frames++;\n\t\t\tconst now = performance.now();\n\t\t\tif (now >= this.time + 1e3) {\n\t\t\t\tthis.updateGraph(this.getFPS(now));\n\t\t\t\tthis.frames = 0;\n\t\t\t\tthis.time = performance.now();\n\t\t\t}\n\t\t}\n\t}\n\n\tgetFPS(now: number) {\n\t\tthis.totalTime += now - this.time!;\n\t\tthis.totalFrames += this.frames;\n\t\treturn Math.round((1e3 * this.frames) / (now - this.time!)) || 0;\n\t}\n\n\tupdateGraph(fpsNow: number) {\n\t\tconst context = this.canvas!.getContext('2d')!;\n\t\tconst fps = Math.round((1e3 * this.totalFrames) / this.totalTime) || 0;\n\t\tconst rect = (this.graphHeight, this.barWidth);\n\n\t\tcontext.fillStyle = this.background;\n\t\tcontext.globalAlpha = 1;\n\t\tcontext.fillRect(0, 0, this.graphWidth, this.graphTop);\n\t\tcontext.fillStyle = this.color;\n\n\t\tthis.readOutput!.textContent = `${fpsNow} FPS (${fps} Avg)`;\n\t\tcontext.drawImage(\n\t\t\tthis.canvas!,\n\t\t\tthis.graphRight + rect,\n\t\t\tthis.graphTop,\n\t\t\tthis.graphWidth - rect,\n\t\t\tthis.graphHeight,\n\t\t\tthis.graphRight,\n\t\t\tthis.graphTop,\n\t\t\tthis.graphWidth - rect,\n\t\t\tthis.graphHeight\n\t\t);\n\t\tcontext.fillRect(\n\t\t\tthis.graphRight + this.graphWidth - rect,\n\t\t\tthis.graphTop,\n\t\t\trect,\n\t\t\tthis.graphHeight\n\t\t);\n\t\tcontext.fillStyle = this.background;\n\t\tcontext.globalAlpha = 0.75;\n\t\tcontext.fillRect(\n\t\t\tthis.graphRight + this.graphWidth - rect,\n\t\t\tthis.graphTop,\n\t\t\trect,\n\t\t\t(1 - fpsNow / 100) * this.graphHeight\n\t\t);\n\t}\n\n}\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglFrameRateControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tbackground : {\n\t\t\ttype : String as PropType<string>,\n\t\t\tdefault: 'rgba(0,0,0,0.9)'\n\t\t},\n\t\tbarWidth : {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 4 * window.devicePixelRatio\n\t\t},\n\t\tcolor : {\n\t\t\ttype : String as PropType<string>,\n\t\t\tdefault: '#7cf859'\n\t\t},\n\t\tfont : {\n\t\t\ttype : String as PropType<string>,\n\t\t\tdefault: 'Monaco, Consolas, Courier, monospace'\n\t\t},\n\t\tgraphHeight: {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 60 * window.devicePixelRatio\n\t\t},\n\t\tgraphWidth : {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 90 * window.devicePixelRatio\n\t\t},\n\t\tgraphTop : {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 0\n\t\t},\n\t\tgraphRight : {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 5 * window.devicePixelRatio\n\t\t},\n\t\twidth : {\n\t\t\ttype : Number as PropType<number>,\n\t\t\tdefault: 100 * window.devicePixelRatio\n\t\t}\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new FrameRateControl(\n\t\t\t\t props.background,\n\t\t\t\t props.barWidth,\n\t\t\t\t props.color,\n\t\t\t\t props.font,\n\t\t\t\t props.graphHeight,\n\t\t\t\t props.graphWidth,\n\t\t\t\t props.graphTop,\n\t\t\t\t props.graphRight,\n\t\t\t\t props.width\n\t\t\t );\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => isInitialized.value && map.value?.removeControl(control));\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport { type FitBoundsOptions, GeolocateControl } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType } from 'vue';\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglGeolocationControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tdefault : Position.TOP_RIGHT,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tpositionOptions : {\n\t\t\ttype : Object as PropType<PositionOptions>,\n\t\t\tdefault: { enableHighAccuracy: false, timeout: 6000 } as PositionOptions\n\t\t},\n\t\tfitBoundsOptions : {\n\t\t\ttype : Object as PropType<FitBoundsOptions>,\n\t\t\tdefault: { maxZoom: 15 } as FitBoundsOptions\n\t\t},\n\t\ttrackUserLocation : {\n\t\t\ttype : Boolean as PropType<boolean>,\n\t\t\tdefault: false\n\t\t},\n\t\tshowAccuracyCircle: {\n\t\t\ttype : Boolean as PropType<boolean>,\n\t\t\tdefault: true\n\t\t},\n\t\tshowUserLocation : {\n\t\t\ttype : Boolean as PropType<boolean>,\n\t\t\tdefault: true\n\t\t}\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new GeolocateControl({\n\t\t\t\t positionOptions : props.positionOptions,\n\t\t\t\t fitBoundsOptions : props.fitBoundsOptions,\n\t\t\t\t trackUserLocation : props.trackUserLocation,\n\t\t\t\t showAccuracyCircle: props.showAccuracyCircle,\n\t\t\t\t showUserLocation : props.showUserLocation\n\t\t\t });\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => isInitialized.value && map.value?.removeControl(control));\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport { NavigationControl } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType } from 'vue';\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglNavigationControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tdefault : Position.TOP_RIGHT,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tshowCompass : { type: Boolean as PropType<boolean>, default: true },\n\t\tshowZoom : { type: Boolean as PropType<boolean>, default: true },\n\t\tvisualizePitch: Boolean as PropType<boolean>\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new NavigationControl({ showCompass: props.showCompass, showZoom: props.showZoom, visualizePitch: props.visualizePitch });\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => isInitialized.value && map.value?.removeControl(control));\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { isInitializedSymbol, mapSymbol } from '@/types';\nimport { ScaleControl } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType } from 'vue';\n\nexport enum ScaleControlUnit {\n\tIMPERIAL = 'imperial',\n\tMETRIC = 'metric',\n\tNAUTICAL = 'nautical'\n}\n\ntype UnitValue = ScaleControlUnit | 'imperial' | 'metric' | 'nautical';\nconst UnitValues = Object.values(ScaleControlUnit);\n\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglScaleControl',\n\tprops: {\n\t\tposition: {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tmaxWidth: { type: Number as PropType<number>, default: 100 },\n\t\tunit : {\n\t\t\ttype : String as PropType<UnitValue>,\n\t\t\tdefault : ScaleControlUnit.METRIC,\n\t\t\tvalidator: (v: ScaleControlUnit) => {\n\t\t\t\treturn UnitValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t}\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t control = new ScaleControl({ maxWidth: props.maxWidth, unit: props.unit });\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\t\tonBeforeUnmount(() => isInitialized.value && map.value?.removeControl(control));\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import { MglButton } from '@/components';\nimport { ButtonType } from '@/components/button.component';\nimport { CustomControl } from '@/components/controls/custom.control';\nimport { Position, type PositionProp, PositionValues } from '@/components/controls/position.enum';\nimport { usePositionWatcher } from '@/composable/usePositionWatcher';\nimport { emitterSymbol, isInitializedSymbol, isLoadedSymbol, mapSymbol, type StyleSwitchItem } from '@/types';\nimport {\n\tcreateCommentVNode,\n\tcreateTextVNode,\n\tdefineComponent,\n\th,\n\tinject,\n\tonBeforeUnmount,\n\ttype PropType,\n\ttype Ref,\n\tref,\n\tshallowRef,\n\ttype SlotsType,\n\tTeleport,\n\twatch\n} from 'vue';\n\nfunction isEvent(e: any): e is Event {\n\treturn e && !!(e as Event).stopPropagation;\n}\n\ninterface SlotProps {\n\tisOpen: Ref<boolean>,\n\ttoggleOpen: (forceIsOpen?: boolean | Event, e?: Event) => void,\n\tsetStyle: (s: StyleSwitchItem) => void,\n\tmapStyles: StyleSwitchItem[],\n\tcurrentStyle: Ref<StyleSwitchItem | undefined>,\n}\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglStyleSwitchControl',\n\tprops: {\n\t\tposition : {\n\t\t\ttype : String as PropType<PositionProp>,\n\t\t\tvalidator: (v: Position) => {\n\t\t\t\treturn PositionValues.indexOf(v) !== -1;\n\t\t\t}\n\t\t},\n\t\tmapStyles : {\n\t\t\ttype : Array as PropType<StyleSwitchItem[]>,\n\t\t\trequired: true,\n\t\t\tdefault : []\n\t\t},\n\t\tmodelValue: {\n\t\t\ttype: Object as PropType<StyleSwitchItem>\n\t\t},\n\t\tisOpen : {\n\t\t\ttype : Boolean as PropType<boolean>,\n\t\t\tdefault: undefined\n\t\t}\n\t},\n\tslots: Object as SlotsType<{ default: SlotProps, button: SlotProps, styleList: SlotProps }>,\n\temits: [ 'update:modelValue', 'update:isOpen' ],\n\tsetup(props, { emit, slots }) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t isInitialized = inject(isInitializedSymbol)!,\n\t\t\t isMapLoaded = inject(isLoadedSymbol)!,\n\t\t\t emitter = inject(emitterSymbol)!,\n\t\t\t isAdded = ref(false),\n\t\t\t isOpen = ref(props.isOpen === undefined ? false : props.isOpen),\n\t\t\t modelValue = shallowRef(props.modelValue === undefined ? (props.mapStyles.length ? props.mapStyles[ 0 ] : undefined) : props.modelValue),\n\t\t\t control = new CustomControl(isAdded, false),\n\t\t\t closer = toggleOpen.bind(null, false);\n\n\t\tfunction setStyleByMap() {\n\t\t\tconst name = map.value!.getStyle().name;\n\t\t\tfor (let i = 0, len = props.mapStyles.length; i < len; i++) {\n\t\t\t\tif (props.mapStyles[ i ]!.name === name) {\n\t\t\t\t\tsetStyle(props.mapStyles[ i ]!);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\twatch(isMapLoaded, (v) => {\n\t\t\tif (v) setStyleByMap();\n\t\t}, { immediate: true });\n\t\tmap.value!.on('style.load', setStyleByMap);\n\t\tdocument.addEventListener('click', closer);\n\n\n\t\tusePositionWatcher(() => props.position, map, control);\n\n\t\tif (props.modelValue !== undefined) {\n\t\t\twatch(() => props.modelValue, v => {\n\t\t\t\tif (v !== undefined) modelValue.value = v;\n\t\t\t});\n\t\t}\n\t\tif (props.isOpen !== undefined) {\n\t\t\twatch(() => props.isOpen, v => {\n\t\t\t\tif (v !== undefined) isOpen.value = v;\n\t\t\t});\n\t\t}\n\n\t\tonBeforeUnmount(() => {\n\t\t\tif (isInitialized.value) {\n\t\t\t\tmap.value!.removeControl(control);\n\t\t\t\tmap.value!.off('style.load', setStyleByMap);\n\t\t\t}\n\t\t\tdocument.removeEventListener('click', closer);\n\t\t});\n\n\t\tfunction setStyle(s: StyleSwitchItem) {\n\t\t\tif (modelValue.value?.name === s.name) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\temitter.emit('styleSwitched', s);\n\n\t\t\t/*\n\t\t\t * Skip diff as long as Maplibre-GL doesn't fie `style.load` correctly\n\t\t\t * @see https://github.com/maplibre/maplibre-gl-js/issues/2587\n\t\t\t*/\n\t\t\tmap.value!.setStyle(s.style, { diff: false });\n\t\t\tif (props.modelValue === undefined) {\n\t\t\t\tmodelValue.value = s;\n\t\t\t}\n\t\t\temit('update:modelValue', s);\n\n\t\t\ttoggleOpen(false);\n\t\t}\n\n\t\tfunction toggleOpen(forceIsOpen?: boolean | Event, e?: Event) {\n\t\t\tif (isEvent(e)) {\n\t\t\t\te.stopPropagation();\n\t\t\t} else if (isEvent(forceIsOpen)) {\n\t\t\t\tforceIsOpen.stopPropagation();\n\t\t\t}\n\t\t\tif (props.isOpen !== undefined && props.isOpen === forceIsOpen || isOpen.value === forceIsOpen) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (props.isOpen === undefined) {\n\t\t\t\tisOpen.value = typeof forceIsOpen === 'boolean' ? forceIsOpen : !isOpen.value;\n\t\t\t\temit('update:isOpen', isOpen.value);\n\t\t\t} else {\n\t\t\t\temit('update:isOpen', typeof forceIsOpen === 'boolean' ? forceIsOpen : !props.isOpen);\n\t\t\t}\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (!isAdded.value) {\n\t\t\t\treturn createCommentVNode('style-switch-control');\n\t\t\t}\n\n\t\t\tconst slotProps: SlotProps = {\n\t\t\t\tisOpen, toggleOpen, setStyle,\n\t\t\t\tmapStyles : props.mapStyles,\n\t\t\t\tcurrentStyle: modelValue,\n\t\t\t};\n\n\t\t\treturn h(\n\t\t\t\tTeleport as any,\n\t\t\t\t{ to: control.container },\n\t\t\t\tslots.default\n\t\t\t\t\t? slots.default(slotProps)\n\t\t\t\t\t: [\n\t\t\t\t\t\tslots.button\n\t\t\t\t\t\t\t? slots.button(slotProps)\n\t\t\t\t\t\t\t: h(MglButton, {\n\t\t\t\t\t\t\t\ttype : ButtonType.MDI,\n\t\t\t\t\t\t\t\tpath : 'M12,18.54L19.37,12.8L21,14.07L12,21.07L3,14.07L4.62,12.81L12,18.54M12,16L3,9L12,2L21,9L12,16M12,4.53L6.26,9L12,13.47L17.74,9L12,4.53Z',\n\t\t\t\t\t\t\t\t'class': [ 'maplibregl-ctrl-icon maplibregl-style-switch', isOpen.value ? 'is-open' : '' ],\n\t\t\t\t\t\t\t\tonClick: toggleOpen.bind(null, true)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tslots.styleList\n\t\t\t\t\t\t\t? slots.styleList(slotProps)\n\t\t\t\t\t\t\t: h(\n\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t{ 'class': [ 'maplibregl-style-list', isOpen.value ? 'is-open' : '' ] },\n\t\t\t\t\t\t\t\tprops.mapStyles.map((s) => {\n\t\t\t\t\t\t\t\t\treturn s.icon\n\t\t\t\t\t\t\t\t\t\t? h(MglButton, {\n\t\t\t\t\t\t\t\t\t\t\ttype : ButtonType.MDI,\n\t\t\t\t\t\t\t\t\t\t\tpath : s.icon.path,\n\t\t\t\t\t\t\t\t\t\t\t'class': modelValue.value?.name === s.name ? 'is-active' : '',\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => setStyle(s)\n\t\t\t\t\t\t\t\t\t\t}, createTextVNode(s.label))\n\t\t\t\t\t\t\t\t\t\t: h('button', {\n\t\t\t\t\t\t\t\t\t\t\ttype : 'button',\n\t\t\t\t\t\t\t\t\t\t\t'class': modelValue.value?.name === s.name ? 'is-active' : '',\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => setStyle(s)\n\t\t\t\t\t\t\t\t\t\t}, createTextVNode(s.label));\n\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t)\n\t\t\t\t\t]\n\t\t\t);\n\t\t};\n\n\t},\n\t// just only for code assist\n\ttemplate: `\n\t\t<slot>\n\t\t\t<slot name=\"button\"></slot>\n\t\t\t<slot name=\"styleList\"></slot>\n\t\t</slot>\n\t`\n});\n\n","import { MapLib } from '@/lib/map.lib';\nimport { mapSymbol } from '@/types';\nimport { type LngLatLike, Marker, type MarkerOptions, type PointLike, type PositionAnchor } from 'maplibre-gl';\nimport { defineComponent, inject, onBeforeUnmount, type PropType, unref, watch } from 'vue';\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglMarker',\n\tprops: {\n\t\tcoordinates: {\n\t\t\ttype : [ Object, Array ] as unknown as PropType<LngLatLike>,\n\t\t\trequired: true\n\t\t},\n\t\toffset : [ Object, Array ] as PropType<PointLike>,\n\t\tanchor : String as PropType<PositionAnchor>,\n\t\tcolor : String as PropType<string>,\n\t\t// draggable : Boolean as PropType<boolean>, todo implement feature\n\t\tclickTolerance : Number as PropType<number>,\n\t\trotation : Number as PropType<number>,\n\t\trotationAlignment: String as PropType<'map' | 'viewport' | 'auto'>,\n\t\tpitchAlignment : String as PropType<'map' | 'viewport' | 'auto'>,\n\t\tscale : Number as PropType<number>\n\t},\n\tsetup(props) {\n\n\t\tconst map = inject(mapSymbol)!,\n\t\t\t opts: MarkerOptions = Object.keys(props)\n\t\t\t\t\t\t\t\t\t\t .filter(opt => (props as any)[ opt ] !== undefined && MapLib.MARKER_OPTION_KEYS.indexOf(opt as keyof MarkerOptions) !== -1)\n\t\t\t\t\t\t\t\t\t\t .reduce((obj, opt) => {\n\t\t\t\t\t\t\t\t\t\t\t (obj as any)[ opt ] = unref((props as any)[ opt ]);\n\t\t\t\t\t\t\t\t\t\t\t return obj;\n\t\t\t\t\t\t\t\t\t\t }, {});\n\n\t\tconst marker = new Marker(opts);\n\t\tmarker.setLngLat(props.coordinates).addTo(map.value!);\n\n\t\twatch(() => props.coordinates, v => marker.setLngLat(v));\n\t\t// watch(() => props.draggable, v => marker.setDraggable(v || false));\n\t\twatch(() => props.offset, v => marker.setOffset(v || [ 0, 0 ]));\n\t\twatch(() => props.pitchAlignment, v => marker.setPitchAlignment(v || 'auto'));\n\t\twatch(() => props.rotationAlignment, v => marker.setRotationAlignment(v || 'auto'));\n\n\t\tonBeforeUnmount(marker.remove.bind(marker));\n\n\t\treturn { marker };\n\n\t},\n\trender() {\n\t\t// nothing\n\t}\n});\n","import type { Source } from 'maplibre-gl';\nimport { ref, type Ref, unref } from 'vue';\n\nexport class SourceLib {\n\n\tprivate static readonly REFS = new Map<string, Ref<Source | undefined | null>>();\n\n\tstatic genSourceOpts<T extends object, O extends object>(type: string, props: object, sourceOpts: Array<keyof O>): T {\n\n\t\treturn Object.keys(props)\n\t\t\t\t\t .filter(opt => (props as any)[ opt ] !== undefined && sourceOpts.indexOf(opt as any) !== -1)\n\t\t\t\t\t .reduce((obj, opt) => {\n\t\t\t\t\t\t (obj as any)[ opt ] = unref((props as any)[ opt ]);\n\t\t\t\t\t\t return obj;\n\t\t\t\t\t }, { type } as T);\n\n\t}\n\n\tstatic getSourceRef<T extends Source>(mcid: number, source: any): Ref<T | undefined | null> {\n\n\t\tconst isString = typeof source === 'string',\n\t\t\t key = String(mcid) + (isString ? source : '');\n\t\tlet r = SourceLib.REFS.get(key);\n\t\tif (!r) {\n\t\t\tr = ref(isString ? null : undefined);\n\t\t\tSourceLib.REFS.set(key, r);\n\t\t}\n\t\treturn r as Ref<T | undefined | null>;\n\n\t}\n\n}\n","export type SourceLayerRegistryHandler = () => void\n\nexport class SourceLayerRegistry {\n\n\tprivate unmountHandlers = new Map<string, SourceLayerRegistryHandler>();\n\n\tregisterUnmountHandler(id: string, handler: SourceLayerRegistryHandler) {\n\t\tthis.unmountHandlers.set(id, handler);\n\t}\n\n\tunregisterUnmountHandler(id: string) {\n\t\tthis.unmountHandlers.delete(id);\n\t}\n\n\tunmount() {\n\t\tthis.unmountHandlers.forEach((h) => h());\n\t}\n\n}\n","import { SourceLib } from '@/lib/source.lib';\nimport { SourceLayerRegistry } from '@/lib/sourceLayer.registry';\nimport { componentIdSymbol, emitterSymbol, isLoadedSymbol, mapSymbol, sourceIdSymbol, sourceLayerRegistry } from '@/types';\nimport type { Source, SourceSpecification } from 'maplibre-gl';\nimport { inject, onBeforeUnmount, provide, type Ref, watch } from 'vue';\n\nexport function useSource<T extends Source, O extends object>(\n\tprops: any,\n\ttype: string,\n\tsourceOpts: Array<keyof O>,\n): Ref<T | null | undefined> {\n\n\tconst map = inject(mapSymbol)!,\n\t\t isLoaded = inject(isLoadedSymbol)!,\n\t\t emitter = inject(emitterSymbol)!;\n\n\tconst cid = inject(componentIdSymbol)!,\n\t\t source = SourceLib.getSourceRef<T>(cid, props.sourceId),\n\t\t registry = new SourceLayerRegistry();\n\n\tprovide(sourceIdSymbol, props.sourceId);\n\tprovide(sourceLayerRegistry, registry);\n\n\n\tfunction addSource() {\n\t\tif (isLoaded.value) {\n\t\t\tmap.value!.addSource(props.sourceId, SourceLib.genSourceOpts<object, O>(type, props, sourceOpts) as SourceSpecification);\n\t\t\tsource.value = map.value!.getSource(props.sourceId) as T;\n\t\t}\n\t}\n\n\tfunction resetSource() {\n\t\tsource.value = null;\n\t}\n\n\twatch(isLoaded, addSource, { immediate: true });\n\tmap.value!.on('style.load', addSource);\n\temitter.on('styleSwitched', resetSource);\n\n\tonBeforeUnmount(() => {\n\t\tif (isLoaded.value) {\n\t\t\tregistry.unmount();\n\t\t\tmap.value!.removeSource(props.sourceId);\n\t\t}\n\t\tmap.value!.off('style.load', addSource);\n\t\temitter.off('styleSwitched', resetSource);\n\t});\n\n\treturn source;\n\n}\n","import { useSource } from '@/composable/useSource';\nimport { AllSourceOptions } from '@/types';\nimport type { CanvasSource, CanvasSourceSpecification, Coordinates } from 'maplibre-gl';\nimport { createCommentVNode, defineComponent, isRef, type PropType, type SlotsType, watch } from 'vue';\n\nconst sourceOpts = AllSourceOptions<CanvasSourceSpecification>({\n\tanimate : undefined,\n\tcanvas : undefined,\n\tcoordinates: undefined,\n});\n\nexport default /*#__PURE__*/ defineComponent({\n\tname : 'MglCanvasSource',\n\tprops: {\n\t\tsourceId : {\n\t\t\ttype : String as PropType<string>,\n\t\t\trequired: true\n\t\t},\n\t\tcoordinates: Array as unknown as PropType<CanvasSourceSpecification['coordinates']>,\n\t\tanimate : Boolean as PropType<CanvasSourceSpecification['animate']>