@maptiler/sdk
Version:
The Javascript & TypeScript map SDK tailored for MapTiler Cloud
1 lines • 1.28 MB
Source Map (JSON)
{"version":3,"file":"maptiler-sdk.mjs","sources":["../src/MLAdapters/Marker.ts","../src/MLAdapters/Popup.ts","../src/MLAdapters/Style.ts","../src/MLAdapters/CanvasSource.ts","../src/MLAdapters/GeoJSONSource.ts","../src/MLAdapters/ImageSource.ts","../src/MLAdapters/RasterTileSource.ts","../src/MLAdapters/RasterDEMTileSource.ts","../src/MLAdapters/VectorTileSource.ts","../src/MLAdapters/VideoSource.ts","../src/MLAdapters/NavigationControl.ts","../src/MLAdapters/GeolocateControl.ts","../src/MLAdapters/AttributionControl.ts","../src/MLAdapters/LogoControl.ts","../src/MLAdapters/ScaleControl.ts","../src/MLAdapters/FullscreenControl.ts","../src/MLAdapters/TerrainControl.ts","../src/MLAdapters/BoxZoomHandler.ts","../src/MLAdapters/ScrollZoomHandler.ts","../src/MLAdapters/CooperativeGesturesHandler.ts","../src/MLAdapters/KeyboardHandler.ts","../src/MLAdapters/TwoFingersTouchPitchHandler.ts","../src/MLAdapters/MapWheelEvent.ts","../src/MLAdapters/MapTouchEvent.ts","../src/MLAdapters/MapMouseEvent.ts","../src/language.ts","../src/constants/defaults.ts","../src/config.ts","../src/controls/MaptilerLogoControl.ts","../src/caching.ts","../src/tools.ts","../node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs","../src/mapstyle.ts","../src/utils/dom.ts","../src/controls/MaptilerTerrainControl.ts","../src/controls/MaptilerNavigationControl.ts","../src/controls/MaptilerGeolocateControl.ts","../src/controls/MaptilerCustomControl.ts","../src/controls/MaptilerProjectionControl.ts","../src/controls/MaptilerExternalControl.ts","../src/controls/Minimap.ts","../src/Telemetry.ts","../node_modules/gl-matrix/esm/common.js","../node_modules/gl-matrix/esm/mat4.js","../node_modules/gl-matrix/esm/vec3.js","../node_modules/color-name/index.js","../node_modules/color-convert/conversions.js","../node_modules/color-convert/route.js","../node_modules/color-convert/index.js","../src/utils/webgl-utils.ts","../src/custom-layers/CubemapLayer/constants.ts","../src/custom-layers/CubemapLayer/cubemap.vert.glsl?raw","../src/custom-layers/CubemapLayer/cubemap.frag.glsl?raw","../src/custom-layers/CubemapLayer/types.ts","../src/custom-layers/CubemapLayer/loadCubemapTexture.ts","../src/utils/math-utils.ts","../src/utils/object.ts","../src/custom-layers/CubemapLayer/CubemapLayer.ts","../src/custom-layers/RadialGradientLayer/radialGradient.vert.glsl?raw","../src/custom-layers/RadialGradientLayer/radialGradient.frag.glsl?raw","../src/custom-layers/RadialGradientLayer/RadialGradientLayer.ts","../src/Map.ts","../src/ImageViewer/events.ts","../src/utils/errors.ts","../src/ImageViewer/monkeyPatchML.ts","../src/controls/ImageViewerFitImageToBoundsControl.ts","../src/ImageViewer/symbols.ts","../src/ImageViewer/ImageViewer.ts","../src/ImageViewer/ImageViewerMarker.ts","../src/converters/xml.ts","../src/helpers/screenshot.ts","../src/helpers/stylehelper.ts","../src/ColorRamp.ts","../src/helpers/vectorlayerhelpers.ts","../src/index.ts"],"sourcesContent":["/**\n * This is an extension of MapLibre Marker to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class Marker extends maplibregl.Marker {\n addTo(map: SDKMap | MapMLGL): this {\n return super.addTo(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre Popup to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class Popup extends maplibregl.Popup {\n addTo(map: SDKMap | MapMLGL): this {\n return super.addTo(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre Style to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL, StyleOptions } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class Style extends maplibregl.Style {\n constructor(map: SDKMap, options: StyleOptions = {}) {\n super(map as MapMLGL, options);\n }\n}\n","/**\n * This is an extension of MapLibre CanvasSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class CanvasSource extends maplibregl.CanvasSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre GeoJSONSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class GeoJSONSource extends maplibregl.GeoJSONSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre ImageSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class ImageSource extends maplibregl.ImageSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre RasterTileSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class RasterTileSource extends maplibregl.RasterTileSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre RasterDEMTileSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class RasterDEMTileSource extends maplibregl.RasterDEMTileSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre VectorTileSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class VectorTileSource extends maplibregl.VectorTileSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre VideoSource to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class VideoSource extends maplibregl.VideoSource {\n onAdd(map: SDKMap | MapMLGL) {\n super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre NavigationControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class NavigationControl extends maplibregl.NavigationControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre GeolocateControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class GeolocateControl extends maplibregl.GeolocateControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre AttributionControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class AttributionControl extends maplibregl.AttributionControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre LogoControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class LogoControl extends maplibregl.LogoControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre ScaleControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class ScaleControl extends maplibregl.ScaleControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre FullscreenControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class FullscreenControl extends maplibregl.FullscreenControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre TerrainControl to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class TerrainControl extends maplibregl.TerrainControl {\n onAdd(map: SDKMap | MapMLGL) {\n return super.onAdd(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class BoxZoomHandler extends maplibregl.BoxZoomHandler {\n constructor(\n map: SDKMap | MapMLGL,\n options: {\n clickTolerance: number;\n },\n ) {\n super(map as MapMLGL, options);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class ScrollZoomHandler extends maplibregl.ScrollZoomHandler {\n constructor(map: SDKMap | MapMLGL, triggerRenderFrame: () => void) {\n super(map as MapMLGL, triggerRenderFrame);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { GestureOptions, Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class CooperativeGesturesHandler extends maplibregl.CooperativeGesturesHandler {\n constructor(map: SDKMap | MapMLGL, options: GestureOptions) {\n super(map as MapMLGL, options);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class KeyboardHandler extends maplibregl.KeyboardHandler {\n constructor(map: SDKMap | MapMLGL) {\n super(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class TwoFingersTouchPitchHandler extends maplibregl.TwoFingersTouchPitchHandler {\n constructor(map: SDKMap | MapMLGL) {\n super(map as MapMLGL);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class MapWheelEvent extends maplibregl.MapWheelEvent {\n constructor(type: string, map: SDKMap | MapMLGL, originalEvent: WheelEvent) {\n super(type, map as MapMLGL, originalEvent);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class MapTouchEvent extends maplibregl.MapTouchEvent {\n constructor(type: string, map: SDKMap | MapMLGL, originalEvent: TouchEvent) {\n super(type, map as MapMLGL, originalEvent);\n }\n}\n","/**\n * This is an extension of MapLibre BoxZoomHandler to make it fully type compatible with the SDK\n */\n\nimport maplibregl from \"maplibre-gl\";\nimport type { Map as MapMLGL } from \"maplibre-gl\";\nimport type { Map as SDKMap } from \"../Map\";\n\nexport class MapMouseEvent extends maplibregl.MapMouseEvent {\n constructor(type: string, map: SDKMap | MapMLGL, originalEvent: MouseEvent, data: Record<string, unknown> = {}) {\n super(type, map as MapMLGL, originalEvent, data);\n }\n}\n","import { Language as LanguageFromClient, getLanguageInfoFromCode, type LanguageInfo } from \"@maptiler/client\";\n\n// Adding some language entries that are specific to the SDK\nconst Language = {\n /**\n * Language mode to display labels in both the local language and the language of the visitor's device, concatenated.\n * Note that if those two languages are the same, labels won't be duplicated.\n */\n VISITOR: {\n code: null,\n flag: \"visitor\",\n name: \"Visitor\",\n latin: true,\n isMode: true,\n geocoding: false,\n } as LanguageInfo,\n\n /**\n * Language mode to display labels in both the local language and English, concatenated.\n * Note that if those two languages are the same, labels won't be duplicated.\n */\n VISITOR_ENGLISH: {\n code: null,\n flag: \"visitor_en\",\n name: \"Visitor English\",\n latin: true,\n isMode: true,\n geocoding: false,\n } as LanguageInfo,\n\n /**\n * Language mode to display labels in a language enforced in the style.\n */\n STYLE: {\n code: null,\n flag: \"style\",\n name: \"Style\",\n latin: false,\n isMode: true,\n geocoding: false,\n } as LanguageInfo,\n\n /**\n * Language mode to display labels in a language enforced in the style. The language cannot be further modified.\n */\n STYLE_LOCK: {\n code: null,\n flag: \"style_lock\",\n name: \"Style Lock\",\n latin: false,\n isMode: true,\n geocoding: false,\n } as LanguageInfo,\n\n ...LanguageFromClient,\n} as const;\n\n/**\n * Get the browser language\n */\nexport function getBrowserLanguage(): LanguageInfo {\n if (typeof navigator === \"undefined\") {\n const code = Intl.DateTimeFormat().resolvedOptions().locale.split(\"-\")[0];\n\n const langInfo = getLanguageInfoFromCode(code);\n\n if (langInfo) return langInfo;\n return Language.ENGLISH;\n }\n\n const canditatelangs = Array.from(new Set(navigator.languages.map((l) => l.split(\"-\")[0])))\n .map((code) => getLanguageInfoFromCode(code))\n .filter((li) => li);\n\n return canditatelangs[0] ?? Language.LOCAL;\n}\n\nexport { Language, type LanguageInfo };\n","import { Language } from \"../language\";\n\n/**\n * Some default settings for the SDK\n */\nconst defaults = {\n maptilerLogoURL: \"https://api.maptiler.com/resources/logo.svg\",\n maptilerURL: \"https://www.maptiler.com/\",\n maptilerApiHost: \"api.maptiler.com\",\n telemetryURL: \"https://api.maptiler.com/metrics\",\n rtlPluginURL: \"https://cdn.maptiler.com/mapbox-gl-rtl-text/v0.2.3/mapbox-gl-rtl-text.min.js\",\n primaryLanguage: Language.STYLE,\n secondaryLanguage: Language.LOCAL,\n terrainSourceURL: \"https://api.maptiler.com/tiles/terrain-rgb-v2/tiles.json\",\n terrainSourceId: \"maptiler-terrain\",\n};\n\nObject.freeze(defaults);\n\nexport { defaults };\n","import EventEmitter from \"events\";\nimport type { LanguageInfo } from \"./language\";\nimport { config as clientConfig, type FetchFunction } from \"@maptiler/client\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { Unit } from \"./types\";\nimport { defaults } from \"./constants/defaults\";\n\nexport const MAPTILER_SESSION_ID = uuidv4();\n\n/**\n * Configuration class for the SDK\n */\nclass SdkConfig extends EventEmitter {\n /**\n * The primary language. By default, the language of the web browser is used.\n */\n primaryLanguage: LanguageInfo = defaults.primaryLanguage;\n\n /**\n * The secondary language, to overwrite the default language defined in the map style.\n * This settings is highly dependant on the style compatibility and may not work in most cases.\n */\n secondaryLanguage?: LanguageInfo;\n\n /**\n * Setting on whether of not the SDK runs with a session logic.\n * A \"session\" is started at the initialization of the SDK and finished when the browser\n * page is being refreshed.\n * When `session` is enabled (default: true), the extra URL param `mtsid` is added to queries\n * on the MapTiler Cloud API. This allows MapTiler to enable \"session based billing\".\n */\n session = true;\n\n /**\n * Enables client-side caching of requests for tiles and fonts.\n * The cached requests persist multiple browser sessions and will be reused when possible.\n * Works only for requests to the MapTiler Cloud API when sessions are enabled.\n */\n caching = true;\n\n /**\n * Telemetry is enabled by default but can be opted-out by setting this value to `false`.\n * The telemetry is very valuable to the team at MapTiler because it shares information\n * about where to add the extra effort. It also helps spotting some incompatibility issues\n * that may arise between the SDK and a specific version of a module.\n *\n * It consists in sending metrics about usage of the following features:\n * - SDK version [string]\n * - API key [string]\n * - MapTiler sesion ID (if opted-in) [string]\n * - if tile caching is enabled [boolean]\n * - if language specified at initialization [boolean]\n * - if terrain is activated at initialization [boolean]\n * - if globe projection is activated at initialization [boolean]\n *\n * In addition, each official module will be added to a list, alongside its version number.\n */\n telemetry = true;\n\n /**\n * Unit to be used\n */\n private _unit: Unit = \"metric\";\n\n /**\n * MapTiler Cloud API key\n */\n private _apiKey = \"\";\n\n /**\n * Set the unit system\n */\n set unit(u: Unit) {\n this._unit = u;\n this.emit(\"unit\", u);\n }\n\n /**\n * Get the unit system\n */\n get unit(): Unit {\n return this._unit;\n }\n\n /**\n * Set the MapTiler Cloud API key\n */\n set apiKey(k: string) {\n this._apiKey = k;\n clientConfig.apiKey = k;\n this.emit(\"apiKey\", k);\n }\n\n /**\n * Get the MapTiler Cloud API key\n */\n get apiKey(): string {\n return this._apiKey;\n }\n\n /**\n * Set a the custom fetch function to replace the default one\n */\n set fetch(f: FetchFunction) {\n clientConfig.fetch = f;\n }\n\n /**\n * Get the fetch fucntion\n */\n get fetch(): FetchFunction | null {\n return clientConfig.fetch;\n }\n}\n\nconst config = new SdkConfig();\n\nexport { config, SdkConfig };\n","import type { LogoControlOptions as LogoControlOptionsML } from \"maplibre-gl\";\nimport { defaults } from \"../constants/defaults\";\nimport { LogoControl } from \"../MLAdapters/LogoControl\";\nimport type { Map as SDKMap } from \"../Map\";\n\ntype LogoControlOptions = LogoControlOptionsML & {\n logoURL?: string;\n linkURL?: string;\n};\n\n/**\n * This LogoControl extends the MapLibre LogoControl but instead can use any image URL and\n * any link URL. By default this is using MapTiler logo and URL.\n */\nexport class MaptilerLogoControl extends LogoControl {\n declare _compact: boolean;\n private logoURL = \"\";\n private linkURL = \"\";\n\n constructor(options: LogoControlOptions = {}) {\n super(options);\n\n this.logoURL = options.logoURL ?? defaults.maptilerLogoURL;\n this.linkURL = options.linkURL ?? defaults.maptilerURL;\n }\n\n onAdd(map: SDKMap): HTMLElement {\n this._map = map;\n this._compact = this.options.compact ?? false;\n this._container = window.document.createElement(\"div\");\n this._container.className = \"maplibregl-ctrl\";\n const anchor = window.document.createElement(\"a\");\n anchor.style.backgroundRepeat = \"no-repeat\";\n anchor.style.cursor = \"pointer\";\n anchor.style.display = \"block\";\n anchor.style.height = \"23px\";\n anchor.style.margin = \"0 0 -4px -4px\";\n anchor.style.overflow = \"hidden\";\n anchor.style.width = \"88px\";\n anchor.style.backgroundImage = `url(${this.logoURL})`;\n anchor.style.backgroundSize = \"100px 30px\";\n anchor.style.width = \"100px\";\n anchor.style.height = \"30px\";\n\n anchor.target = \"_blank\";\n anchor.rel = \"noopener\";\n anchor.href = this.linkURL;\n anchor.setAttribute(\"aria-label\", \"MapTiler logo\");\n anchor.setAttribute(\"rel\", \"noopener\");\n this._container.appendChild(anchor);\n this._container.style.display = \"block\";\n\n this._map.on(\"resize\", this._updateCompact);\n this._updateCompact();\n\n return this._container;\n }\n}\n","import type { GetResourceResponse, RequestParameters, ResourceType } from \"maplibre-gl\";\n\nimport { config } from \"./config\";\n\nimport maplibregl from \"maplibre-gl\";\n\nimport { defaults } from \"./constants/defaults\";\n\nconst LOCAL_CACHE_PROTOCOL_SOURCE = \"localcache_source\";\nconst LOCAL_CACHE_PROTOCOL_DATA = \"localcache\";\nconst LOCAL_CACHE_NAME = \"maptiler_sdk\";\n\nconst CACHE_LIMIT_ITEMS = 1000;\nconst CACHE_LIMIT_CHECK_INTERVAL = 100;\nexport const CACHE_API_AVAILABLE = typeof caches !== \"undefined\";\n\nconst { addProtocol } = maplibregl;\n\nexport function localCacheTransformRequest(reqUrl: URL, resourceType?: ResourceType): string {\n if (CACHE_API_AVAILABLE && config.caching && config.session && reqUrl.host === defaults.maptilerApiHost) {\n if (resourceType === \"Source\" && reqUrl.href.includes(\"tiles.json\")) {\n return reqUrl.href.replace(\"https://\", `${LOCAL_CACHE_PROTOCOL_SOURCE}://`);\n }\n\n if (resourceType === \"Tile\" || resourceType === \"Glyphs\") {\n return reqUrl.href.replace(\"https://\", `${LOCAL_CACHE_PROTOCOL_DATA}://`);\n }\n }\n return reqUrl.href;\n}\n\nlet cacheInstance: Cache;\n\nasync function getCache() {\n if (!cacheInstance) {\n cacheInstance = await caches.open(LOCAL_CACHE_NAME);\n }\n return cacheInstance;\n}\n\nlet cachePutCounter = 0;\nasync function limitCache() {\n const cache = await getCache();\n const keys = await cache.keys();\n const toPurge = keys.slice(0, Math.max(keys.length - CACHE_LIMIT_ITEMS, 0));\n for (const key of toPurge) {\n cache.delete(key);\n }\n}\n\nexport function registerLocalCacheProtocol() {\n addProtocol(\n LOCAL_CACHE_PROTOCOL_SOURCE,\n async (\n params: RequestParameters,\n abortController: AbortController,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<GetResourceResponse<any>> => {\n if (!params.url) throw new Error(\"\");\n\n params.url = params.url.replace(`${LOCAL_CACHE_PROTOCOL_SOURCE}://`, \"https://\");\n\n const requestInit: RequestInit = params;\n requestInit.signal = abortController.signal;\n const response = await fetch(params.url, requestInit);\n const json = await response.json();\n\n if (json.tiles && json.tiles.length > 0) {\n // move `Last-Modified` to query so it propagates to tile URLs\n json.tiles[0] += `&last-modified=${response.headers.get(\"Last-Modified\")}`;\n }\n\n return {\n data: json,\n cacheControl: response.headers.get(\"Cache-Control\"),\n expires: response.headers.get(\"Expires\"),\n };\n },\n );\n addProtocol(LOCAL_CACHE_PROTOCOL_DATA, async (params: RequestParameters, abortController: AbortController): Promise<GetResourceResponse<any>> => {\n if (!params.url) throw new Error(\"\");\n\n params.url = params.url.replace(`${LOCAL_CACHE_PROTOCOL_DATA}://`, \"https://\");\n\n const url = new URL(params.url);\n\n const cacheableUrl = new URL(url);\n cacheableUrl.searchParams.delete(\"mtsid\");\n cacheableUrl.searchParams.delete(\"key\");\n const cacheKey = cacheableUrl.toString();\n\n const fetchableUrl = new URL(url);\n fetchableUrl.searchParams.delete(\"last-modified\");\n const fetchUrl = fetchableUrl.toString();\n\n const respond = async (response: Response): Promise<GetResourceResponse<any>> => {\n return {\n data: await response.arrayBuffer(),\n cacheControl: response.headers.get(\"Cache-Control\"),\n expires: response.headers.get(\"Expires\"),\n };\n };\n\n const cache = await getCache();\n const cacheMatch = await cache.match(cacheKey);\n\n if (cacheMatch) {\n return respond(cacheMatch);\n }\n\n const requestInit: RequestInit = params;\n requestInit.signal = abortController.signal;\n const response = await fetch(fetchUrl, requestInit);\n if (response.status >= 200 && response.status < 300) {\n cache.put(cacheKey, response.clone()).catch(() => {\n // \"DOMException: Cache.put() was aborted\"\n // can happen here because the response is not done streaming yet\n });\n if (++cachePutCounter > CACHE_LIMIT_CHECK_INTERVAL) {\n limitCache();\n cachePutCounter = 0;\n }\n }\n return respond(response);\n });\n}\n","import maplibregl from \"maplibre-gl\";\nimport type { RequestParameters, ResourceType, RequestTransformFunction, SymbolLayerSpecification } from \"maplibre-gl\";\nimport { defaults } from \"./constants/defaults\";\nimport { config } from \"./config\";\nimport { MAPTILER_SESSION_ID } from \"./config\";\nimport { localCacheTransformRequest } from \"./caching\";\nimport type { Map as MapSDK } from \"./Map\";\n\n// TODO These function should gradually be moved to\n// to utils directory\n\nexport async function enableRTL(customPluginURL?: string) {\n // Prevent this from running server side\n if (typeof window === \"undefined\") return;\n\n const status = maplibregl.getRTLTextPluginStatus();\n\n if (status === \"unavailable\" || status === \"requested\") {\n try {\n await maplibregl.setRTLTextPlugin(customPluginURL ?? defaults.rtlPluginURL, true);\n } catch (_e) {\n console.error(\"Error enabling RTL plugin. It is enabled by default and cannot be unset after. Are you attempting to enable it twice?\", _e);\n }\n }\n}\n\n// This comes from:\n// https://github.com/maplibre/maplibre-gl-js/blob/v2.4.0/src/util/util.ts#L223\nexport function bindAll(fns: Array<string>, context: any): void {\n for (const fn of fns) {\n if (typeof context[fn] !== \"function\") continue;\n context[fn] = context[fn].bind(context);\n }\n}\n\n/**\n * This function is meant to be used as transformRequest by any Map instance created.\n * It adds the session ID as well as the MapTiler Cloud key from the config to all the requests\n * performed on MapTiler Cloud servers.\n */\nexport function maptilerCloudTransformRequest(url: string, resourceType?: ResourceType): RequestParameters {\n let reqUrl = null;\n\n try {\n // The URL is expected to be absolute.\n // Yet, if it's local we just return it without assuming a 'base' url (in the URL constructor)\n // and we let the URL be locally resolved with a potential base path.\n reqUrl = new URL(url);\n } catch (_e) {\n return {\n url,\n };\n }\n\n if (reqUrl.host === defaults.maptilerApiHost) {\n if (!reqUrl.searchParams.has(\"key\")) {\n reqUrl.searchParams.append(\"key\", config.apiKey);\n }\n\n if (config.session) {\n reqUrl.searchParams.append(\"mtsid\", MAPTILER_SESSION_ID);\n }\n }\n\n const localCacheTransformedReq = localCacheTransformRequest(reqUrl, resourceType);\n\n return {\n url: localCacheTransformedReq,\n };\n}\n\n/**\n * This combines a user-defined tranformRequest function (optionnal)\n * with the MapTiler Cloud-specific one: maptilerCloudTransformRequest\n */\nexport function combineTransformRequest(userDefinedRTF?: RequestTransformFunction | null): RequestTransformFunction {\n return (url: string, resourceType?: ResourceType): RequestParameters => {\n if (userDefinedRTF !== undefined && userDefinedRTF !== null) {\n const rp = userDefinedRTF(url, resourceType);\n const rp2 = maptilerCloudTransformRequest(rp?.url ?? \"\", resourceType);\n\n return {\n ...rp,\n ...rp2,\n };\n }\n\n return maptilerCloudTransformRequest(url, resourceType);\n };\n}\n\n/**\n * Generate a random string. Handy to create random IDs\n */\nexport function generateRandomString(): string {\n return Math.random().toString(36).substring(2);\n}\n\n/**\n * Check if a given string is in a uuid format\n */\nexport function isUUID(s: string): boolean {\n // Regular expression to check if string is a valid UUID\n const regexExp = /^[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}$/gi;\n return regexExp.test(s);\n}\n\n/**\n * Attempt a JSON parse of a string but does not throw if the string is not valid JSON, returns `null` instead.\n */\nexport function jsonParseNoThrow<T>(doc: string): T | null {\n try {\n return JSON.parse(doc);\n } catch (_e) {\n // pass\n }\n\n return null;\n}\n\n/**\n * Simple function to check if an object is a GeoJSON\n */\nexport function isValidGeoJSON<T>(obj: T & { type: string }): boolean {\n if (typeof obj !== \"object\" || Array.isArray(obj) || obj === null) return false;\n if (!(\"type\" in obj)) return false;\n\n const validTypes = [\"Feature\", \"FeatureCollection\", \"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\", \"Polygon\", \"MultiPolygon\", \"GeometryCollection\"];\n\n if (validTypes.includes(obj.type)) return true;\n return false;\n}\n\n/**\n * This function tests if WebGL2 is supported. Since it can be for a different reasons that WebGL2 is\n * not supported but we do not have an action to take based on the reason, this function return null\n * if there is no error (WebGL is supported), or returns a string with the error message if WebGL2 is\n * not supported.\n */\nexport function getWebGLSupportError(): string | null {\n const gl = document.createElement(\"canvas\").getContext(\"webgl2\");\n if (!gl) {\n if (typeof WebGL2RenderingContext !== \"undefined\") {\n return \"Graphic rendering with WebGL2 has been disabled or is not supported by your graphic card. The map cannot be displayed.\";\n }\n return \"Your browser does not support graphic rendering with WebGL2. The map cannot be displayed.\";\n }\n return null;\n}\n\n/**\n * Display an error message in the Map div if WebGL2 is not supported\n */\nexport function displayNoWebGlWarning(container: HTMLElement | string) {\n const webglError = getWebGLSupportError();\n\n if (!webglError) return;\n\n let actualContainer: HTMLElement | null = null;\n\n if (typeof container === \"string\") {\n actualContainer = document.getElementById(container);\n } else if (container instanceof HTMLElement) {\n actualContainer = container;\n }\n\n if (!actualContainer) {\n throw new Error(\"The Map container must be provided.\");\n }\n\n const errorMessageDiv = document.createElement(\"div\");\n errorMessageDiv.innerHTML = webglError;\n errorMessageDiv.classList.add(\"webgl-warning-div\");\n actualContainer.appendChild(errorMessageDiv);\n throw new Error(webglError);\n}\n\n/**\n * Display a warning message in the Map div if the WebGL context was lost\n */\nexport function displayWebGLContextLostWarning(map: MapSDK) {\n const webglError = \"The WebGL context was lost.\";\n const container: HTMLElement = map.getContainer();\n const errorMessageDiv = document.createElement(\"div\");\n errorMessageDiv.innerHTML = webglError;\n errorMessageDiv.classList.add(\"webgl-warning-div\");\n container.appendChild(errorMessageDiv);\n}\n\n/**\n * Return true if the provided piece of style expression has the form [\"get\", \"name:XX\"]\n * with XX being a 2-letter is code for languages\n */\nfunction isGetNameLanguage(subExpr: unknown, localized: boolean): boolean {\n if (!Array.isArray(subExpr)) return false;\n if (subExpr.length !== 2) return false;\n if (subExpr[0] !== \"get\") return false;\n if (typeof subExpr[1] !== \"string\") return false;\n if (localized && !subExpr[1].startsWith(\"name:\")) return false;\n if (!localized && subExpr[1] !== \"name\") return false;\n\n return true;\n}\n\n/**\n * In a text-field style property (as an object, not a string) the languages that are specified as\n * [\"get\", \"name:XX\"] are replaced by the proved replacer (also an object).\n * This replacement happened regardless of how deep in the object the flag is.\n * Note that it does not replace the occurences of [\"get\", \"name\"] (local names)\n */\nexport function changeFirstLanguage(\n origExpr: maplibregl.ExpressionSpecification,\n replacer: maplibregl.ExpressionSpecification,\n localized: boolean,\n): maplibregl.ExpressionSpecification {\n const expr = structuredClone(origExpr) as maplibregl.ExpressionSpecification;\n\n const exploreNode = (subExpr: maplibregl.ExpressionSpecification | string) => {\n if (typeof subExpr === \"string\") return;\n\n for (let i = 0; i < subExpr.length; i += 1) {\n if (isGetNameLanguage(subExpr[i], localized)) {\n subExpr[i] = structuredClone(replacer);\n } else {\n exploreNode(subExpr[i] as maplibregl.ExpressionSpecification | string);\n }\n }\n };\n\n // The provided expression could be directly a [\"get\", \"name:xx\"]\n if (isGetNameLanguage(expr, localized)) {\n return replacer;\n }\n\n exploreNode(expr);\n return expr;\n}\n\n/**\n * If `localized` is `true`, it checks for the pattern \"{name:xx}\" (with \"xx\" being a language iso string).\n * If `localized` is `false`, it check for {name}.\n * In a exact way or is a loose way (such as \"foo {name:xx}\" or \"foo {name} bar\")\n */\nexport function checkNamePattern(str: string, localized: boolean): { contains: boolean; exactMatch: boolean } {\n const regex = localized ? /\\{name:\\S+\\}/ : /\\{name\\}/;\n return {\n contains: regex.test(str),\n exactMatch: new RegExp(`^${regex.source}$`).test(str),\n };\n}\n\n/**\n * Replaces the occurences of {name:xx} in a string by a provided object-expression to return a concat object expression\n */\nexport function replaceLanguage(origLang: string, newLang: maplibregl.ExpressionSpecification, localized: boolean): maplibregl.ExpressionSpecification {\n const regex = localized ? /\\{name:\\S+\\}/ : /\\{name\\}/;\n const elementsToConcat = origLang.split(regex);\n\n const allElements = elementsToConcat.flatMap((item, i) => (i === elementsToConcat.length - 1 ? [item] : [item, newLang]));\n\n const expr = [\"concat\", ...allElements] as maplibregl.ExpressionSpecification;\n return expr;\n}\n\n/**\n * Find languages used in string label definition.\n * The returned array contains languages such as \"en\", \"fr\" but\n * can also contain null that stand for the use of {name}\n */\nexport function findLanguageStr(str: string): Array<string | null> {\n const regex = /\\{name(?::(?<language>\\S+))?\\}/g;\n const languageUsed = [] as Array<string | null>;\n\n while (true) {\n const match = regex.exec(str);\n if (!match) break;\n\n // The is a match\n const language = match.groups?.language ?? null;\n\n // The language is non-null if provided {name:xx}\n // but if provided {name} then language will be null\n languageUsed.push(language);\n }\n return languageUsed;\n}\n\nfunction isGetNameLanguageAndFind(subExpr: unknown): { isLanguage: boolean; localization: string | null } | null {\n // Not language expression\n if (!Array.isArray(subExpr)) return null;\n if (subExpr.length !== 2) return null;\n if (subExpr[0] !== \"get\") return null;\n if (typeof subExpr[1] !== \"string\") return null;\n\n // Is non localized language\n if (subExpr[1].trim() === \"name\") {\n return {\n isLanguage: true,\n localization: null,\n };\n }\n\n // Is a localized language\n if (subExpr[1].trim().startsWith(\"name:\")) {\n return {\n isLanguage: true,\n localization: subExpr[1].trim().split(\":\").pop()!,\n };\n }\n\n return null;\n}\n\n/**\n * Find languages used in object label definition.\n * The returned array contains languages such as \"en\", \"fr\" but\n * can also contain null that stand for the use of {name}\n */\nexport function findLanguageObj(origExpr: maplibregl.ExpressionSpecification): Array<string | null> {\n const languageUsed = [] as Array<string | null>;\n const expr = structuredClone(origExpr) as maplibregl.ExpressionSpecification;\n\n const exploreNode = (subExpr: maplibregl.ExpressionSpecification | string | Array<maplibregl.ExpressionSpecification | string>) => {\n if (typeof subExpr === \"string\") return;\n\n for (let i = 0; i < subExpr.length; i += 1) {\n const result = isGetNameLanguageAndFind(subExpr[i]);\n if (result) {\n languageUsed.push(result.localization);\n } else {\n exploreNode(subExpr[i] as maplibregl.ExpressionSpecification | string);\n }\n }\n };\n\n exploreNode([expr]);\n return languageUsed;\n}\n\nexport function computeLabelsLocalizationMetrics(layers: maplibregl.LayerSpecification[], map: MapSDK): { unlocalized: number; localized: Record<string, number> } {\n const languages: Array<string | null>[] = [];\n\n for (const genericLayer of layers) {\n // Only symbole layer can have a layout with text-field\n if (genericLayer.type !== \"symbol\") {\n continue;\n }\n\n const layer = genericLayer as SymbolLayerSpecification;\n const { id, layout } = layer;\n\n if (!layout) {\n continue;\n }\n\n if (!(\"text-field\" in layout)) {\n continue;\n }\n\n const textFieldLayoutProp: string | maplibregl.ExpressionSpecification = map.getLayoutProperty(id, \"text-field\");\n\n if (!textFieldLayoutProp) {\n continue;\n }\n\n if (typeof textFieldLayoutProp === \"string\") {\n const l = findLanguageStr(textFieldLayoutProp);\n languages.push(l);\n } else {\n const l = findLanguageObj(textFieldLayoutProp);\n languages.push(l);\n }\n }\n\n const flatLanguages = languages.flat();\n const localizationMetrics: {\n unlocalized: number;\n localized: Record<string, number>;\n } = {\n unlocalized: 0,\n localized: {},\n };\n\n for (const lang of flatLanguages) {\n if (lang === null) {\n localizationMetrics.unlocalized += 1;\n } else {\n if (!(lang in localizationMetrics.localized)) {\n localizationMetrics.localized[lang] = 0;\n }\n localizationMetrics.localized[lang] += 1;\n }\n }\n return localizationMetrics;\n}\n","var $version = 8;\nvar $root = {\n\tversion: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: [\n\t\t\t8\n\t\t]\n\t},\n\tname: {\n\t\ttype: \"string\"\n\t},\n\tmetadata: {\n\t\ttype: \"*\"\n\t},\n\tcenter: {\n\t\ttype: \"array\",\n\t\tvalue: \"number\"\n\t},\n\tcenterAltitude: {\n\t\ttype: \"number\"\n\t},\n\tzoom: {\n\t\ttype: \"number\"\n\t},\n\tbearing: {\n\t\ttype: \"number\",\n\t\t\"default\": 0,\n\t\tperiod: 360,\n\t\tunits: \"degrees\"\n\t},\n\tpitch: {\n\t\ttype: \"number\",\n\t\t\"default\": 0,\n\t\tunits: \"degrees\"\n\t},\n\troll: {\n\t\ttype: \"number\",\n\t\t\"default\": 0,\n\t\tunits: \"degrees\"\n\t},\n\tstate: {\n\t\ttype: \"state\",\n\t\t\"default\": {\n\t\t}\n\t},\n\tlight: {\n\t\ttype: \"light\"\n\t},\n\tsky: {\n\t\ttype: \"sky\"\n\t},\n\tprojection: {\n\t\ttype: \"projection\"\n\t},\n\tterrain: {\n\t\ttype: \"terrain\"\n\t},\n\tsources: {\n\t\trequired: true,\n\t\ttype: \"sources\"\n\t},\n\tsprite: {\n\t\ttype: \"sprite\"\n\t},\n\tglyphs: {\n\t\ttype: \"string\"\n\t},\n\ttransition: {\n\t\ttype: \"transition\"\n\t},\n\tlayers: {\n\t\trequired: true,\n\t\ttype: \"array\",\n\t\tvalue: \"layer\"\n\t}\n};\nvar sources = {\n\t\"*\": {\n\t\ttype: \"source\"\n\t}\n};\nvar source = [\n\t\"source_vector\",\n\t\"source_raster\",\n\t\"source_raster_dem\",\n\t\"source_geojson\",\n\t\"source_video\",\n\t\"source_image\"\n];\nvar source_vector = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvector: {\n\t\t\t}\n\t\t}\n\t},\n\turl: {\n\t\ttype: \"string\"\n\t},\n\ttiles: {\n\t\ttype: \"array\",\n\t\tvalue: \"string\"\n\t},\n\tbounds: {\n\t\ttype: \"array\",\n\t\tvalue: \"number\",\n\t\tlength: 4,\n\t\t\"default\": [\n\t\t\t-180,\n\t\t\t-85.051129,\n\t\t\t180,\n\t\t\t85.051129\n\t\t]\n\t},\n\tscheme: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\txyz: {\n\t\t\t},\n\t\t\ttms: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"xyz\"\n\t},\n\tminzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 0\n\t},\n\tmaxzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 22\n\t},\n\tattribution: {\n\t\ttype: \"string\"\n\t},\n\tpromoteId: {\n\t\ttype: \"promoteId\"\n\t},\n\tvolatile: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\t\"*\": {\n\t\ttype: \"*\"\n\t}\n};\nvar source_raster = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\traster: {\n\t\t\t}\n\t\t}\n\t},\n\turl: {\n\t\ttype: \"string\"\n\t},\n\ttiles: {\n\t\ttype: \"array\",\n\t\tvalue: \"string\"\n\t},\n\tbounds: {\n\t\ttype: \"array\",\n\t\tvalue: \"number\",\n\t\tlength: 4,\n\t\t\"default\": [\n\t\t\t-180,\n\t\t\t-85.051129,\n\t\t\t180,\n\t\t\t85.051129\n\t\t]\n\t},\n\tminzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 0\n\t},\n\tmaxzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 22\n\t},\n\ttileSize: {\n\t\ttype: \"number\",\n\t\t\"default\": 512,\n\t\tunits: \"pixels\"\n\t},\n\tscheme: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\txyz: {\n\t\t\t},\n\t\t\ttms: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"xyz\"\n\t},\n\tattribution: {\n\t\ttype: \"string\"\n\t},\n\tvolatile: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\t\"*\": {\n\t\ttype: \"*\"\n\t}\n};\nvar source_raster_dem = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\t\"raster-dem\": {\n\t\t\t}\n\t\t}\n\t},\n\turl: {\n\t\ttype: \"string\"\n\t},\n\ttiles: {\n\t\ttype: \"array\",\n\t\tvalue: \"string\"\n\t},\n\tbounds: {\n\t\ttype: \"array\",\n\t\tvalue: \"number\",\n\t\tlength: 4,\n\t\t\"default\": [\n\t\t\t-180,\n\t\t\t-85.051129,\n\t\t\t180,\n\t\t\t85.051129\n\t\t]\n\t},\n\tminzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 0\n\t},\n\tmaxzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 22\n\t},\n\ttileSize: {\n\t\ttype: \"number\",\n\t\t\"default\": 512,\n\t\tunits: \"pixels\"\n\t},\n\tattribution: {\n\t\ttype: \"string\"\n\t},\n\tencoding: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tterrarium: {\n\t\t\t},\n\t\t\tmapbox: {\n\t\t\t},\n\t\t\tcustom: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"mapbox\"\n\t},\n\tredFactor: {\n\t\ttype: \"number\",\n\t\t\"default\": 1\n\t},\n\tblueFactor: {\n\t\ttype: \"number\",\n\t\t\"default\": 1\n\t},\n\tgreenFactor: {\n\t\ttype: \"number\",\n\t\t\"default\": 1\n\t},\n\tbaseShift: {\n\t\ttype: \"number\",\n\t\t\"default\": 0\n\t},\n\tvolatile: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\t\"*\": {\n\t\ttype: \"*\"\n\t}\n};\nvar source_geojson = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tgeojson: {\n\t\t\t}\n\t\t}\n\t},\n\tdata: {\n\t\trequired: true,\n\t\ttype: \"*\"\n\t},\n\tmaxzoom: {\n\t\ttype: \"number\",\n\t\t\"default\": 18\n\t},\n\tattribution: {\n\t\ttype: \"string\"\n\t},\n\tbuffer: {\n\t\ttype: \"number\",\n\t\t\"default\": 128,\n\t\tmaximum: 512,\n\t\tminimum: 0\n\t},\n\tfilter: {\n\t\ttype: \"*\"\n\t},\n\ttolerance: {\n\t\ttype: \"number\",\n\t\t\"default\": 0.375\n\t},\n\tcluster: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\tclusterRadius: {\n\t\ttype: \"number\",\n\t\t\"default\": 50,\n\t\tminimum: 0\n\t},\n\tclusterMaxZoom: {\n\t\ttype: \"number\"\n\t},\n\tclusterMinPoints: {\n\t\ttype: \"number\"\n\t},\n\tclusterProperties: {\n\t\ttype: \"*\"\n\t},\n\tlineMetrics: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\tgenerateId: {\n\t\ttype: \"boolean\",\n\t\t\"default\": false\n\t},\n\tpromoteId: {\n\t\ttype: \"promoteId\"\n\t}\n};\nvar source_video = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvideo: {\n\t\t\t}\n\t\t}\n\t},\n\turls: {\n\t\trequired: true,\n\t\ttype: \"array\",\n\t\tvalue: \"string\"\n\t},\n\tcoordinates: {\n\t\trequired: true,\n\t\ttype: \"array\",\n\t\tlength: 4,\n\t\tvalue: {\n\t\t\ttype: \"array\",\n\t\t\tlength: 2,\n\t\t\tvalue: \"number\"\n\t\t}\n\t}\n};\nvar source_image = {\n\ttype: {\n\t\trequired: true,\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\timage: {\n\t\t\t}\n\t\t}\n\t},\n\turl: {\n\t\trequired: true,\n\t\ttype: \"string\"\n\t},\n\tcoordinates: {\n\t\trequired: true,\n\t\ttype: \"array\",\n\t\tlength: 4,\n\t\tvalue: {\n\t\t\ttype: \"array\",\n\t\t\tlength: 2,\n\t\t\tvalue: \"number\"\n\t\t}\n\t}\n};\nvar layer = {\n\tid: {\n\t\ttype: \"string\",\n\t\trequired: true\n\t},\n\ttype: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tfill: {\n\t\t\t},\n\t\t\tline: {\n\t\t\t},\n\t\t\tsymbol: {\n\t\t\t},\n\t\t\tcircle: {\n\t\t\t},\n\t\t\theatmap: {\n\t\t\t},\n\t\t\t\"fill-extrusion\": {\n\t\t\t},\n\t\t\traster: {\n\t\t\t},\n\t\t\thillshade: {\n\t\t\t},\n\t\t\t\"color-relief\": {\n\t\t\t},\n\t\t\tbackground: {\n\t\t\t}\n\t\t},\n\t\trequired: true\n\t},\n\tmetadata: {\n\t\ttype: \"*\"\n\t},\n\tsource: {\n\t\ttype: \"string\"\n\t},\n\t\"source-layer\": {\n\t\ttype: \"string\"\n\t},\n\tminzoom: {\n\t\ttype: \"number\",\n\t\tminimum: 0,\n\t\tmaximum: 24\n\t},\n\tmaxzoom: {\n\t\ttype: \"number\",\n\t\tminimum: 0,\n\t\tmaximum: 24\n\t},\n\tfilter: {\n\t\ttype: \"filter\"\n\t},\n\tlayout: {\n\t\ttype: \"layout\"\n\t},\n\tpaint: {\n\t\ttype: \"paint\"\n\t}\n};\nvar layout = [\n\t\"layout_fill\",\n\t\"layout_line\",\n\t\"layout_circle\",\n\t\"layout_heatmap\",\n\t\"layout_fill-extrusion\",\n\t\"layout_symbol\",\n\t\"layout_raster\",\n\t\"layout_hillshade\",\n\t\"layout_color-relief\",\n\t\"layout_background\"\n];\nvar layout_background = {\n\tvisibility: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvisible: {\n\t\t\t},\n\t\t\tnone: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"visible\",\n\t\t\"property-type\": \"constant\"\n\t}\n};\nvar layout_fill = {\n\t\"fill-sort-key\": {\n\t\ttype: \"number\",\n\t\texpression: {\n\t\t\tinterpolated: false,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\",\n\t\t\t\t\"feature\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-driven\"\n\t},\n\tvisibility: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvisible: {\n\t\t\t},\n\t\t\tnone: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"visible\",\n\t\t\"property-type\": \"constant\"\n\t}\n};\nvar layout_circle = {\n\t\"circle-sort-key\": {\n\t\ttype: \"number\",\n\t\texpression: {\n\t\t\tinterpolated: false,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\",\n\t\t\t\t\"feature\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-driven\"\n\t},\n\tvisibility: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvisible: {\n\t\t\t},\n\t\t\tnone: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"visible\",\n\t\t\"property-type\": \"constant\"\n\t}\n};\nvar layout_heatmap = {\n\tvisibility: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvisible: {\n\t\t\t},\n\t\t\tnone: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"visible\",\n\t\t\"property-type\": \"constant\"\n\t}\n};\nvar layout_line = {\n\t\"line-cap\": {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tbutt: {\n\t\t\t},\n\t\t\tround: {\n\t\t\t},\n\t\t\tsquare: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"butt\",\n\t\texpression: {\n\t\t\tinterpolated: false,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-constant\"\n\t},\n\t\"line-join\": {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tbevel: {\n\t\t\t},\n\t\t\tround: {\n\t\t\t},\n\t\t\tmiter: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"miter\",\n\t\texpression: {\n\t\t\tinterpolated: false,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\",\n\t\t\t\t\"feature\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-driven\"\n\t},\n\t\"line-miter-limit\": {\n\t\ttype: \"number\",\n\t\t\"default\": 2,\n\t\trequires: [\n\t\t\t{\n\t\t\t\t\"line-join\": \"miter\"\n\t\t\t}\n\t\t],\n\t\texpression: {\n\t\t\tinterpolated: true,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-constant\"\n\t},\n\t\"line-round-limit\": {\n\t\ttype: \"number\",\n\t\t\"default\": 1.05,\n\t\trequires: [\n\t\t\t{\n\t\t\t\t\"line-join\": \"round\"\n\t\t\t}\n\t\t],\n\t\texpression: {\n\t\t\tinterpolated: true,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-constant\"\n\t},\n\t\"line-sort-key\": {\n\t\ttype: \"number\",\n\t\texpression: {\n\t\t\tinterpolated: false,\n\t\t\tparameters: [\n\t\t\t\t\"zoom\",\n\t\t\t\t\"feature\"\n\t\t\t]\n\t\t},\n\t\t\"property-type\": \"data-driven\"\n\t},\n\tvisibility: {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tvisible: {\n\t\t\t},\n\t\t\tnone: {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"visible\",\n\t\t\"property-type\": \"constant\"\n\t}\n};\nvar layout_symbol = {\n\t\"symbol-placement\": {\n\t\ttype: \"enum\",\n\t\tvalues: {\n\t\t\tpoint: {\n\t\t\t},\n\t\t\tline: {\n\t\t\t},\n\t\t\t\"line-center\": {\n\t\t\t}\n\t\t},\n\t\t\"default\": \"point\",\n\t\texpress