@tomtom-org/maps-sdk 0.45.5 → 0.45.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"map.es.js","sources":["../src/shared/errorMessages.ts","../src/shared/imageUtils.ts","../src/shared/resources/index.ts","../src/shared/resources/pin.svg?raw","../src/shared/assertionUtils.ts","../src/shared/TomTomMapSource.ts","../src/shared/SourceWithLayers.ts","../src/shared/mapUtils.ts","../src/shared/AbstractMapModule.ts","../src/shared/EventsModule.ts","../src/shared/AbstractEventProxy.ts","../src/shared/eventUtils.ts","../src/shared/EventsProxy.ts","../src/shared/layers/layerIDs.ts","../src/shared/layers/sourcesIDs.ts","../src/pois/layers/poisLayers.ts","../src/pois/poiCategoryMapping.ts","../src/shared/layers/commonLayerProps.ts","../src/shared/layers/symbolLayers.ts","../src/shared/layers/utils.ts","../src/places/utils/customIconScales.ts","../src/places/utils/evAvailabilityHelpers.ts","../src/places/utils/textOffsetCalculator.ts","../src/places/utils/layerConfiguration.ts","../src/places/utils/themeAdaptation.ts","../src/shared/layers/eventState.ts","../src/places/utils/layerSpecBuilders.ts","../src/places/layers/placesLayers.ts","../src/places/resources/index.ts","../src/places/utils/toPinImageID.ts","../src/places/utils/preparePlacesForDisplay.ts","../src/places/PlacesModule.ts","../src/shared/mapLibreFilterUtils.ts","../src/pois/poiCategoryGroups.ts","../src/pois/POIsModule.ts","../src/base/layerGroups.ts","../src/base/BaseMapModule.ts","../src/base/types/baseMapModuleConfig.ts","../src/geometry/types/geometryDisplayProps.ts","../src/geometry/layers/geometryLayers.ts","../src/geometry/prepareGeometryForDisplay.ts","../src/geometry/GeometriesModule.ts","../src/hillshade/HillshadeModule.ts","../src/init/types/mapInit.ts","../src/routing/layers/shared.ts","../src/routing/layers/chargingStopLayers.ts","../src/routing/layers/guidanceLayers.ts","../src/routing/layers/routeFerrySectionLayers.ts","../src/routing/layers/routeMainLineLayers.ts","../src/routing/layers/routeTollRoadLayers.ts","../src/routing/layers/routeTrafficSectionLayers.ts","../src/routing/layers/routeTunnelSectionLayers.ts","../src/routing/layers/routeVehicleRestrictedLayers.ts","../src/routing/layers/summaryBubbleLayers.ts","../src/routing/types/waypointDisplayProps.ts","../src/routing/layers/waypointLayers.ts","../src/routing/layers/routingLayers.ts","../src/routing/resources/index.ts","../src/routing/resources/instruction-line-arrow.svg?raw","../src/routing/resources/summary-map-bubble.svg?raw","../src/routing/resources/traffic.svg?raw","../src/routing/util/config.ts","../src/routing/util/displayChargingStops.ts","../src/routing/util/displayTrafficSectionProps.ts","../src/routing/util/displayWaypoints.ts","../src/routing/util/routeSections.ts","../src/routing/util/routeSelection.ts","../src/routing/util/routes.ts","../src/routing/RoutingModule.ts","../src/routing/resources/start.svg?raw","../src/routing/resources/circle.svg?raw","../src/routing/resources/finish.svg?raw","../src/routing/util/guidance.ts","../src/init/styleInputBuilder.ts","../src/TomTomMap.ts","../src/init/buildMapOptions.ts","../src/shared/localization.ts","../src/traffic/types/trafficModuleConfig.ts","../src/traffic/filters/trafficFilters.ts","../src/traffic/TrafficFlowModule.ts","../src/traffic/TrafficIncidentsModule.ts","../src/utils/paddedBounds.ts"],"sourcesContent":["import type { StyleModule } from '../init';\n\n/**\n * @ignore\n */\nexport const notInTheStyle = (actionText: string): Error =>\n new Error(`Trying to ${actionText} while it is not in the map style. Did you exclude it when loading the map?`);\n\n/**\n * @ignore\n */\nexport const cannotAddStyleModuleToCustomStyle = (styleModule: StyleModule): Error =>\n new Error(`Trying to add style module ${styleModule} to the custom style!`);\n","/**\n * @ignore\n */\nexport const isDOMImageSupported = (): boolean =>\n typeof document != 'undefined' && typeof DOMParser != 'undefined' && typeof btoa != 'undefined';\n\n/**\n * @ignore\n */\nexport const svgToImg = (svgDomElement: SVGElement): HTMLImageElement => {\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const img = document.createElement('img');\n img.src = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgDomElement))}`;\n return img;\n};\n","import { SVGIconStyleOptions } from '../types';\nimport pinSvgRaw from './pin.svg?raw';\n\n/**\n * Helper function to parse SVG string (typically imported from ...svg?raw) to SVGElement\n * @ignore\n */\nexport const parseSvg = (svgString: string): SVGElement =>\n new DOMParser().parseFromString(svgString, 'image/svg+xml').documentElement as unknown as SVGElement;\n\n/**\n * @ignore\n */\nexport const pinSvg = (options: SVGIconStyleOptions | undefined): SVGElement => {\n const element = parseSvg(pinSvgRaw);\n // see pin.svg structure\n if (options?.fillColor) {\n element.querySelector('#background')?.setAttribute('fill', options.fillColor);\n }\n if (options?.outlineColor) {\n element.querySelector('#outline')?.setAttribute('fill', options.outlineColor);\n }\n if (options?.outlineOpacity !== undefined) {\n element.querySelector('#outline')?.setAttribute('fill-opacity', options.outlineOpacity.toString());\n }\n return element;\n};\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path id=\\\"background\\\" d=\\\"M63.95 137.516c6.556-12.737 19.098-21.09 30.828-29.388C110.046 97.324 120 79.603 120 59.574 120 26.671 93.138 0 60 0S0 26.671 0 59.574c0 20.029 9.954 37.75 25.22 48.55 11.734 8.302 24.274 16.652 30.83 29.388 1.706 3.316 6.194 3.316 7.9 0z\\\"\\n fill=\\\"#1988cf\\\"/>\\n <path id=\\\"outline\\\" d=\\\"M91.298 103.272C105.052 93.544 114 77.596 114 59.574c0-29.61-24.176-53.617-54-53.617S6 29.964 6 59.574c0 18.022 8.95 33.97 22.7 43.698l2.27 1.601c10.172 7.153 21.922 15.423 29.03 27.416 7.104-11.993 18.858-20.259 29.03-27.416l2.268-1.601zm1.468 6.274c-11.13 7.845-22.632 15.957-28.816 27.966-1.706 3.316-6.194 3.316-7.9 0-6.182-12.009-17.686-20.121-28.816-27.966l-2.016-1.422C9.954 97.324 0 79.603 0 59.574 0 26.671 26.862 0 60 0s60 26.671 60 59.574c0 20.029-9.954 37.75-25.222 48.55l-2.016 1.422z\\\"\\n fill-opacity=\\\".3\\\"/>\\n</svg>\\n\"","import { isNil } from 'lodash-es';\n\n/**\n * Throws an error if the given value is not defined.\n * @ignore\n */\nexport function assertDefined<T>(value: T | null | undefined): asserts value is T {\n if (isNil(value)) {\n throw new Error(`${value} must not be null/undefined. Something went wrong with its initialization.`);\n }\n}\n\n/**\n * Returns the given value as its defined type, or throws an error if it isn't defined.\n * @ignore\n * @throws\n */\nexport const asDefined = <T>(value: T | null | undefined): T => {\n assertDefined(value);\n return value;\n};\n","import type { Map, Source, SourceSpecification } from 'maplibre-gl';\n\n/**\n * Contains a source relevant for TomTom Maps SDK JS.\n * * The source might already be initialized from the map style, or it might be initialized here.\n * @ignore\n */\nexport class TomTomMapSource<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> {\n constructor(\n readonly id: string,\n readonly spec?: SOURCE_SPEC,\n public runtimeSource?: RUNTIME_SOURCE,\n ) {}\n\n ensureAddedToMap(map: Map) {\n if (!this.runtimeSource) {\n if (!map.getSource(this.id) && this.spec) {\n map.addSource(this.id, this.spec);\n }\n this.runtimeSource = map.getSource(this.id) as RUNTIME_SOURCE;\n }\n }\n}\n","import type { FeatureCollection } from 'geojson';\nimport type {\n GeoJSONSource,\n GeoJSONSourceSpecification,\n LayerSpecification,\n Map,\n Source,\n SourceSpecification,\n} from 'maplibre-gl';\nimport { asDefined } from './assertionUtils';\nimport { TomTomMapSource } from './TomTomMapSource';\nimport type {\n ByIdOrIndex,\n CleanEventStateOptions,\n CleanEventStatesOptions,\n LayerSpecFilter,\n LayerSpecWithSource,\n PutEventStateOptions,\n SourceWithLayerIDs,\n SourceWithLayers,\n ToBeAddedLayerSpec,\n ToBeAddedLayerSpecWithoutSource,\n} from './types';\n\n/**\n * Contains a source and the layers to render its data.\n * @ignore\n */\nexport abstract class AbstractSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n LAYER_SPEC extends LayerSpecification = LayerSpecification,\n> {\n readonly _layerSpecs: LAYER_SPEC[];\n private _sourceAndLayerIDs!: SourceWithLayerIDs;\n\n constructor(\n readonly map: Map,\n readonly source: TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>,\n layerSpecs: LAYER_SPEC[],\n ) {\n this._layerSpecs = layerSpecs;\n this._updateSourceAndLayerIDs();\n }\n\n isAnyLayerVisible(filter?: LayerSpecFilter): boolean {\n return this.getLayerSpecs(filter).some((layer) => this.isLayerVisible(layer));\n }\n\n areAllLayersVisible(filter?: LayerSpecFilter): boolean {\n return this.getLayerSpecs(filter).every((layer) => this.isLayerVisible(layer));\n }\n\n private getLayerSpecs(filter?: LayerSpecFilter) {\n return filter ? this._layerSpecs.filter(filter) : this._layerSpecs;\n }\n\n _updateSourceAndLayerIDs(): void {\n this._sourceAndLayerIDs = { sourceID: this.source.id, layerIDs: this._layerSpecs.map((layer) => layer.id) };\n }\n\n private isLayerVisible(layer: LayerSpecification): boolean {\n return this.map.getLayoutProperty(layer.id, 'visibility') !== 'none';\n }\n\n setLayersVisible(visible: boolean, filter?: LayerSpecFilter): void {\n for (const layerSpec of this.getLayerSpecs(filter)) {\n this.map.setLayoutProperty(layerSpec.id, 'visibility', visible ? 'visible' : 'none', { validate: false });\n }\n }\n\n get sourceAndLayerIDs(): SourceWithLayerIDs {\n return this._sourceAndLayerIDs;\n }\n\n equalSourceAndLayerIDs(other: SourceWithLayers): boolean {\n return (\n this.sourceAndLayerIDs.sourceID === other.sourceAndLayerIDs.sourceID &&\n this.sourceAndLayerIDs.layerIDs.length === other.sourceAndLayerIDs.layerIDs.length &&\n this.sourceAndLayerIDs.layerIDs.every((id, index) => id === other.sourceAndLayerIDs.layerIDs[index])\n );\n }\n}\n\n/**\n * @ignore\n * @param loadedMap\n * @param sourceIDs\n */\nexport const filterLayersBySources = (loadedMap: Map, sourceIDs: string[]): LayerSpecWithSource[] =>\n loadedMap\n .getStyle()\n .layers.filter((layer) => sourceIDs.includes((layer as LayerSpecWithSource)?.source)) as LayerSpecWithSource[];\n\n/**\n * Source with layers which are coming from the downloaded TT map style.\n * * Examples are parts of the base map, traffic, pois, and hillshade.\n * @ignore\n */\nexport class StyleSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> extends AbstractSourceWithLayers<SourceSpecification, RUNTIME_SOURCE> {\n constructor(map: Map, runtimeSource: RUNTIME_SOURCE, filter?: LayerSpecFilter) {\n let layers = filterLayersBySources(map, [runtimeSource.id]);\n if (filter) {\n layers = layers.filter(filter);\n }\n super(\n map,\n new TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>(\n runtimeSource.id,\n map.getStyle().sources[runtimeSource.id] as SOURCE_SPEC,\n runtimeSource,\n ),\n layers,\n );\n }\n}\n\n/**\n * Source with layers which originally is not in the map style, but it's to be added after the map is initialized.\n * @ignore\n */\nexport class AddedSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> extends AbstractSourceWithLayers<SOURCE_SPEC, RUNTIME_SOURCE, ToBeAddedLayerSpec> {\n constructor(map: Map, sourceId: string, sourceSpec: SOURCE_SPEC, layerSpecs: ToBeAddedLayerSpecWithoutSource[]) {\n super(\n map,\n new TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>(sourceId, sourceSpec),\n // We ensure the source ID is assigned to the layers:\n layerSpecs.map((layerSpec) => ({ ...layerSpec, source: sourceId }) as ToBeAddedLayerSpec),\n );\n }\n\n private ensureLayersAddedToMap(): void {\n for (const layerSpec of this._layerSpecs) {\n if (!this.map.getLayer(layerSpec.id)) {\n this.map.addLayer(layerSpec, layerSpec.beforeID);\n }\n }\n }\n\n ensureAddedToMapWithVisibility(visible: boolean, addLayersToMap: boolean): void {\n this.source.ensureAddedToMap(this.map);\n if (addLayersToMap) {\n this.ensureLayersAddedToMap();\n this.setLayersVisible(visible);\n }\n }\n}\n\nconst emptyFeatureCollection: FeatureCollection = { type: 'FeatureCollection', features: [] };\n\n/**\n * @ignore\n */\nexport class GeoJSONSourceWithLayers<T extends FeatureCollection = FeatureCollection> extends AddedSourceWithLayers<\n GeoJSONSourceSpecification,\n GeoJSONSource\n> {\n shownFeatures: T = emptyFeatureCollection as T;\n\n constructor(map: Map, sourceId: string, layerSpecs: ToBeAddedLayerSpecWithoutSource[], addLayersToMap = true) {\n // MapLibre does not reuse the given feature ID. Either we generate it on the fly or use the one from properties via promotedId value.\n // We must generate \"id\" property based on the feature id on the fly on \"prepareForDisplay\" functions.\n super(map, sourceId, { type: 'geojson', data: emptyFeatureCollection, promoteId: 'id' }, layerSpecs);\n this.ensureAddedToMapWithVisibility(false, addLayersToMap);\n }\n\n show(featureCollection: T): void {\n this.shownFeatures = featureCollection;\n asDefined(this.source.runtimeSource).setData(featureCollection);\n this.setLayersVisible(!!featureCollection.features.length);\n }\n\n clear(): void {\n this.show(emptyFeatureCollection as T);\n }\n\n private findFeature(options: ByIdOrIndex) {\n if ('index' in options) {\n return this.shownFeatures.features[options.index];\n } else if ('id' in options) {\n return this.shownFeatures.features.find((f) => f.id === options.id);\n }\n return undefined;\n }\n\n putEventState(options: PutEventStateOptions) {\n const mode = options.mode ?? 'put';\n if (mode === 'put') {\n for (const feature of this.shownFeatures.features) {\n if (feature.properties?.eventState === options.state) {\n delete feature.properties.eventState;\n }\n }\n }\n\n const feature = this.findFeature(options);\n if (feature) {\n feature.properties = { ...feature.properties, eventState: options.state };\n }\n\n if (options.show !== false) {\n this.show(this.shownFeatures);\n }\n }\n\n cleanEventState(options: CleanEventStateOptions) {\n const feature = this.findFeature(options);\n\n if (feature?.properties?.eventState) {\n delete feature?.properties?.eventState;\n if (options?.show !== false) {\n this.show(this.shownFeatures);\n }\n }\n }\n\n cleanEventStates(options?: CleanEventStatesOptions) {\n let changed = false;\n for (const feature of this.shownFeatures.features) {\n if (!options?.states || options.states.includes(feature.properties?.eventState)) {\n delete feature.properties?.eventState;\n changed = true;\n }\n }\n\n if (options?.show !== false && changed) {\n this.show(this.shownFeatures);\n }\n }\n}\n","import type { GlobalConfig } from '@tomtom-org/maps-sdk/core';\nimport { generateTomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type {\n FilterSpecification,\n Map,\n MapGeoJSONFeature,\n RequestParameters,\n ResourceType,\n StyleImageMetadata,\n} from 'maplibre-gl';\nimport { InternalTomTomMapParams, StandardStyle, StandardStyleID, StyleInput, StyleModule } from '../init';\nimport type { TomTomMap } from '../TomTomMap';\nimport { cannotAddStyleModuleToCustomStyle } from './errorMessages';\nimport { svgToImg } from './imageUtils';\nimport { parseSvg } from './resources';\nimport { AbstractSourceWithLayers, filterLayersBySources } from './SourceWithLayers';\nimport type { LightDark, ToBeAddedLayerSpec, ToBeAddedLayerSpecWithoutSource } from './types';\n\n/**\n * Wait until the map is ready.\n * @param tomtomMap The TomTomMap instance.\n * @returns {Promise<boolean>} Returns a Promise<boolean>\n */\nexport const waitUntilMapIsReady = async (tomtomMap: TomTomMap): Promise<void> => {\n if (!tomtomMap.mapReady) {\n await tomtomMap.mapLibreMap.once('styledata');\n // Recursively waiting for map to be ready (in case of style changes quickly in succession):\n await waitUntilMapIsReady(tomtomMap);\n }\n};\n\n/**\n * Wait until the source is ready.\n * @param tomtomMap The TomTomMap instance.\n * @param sourceId we want to check for.\n * @returns {Promise<boolean>} Returns a Promise<boolean>\n */\nexport const waitUntilSourceIsLoaded = async (tomtomMap: TomTomMap, sourceId: string): Promise<void> => {\n if (!tomtomMap.mapLibreMap.getSource(sourceId) || !tomtomMap.mapLibreMap.isSourceLoaded(sourceId)) {\n await tomtomMap.mapLibreMap.once('sourcedata');\n }\n};\n\n/**\n * Deserializes the properties from MapLibre features.\n * * Maplibre has a bug where all properties from a feature are stringified.\n * See: {@link} https://github.com/maplibre/maplibre-gl-js/issues/1325\n *\n * @param features An Array with MapGeoJSONFeatures objects\n *\n * @ignore\n */\nexport const deserializeFeatures = (features: MapGeoJSONFeature[]): void => {\n for (const feature of features) {\n if (!feature || !Object.keys(feature.properties).length) {\n continue;\n }\n\n for (const key in feature.properties) {\n if (typeof feature.properties[key] === 'string') {\n try {\n feature.properties[key] = JSON.parse(feature.properties[key]);\n } catch (_e) {\n // We ignore the error if the object can't be parsed and continue.\n // console.debug('Cannot deserialize feature property', key, 'with value', feature.properties[key], e);\n }\n }\n }\n }\n};\n\n/**\n * Inject TomTom custom headers to requests to TomTom.\n *\n * @ignore\n * @param params Global SDK Map configuration\n */\nexport const injectTomTomHeaders =\n (params: Partial<GlobalConfig>) =>\n (url: string, resourceType?: ResourceType): RequestParameters => {\n if (url.includes('tomtom.com')) {\n if (resourceType === 'Image') {\n return { url };\n }\n return { url, headers: { ...generateTomTomHeaders(params) } };\n }\n return { url };\n };\n\n/**\n * Compares two MapLibre features by ID.\n * @ignore\n */\nexport const areBothDefinedAndEqual = (\n featureA: MapGeoJSONFeature | undefined,\n featureB: MapGeoJSONFeature | undefined,\n): boolean => !!featureA && !!featureB && featureA.id === featureB.id;\n\ntype LayerProps = {\n id: string;\n layout?: any;\n paint?: any;\n minzoom?: number;\n maxzoom?: number;\n filter?: FilterSpecification;\n};\n\n/**\n * Applies the layout and paint properties from newLayoutPaint\n * while unsetting (setting as undefined) the ones from previousSpec which no longer exist in newLayoutPaint.\n * * This allows for a quick change of a layer visuals without removing-re-adding the layer.\n * @ignore\n * @param newLayerProps The new layer from which to apply layout/pain props.\n * @param prevLayerProps The previous layer to ensure layout/paint props are removed.\n * @param map\n */\nexport const changeLayerProps = (newLayerProps: LayerProps, prevLayerProps: LayerProps, map: Map) => {\n const layerId = newLayerProps.id;\n if (newLayerProps.maxzoom !== prevLayerProps.maxzoom || newLayerProps.minzoom !== prevLayerProps.minzoom) {\n map.setLayerZoomRange(\n layerId,\n newLayerProps.minzoom ?? map.getMinZoom(),\n newLayerProps.maxzoom ?? map.getMaxZoom(),\n );\n }\n map.setFilter(layerId, newLayerProps.filter, { validate: false });\n for (const property of Object.keys(prevLayerProps.layout ?? [])) {\n if (!newLayerProps.layout?.[property]) {\n map.setLayoutProperty(layerId, property, undefined, { validate: false });\n }\n }\n for (const property of Object.keys(prevLayerProps.paint ?? [])) {\n if (!newLayerProps.paint?.[property]) {\n map.setPaintProperty(layerId, property, undefined, { validate: false });\n }\n }\n for (const [property, value] of Object.entries(newLayerProps.paint ?? [])) {\n map.setPaintProperty(layerId, property, value, { validate: false });\n }\n\n for (const [property, value] of Object.entries(newLayerProps.layout ?? [])) {\n map.setLayoutProperty(layerId, property, value, { validate: false });\n }\n};\n\n/**\n * Applies the layer properties from each layer of newLayoutPaints\n * while unsetting (setting as undefined) the ones from the corresponding layer from prevLayoutPaints\n * which no longer exist in the new one.\n * * The two layer inputs are expected to be parallel arrays.\n * * This allows for quick changes of layer visuals without removing-re-adding the layers.\n * @ignore\n */\nexport const changeLayersProps = (newLayerProps: LayerProps[], prevLayerProps: LayerProps[], map: Map) => {\n newLayerProps.forEach((layoutPaint, index) => changeLayerProps(layoutPaint, prevLayerProps[index], map));\n};\n\n/**\n * Handles new layer specs for the provided source. It will remove layers no longer present,\n * update existing layers and add new one if needed to the source.\n * Adding layers to the map needs to be done correctly, so after calling this method, you should call addLayersInCorrectOrder.\n * If ID of layer to be added already is present on map, MapLibre will through exception.\n * @param newLayersSpecs new layer specification for provided source.\n * @param oldLayersSpecs current layer specification for provided source.\n * @param sourceWithLayers provided source that contains layers.\n * @param map provided map libre map object.\n * @ignore\n */\nexport const updateLayersAndSource = (\n newLayersSpecs: ToBeAddedLayerSpecWithoutSource[],\n oldLayersSpecs: ToBeAddedLayerSpecWithoutSource[],\n sourceWithLayers: AbstractSourceWithLayers,\n map: Map,\n): void => {\n // map layers by id in object for easier access, reduces number of loops\n const newLayersMap: Record<string, ToBeAddedLayerSpecWithoutSource> = newLayersSpecs.reduce(\n (acc, cur) => ({ ...acc, [cur.id]: cur }),\n {},\n );\n const oldLayersMap: Record<string, ToBeAddedLayerSpecWithoutSource> = oldLayersSpecs.reduce(\n (acc, cur) => ({ ...acc, [cur.id]: cur }),\n {},\n );\n\n // we need to store layers in four arrays, layers to add ID, layers to remove ID and layers to update\n const layersToAdd: string[] = [];\n const layersToRemove: string[] = [];\n const newLayersToUpdate: ToBeAddedLayerSpecWithoutSource[] = [];\n const oldLayersToUpdate: ToBeAddedLayerSpecWithoutSource[] = [];\n Object.keys(newLayersMap).forEach((key) => {\n if (oldLayersMap[key]) {\n newLayersToUpdate.push(newLayersMap[key]);\n oldLayersToUpdate.push(oldLayersMap[key]);\n } else {\n layersToAdd.push(key);\n }\n });\n Object.keys(oldLayersMap).forEach((key) => {\n if (!newLayersMap[key]) {\n layersToRemove.push(key);\n }\n });\n\n // remove the old layers no longer present in new layer specification\n const layerSpecs: ToBeAddedLayerSpec[] = sourceWithLayers._layerSpecs;\n layersToRemove.forEach((layerId) => {\n map.removeLayer(layerId);\n for (let i = 0; i < layerSpecs.length; i++) {\n if (layerSpecs[i].id === layerId) {\n layerSpecs.splice(i, 1);\n break;\n }\n }\n });\n // add new layers\n layersToAdd.forEach((layerId) => {\n // add layer spec and map\n const toBeAddedLayerSpec: ToBeAddedLayerSpec = {\n ...newLayersMap[layerId],\n source: sourceWithLayers.source.id,\n } as ToBeAddedLayerSpec;\n layerSpecs.push(toBeAddedLayerSpec);\n });\n sourceWithLayers._updateSourceAndLayerIDs();\n // update existing layers\n changeLayersProps(newLayersToUpdate, oldLayersToUpdate, map);\n};\n\n/**\n * Adds the given layers to the map ensuring they respect their \"beforeID\" properties.\n * * We need to make sure that layers are added in the correct Z order because one layer may depend on another layer.\n * @param layersToAdd\n * @param map MapLibre map\n * @ignore\n */\nexport const addLayers = (layersToAdd: ToBeAddedLayerSpec[], map: Map): void => {\n const layerIdsAlreadyOnMap = new Set<string>();\n const addLayer = (layer: ToBeAddedLayerSpec): void => {\n // we can safely add this layer\n if (!map.getLayer(layer.id)) {\n map.addLayer({ ...layer, layout: { ...layer.layout, visibility: 'none' } }, layer.beforeID);\n }\n layerIdsAlreadyOnMap.add(layer.id);\n };\n\n const mapIdDependency: Record<string, ToBeAddedLayerSpec[]> = {};\n\n layersToAdd.forEach((layer) => {\n if (layer.beforeID) {\n if (layerIdsAlreadyOnMap.has(layer.beforeID) || map.getLayer(layer.beforeID)) {\n layerIdsAlreadyOnMap.add(layer.beforeID);\n addLayer(layer);\n } else if (mapIdDependency[layer.beforeID]) {\n // we cannot add this layer yet\n mapIdDependency[layer.beforeID].push(layer);\n } else {\n mapIdDependency[layer.beforeID] = [layer];\n }\n } else {\n addLayer(layer);\n }\n });\n\n // try to process the rest of layers\n while (Object.keys(mapIdDependency).length > 0) {\n const idsWeCanProcess = Object.keys(mapIdDependency).filter((id) => layerIdsAlreadyOnMap.has(id));\n if (!idsWeCanProcess.length) {\n console.error(\n `Some layers cannot be added. Check for non-existing layers, or circular beforeID dependencies for the following: ${JSON.stringify(Object.keys(mapIdDependency))}`,\n );\n return;\n }\n idsWeCanProcess.forEach((id) => {\n mapIdDependency[id].forEach((layer) => addLayer(layer));\n delete mapIdDependency[id];\n });\n }\n};\n\n/**\n * Adding a style-based module to the given style input, if possible.\n * * This results in a style input which will include such style-based module (e.g. include traffic layers).\n * @param style which we want to update.\n * @param styleModule module we want to add.\n * @ignore\n */\nexport const updateStyleWithModule = (style: StyleInput | undefined, styleModule: StyleModule): StyleInput => {\n switch (typeof style) {\n case 'undefined':\n return { type: 'standard', include: [styleModule] };\n case 'string':\n // this is a standard style\n return { type: 'standard', id: style, include: [styleModule] };\n default:\n if (style.type === 'standard') {\n if (style.include) {\n return { ...style, include: [...style.include, styleModule] };\n }\n return { ...style, include: [styleModule] };\n }\n throw cannotAddStyleModuleToCustomStyle(styleModule);\n }\n};\n\n/**\n * Checks if the source is missing and try to add it to the map by reloading its style.\n * @param map the TomTom map instance.\n * @param sourceId id of the source.\n * @param styleModule style module of the source.\n * @ignore\n */\nexport const ensureAddedToStyle = async (map: TomTomMap, sourceId: string, styleModule: StyleModule): Promise<void> => {\n if (!map.mapLibreMap.getSource(sourceId)) {\n const mapLibreMap = map.mapLibreMap;\n if (!mapLibreMap.isStyleLoaded()) {\n // we let the map settle before changing its style again, so the previous style/data load goes smoother:\n await mapLibreMap.once('idle');\n }\n map.setStyle(updateStyleWithModule(map.getStyle(), styleModule));\n await waitUntilSourceIsLoaded(map, sourceId);\n\n await mapLibreMap.once('styledata');\n // we're loading a bunch of style layers to the map, and we hide them all by default:\n // see TomTomMap.handleStyleData for similar logic\n for (const layer of filterLayersBySources(mapLibreMap, [sourceId])) {\n mapLibreMap.setLayoutProperty(layer.id, 'visibility', 'none', { validate: false });\n }\n // Since we just changed the style visibility, we ensure to wait until the style data is changed before returning to prevent race conditions:\n await mapLibreMap.once('styledata');\n }\n};\n\n/**\n * Sets the given image on the map (loading it if necessary), either adding or updating it.\n * @ignore\n */\nexport const addOrUpdateImage = async (\n mode: 'if-not-in-sprite' | 'add-or-update',\n imageId: string,\n imageToLoad: string | HTMLImageElement,\n map: Map,\n options?: Partial<StyleImageMetadata>,\n) => {\n // defensive check (should not happen but we cannot let it crash):\n if (!imageToLoad) {\n console.warn(`addOrUpdateImage called with empty image for ID ${imageId}`);\n return;\n }\n\n // Helper function to add or update the image\n const addOrUpdateToMap = (imgElement: HTMLImageElement | ImageData | ImageBitmap) => {\n const imageExists = map.hasImage(imageId);\n if (imageExists && mode == 'add-or-update') {\n map.updateImage(imageId, imgElement);\n } else if (!imageExists) {\n map.addImage(imageId, imgElement, options);\n }\n };\n\n const ensureImageLoaded = (imgElement: HTMLImageElement) => {\n // An image is successfully loaded if it's complete AND has valid dimensions\n // (naturalWidth > 0 ensures the image didn't fail to load)\n if (imgElement.complete) {\n if (imgElement.naturalWidth > 0) {\n addOrUpdateToMap(imgElement);\n } else {\n // Image is complete but failed to load\n console.warn(`Failed to load image for ID ${imageId}`);\n }\n } else {\n imgElement.onload = () => addOrUpdateToMap(imgElement);\n imgElement.onerror = () => console.warn(`Failed to load image for ID ${imageId}`);\n }\n };\n\n if (typeof imageToLoad === 'string') {\n if (imageToLoad.includes('<svg')) {\n // Supporting raw SVGs:\n const imgElement = svgToImg(parseSvg(imageToLoad));\n ensureImageLoaded(imgElement);\n } else {\n // Expecting image URL, so the image needs to be downloaded first:\n addOrUpdateToMap((await map.loadImage(imageToLoad)).data);\n }\n } else {\n // Expecting HTMLImageElement, wait for it to be loaded\n ensureImageLoaded(imageToLoad);\n }\n};\n\n/**\n * Returns the light/dark theme for a known standard style.\n * @param standardStyleID\n */\nconst getStandardStyleTheme = (standardStyleID: StandardStyleID): LightDark => {\n switch (standardStyleID) {\n case 'standardDark':\n case 'drivingDark':\n case 'monoDark':\n case 'satellite':\n return 'dark';\n default:\n return 'light';\n }\n};\n\n/**\n * Returns the light/dark theme for a given style input.\n * * Unknown standard styles and custom styles are considered as 'light' theme.\n * @param styleInput The style input to check. If not provided, 'light' is returned.\n * @ignore\n */\nexport const getStyleLightDarkTheme = (styleInput?: StyleInput): LightDark => {\n if (typeof styleInput === 'string') {\n return getStandardStyleTheme(styleInput);\n }\n const standardStyle = styleInput as StandardStyle;\n if (standardStyle?.id) {\n return getStandardStyleTheme(standardStyle.id);\n }\n return 'light';\n};\n\n/**\n * Adds the large POI sprite to the map style.\n * @ignore\n */\nexport const addPinCategoriesSpriteToStyle = async (mapParams: InternalTomTomMapParams, mapLibreMap: Map) => {\n mapLibreMap.setSprite(\n `${mapParams.commonBaseURL}/maps/orbis/assets/sprites/2.*/sprite?key=${mapParams.apiKey}&poi=poi_${getStyleLightDarkTheme(mapParams.style)}&apiVersion=1&apiChannel=preview`,\n { validate: false },\n );\n};\n","import type { Map } from 'maplibre-gl';\nimport type { TomTomMap } from '../TomTomMap';\nimport type { EventsProxy } from './EventsProxy';\nimport { waitUntilMapIsReady } from './mapUtils';\nimport type { MapModuleCommonConfig, SourcesWithLayers, SourceWithLayerIDs } from './types';\n\n/**\n * Whether a map module is based on a map style or on added GeoJSON data.\n */\ntype MapModuleSource = 'style' | 'geojson';\n\n/**\n * Base class for all Maps SDK map modules.\n *\n * This abstract class provides the foundation for creating map modules that can display\n * and manage various types of data on a TomTom map. It handles module lifecycle management,\n * including initialization, configuration updates, and automatic restoration after map style changes.\n *\n * @remarks\n * All map modules extend this class to ensure consistent behavior across the SDK.\n * The class manages the module's sources, layers, and configuration, automatically\n * handling map style changes by restoring the module's state when needed.\n *\n * @typeParam SOURCES_WITH_LAYERS - The type defining the sources and layers used by this module\n * @typeParam CFG - The configuration type for this module, or undefined if no configuration is needed. When defined, must extend MapModuleCommonConfig.\n *\n * @example\n * ```typescript\n * class CustomModule extends AbstractMapModule<MySourcesWithLayers, MyConfig> {\n * constructor(tomtomMap: TomTomMap, config?: MyConfig) {\n * super('geojson', tomtomMap, config);\n * }\n * // Implement abstract methods...\n * }\n * ```\n *\n * @group Shared\n */\nexport abstract class AbstractMapModule<\n SOURCES_WITH_LAYERS extends SourcesWithLayers,\n CFG extends MapModuleCommonConfig | undefined = undefined,\n> {\n private readonly sourceType: MapModuleSource;\n /**\n * @ignore\n */\n protected readonly tomtomMap: TomTomMap;\n /**\n * @ignore\n */\n protected readonly eventsProxy: EventsProxy;\n /**\n * @ignore\n */\n protected readonly mapLibreMap: Map;\n /**\n * @ignore\n */\n protected sourcesWithLayers!: SOURCES_WITH_LAYERS;\n /**\n * @ignore\n */\n protected _sourceAndLayerIDs!: Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs>;\n /**\n * @ignore\n */\n protected config?: CFG;\n /**\n * @ignore\n */\n protected _initializing = true;\n\n /**\n * Indicates that this module is currently adding its sources and layers to the map, so during this time it might not function properly.\n * @see waitUntilModuleReady\n * @private\n */\n private moduleReady = false;\n\n /**\n * Builds this module based on a given Maps SDK map.\n * @param tomtomMap The map. It may or may not be initialized at this stage,\n * but the module ensures to initialize itself once it is.\n * @param sourceType Whether the module is based on a map style or on added GeoJSON data.\n * @param config Optional configuration to initialize directly as soon as the map is ready.\n */\n protected constructor(sourceType: MapModuleSource, tomtomMap: TomTomMap, config?: CFG) {\n this.sourceType = sourceType;\n this.tomtomMap = tomtomMap;\n this.eventsProxy = tomtomMap._eventsProxy;\n this.mapLibreMap = tomtomMap.mapLibreMap;\n // TODO: we need to find a cleaner separation between initSourcesWithLayers and applyConfig for most modules to prevent double work, particularly with adding layers\n this.initSourcesWithLayers(config);\n this.applyConfig(config);\n this.tomtomMap.addStyleChangeHandler({\n onStyleAboutToChange: () => {\n this.moduleReady = false;\n },\n onStyleChanged: () => this.restoreDataAndConfig(),\n });\n this._initializing = false;\n }\n\n /**\n * Initializes the sources with layers of this module.\n * @param config The optional configuration for the module.\n * @param restore Whether we are restoring an existing module after the map style got reloaded.\n * @protected\n * @ignore\n */\n protected initSourcesWithLayers(config?: CFG, restore?: boolean): void {\n this.moduleReady = false;\n this.sourcesWithLayers = this._initSourcesWithLayers(config, restore);\n this._sourceAndLayerIDs = Object.fromEntries(\n Object.entries(this.sourcesWithLayers).map(([name, sourceWithLayers]) => [\n name,\n sourceWithLayers.sourceAndLayerIDs,\n ]),\n ) as Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs>;\n if (restore) {\n this.eventsProxy.updateIfRegistered(this.sourcesWithLayers);\n }\n // Only if the map is still ready, we consider that the module is ready.\n // Otherwise, we assume there's a quick style change in progress and expect that'll trigger the module to restore itself again.\n if (this.tomtomMap.mapReady) {\n this.moduleReady = true;\n }\n }\n\n /**\n * Initializes the sources with layers for the specific module.\n * @protected\n * @ignore\n */\n protected abstract _initSourcesWithLayers(config?: CFG, restore?: boolean): SOURCES_WITH_LAYERS;\n\n protected async waitUntilModuleReady(): Promise<void> {\n await waitUntilMapIsReady(this.tomtomMap);\n if (!this.moduleReady) {\n await new Promise<void>((resolve) => {\n const interval = setInterval(() => {\n if (this.tomtomMap.mapReady && this.moduleReady) {\n clearInterval(interval);\n resolve();\n }\n }, 200);\n });\n }\n }\n\n /**\n * Applies a configuration to this module.\n *\n * This method updates the module's behavior and appearance based on the provided configuration.\n * The configuration is stored internally and will be automatically reapplied if the map style changes.\n *\n * @param config - The configuration object to apply to the module. Pass `undefined` to reset\n * the configuration to default values.\n *\n * @remarks\n * When a configuration is applied, the module updates its visual representation and behavior\n * accordingly. The configuration persists across map style changes, ensuring consistent\n * module behavior even when the map's base style is modified.\n *\n * @example\n * ```typescript\n * // Apply a new configuration\n * myModule.applyConfig({ visible: true, opacity: 0.8 });\n *\n * // Reset to default configuration\n * myModule.applyConfig(undefined);\n * ```\n *\n * @see {@link resetConfig} for a convenience method to reset configuration\n * @see {@link getConfig} to retrieve the current configuration\n */\n applyConfig(config: CFG | undefined) {\n this.config = this._applyConfig(config);\n }\n\n /**\n * Internal implementation to apply config for the specific module.\n * @param config The config to apply. this.config contains the previous configuration (if any).\n * Once the method returns config, it will be assigned to this.config.\n * @protected\n * @ignore\n */\n protected abstract _applyConfig(config: CFG | undefined): CFG | undefined;\n\n /**\n * Resets the configuration of this module to its default values.\n *\n * This is a convenience method that clears any previously applied configuration\n * and restores the module to its initial state. This is equivalent to calling\n * `applyConfig(undefined)`.\n *\n * @remarks\n * After calling this method, the module will behave as if no configuration was ever applied.\n * Any custom settings, styling, or behavior modifications will be removed and replaced\n * with default values.\n *\n * @example\n * ```typescript\n * // Apply some configuration\n * myModule.applyConfig({ visible: true, opacity: 0.5 });\n *\n * // Later, reset to defaults\n * myModule.resetConfig();\n * ```\n *\n * @see {@link applyConfig} to apply a new configuration\n * @see {@link getConfig} to retrieve the current configuration before resetting\n */\n resetConfig(): void {\n this.applyConfig(undefined);\n }\n\n private async restoreDataAndConfig() {\n // defensively declaring the module as not ready to prevent race conditions:\n this.moduleReady = false;\n if (this.sourceType === 'geojson') {\n // Defer GeoJSON layer restoration by one animation frame to work around a MapLibre\n // glitch where symbol layers can't be added right after a styledata event.\n requestAnimationFrame(() => {\n this.restoreDataAndConfigImpl();\n });\n } else {\n this.restoreDataAndConfigImpl();\n }\n }\n\n /**\n * implementation needed to restore the module state (data and config applied to the module).\n * to be used to restore module state after map style change\n * @protected\n * @ignore\n */\n protected restoreDataAndConfigImpl(): void {\n this.initSourcesWithLayers(this.config, true);\n this._applyConfig(this.config);\n }\n\n /**\n * Retrieves a copy of the current module configuration.\n *\n * This method returns a shallow copy of the configuration object that is currently\n * applied to the module. If no configuration has been applied, it returns `undefined`.\n *\n * @returns A shallow copy of the current configuration object, or `undefined` if no\n * configuration is currently applied. The returned object is a copy to prevent\n * unintended modifications to the internal state.\n *\n * @remarks\n * The returned configuration object is a shallow copy, which means that while the\n * top-level properties are copied, any nested objects or arrays are still referenced\n * from the original configuration. This is sufficient for most use cases but should\n * be kept in mind when dealing with complex configurations.\n *\n * @example\n * ```typescript\n * // Apply a configuration\n * myModule.applyConfig({ visible: true, opacity: 0.8 });\n *\n * // Later, retrieve the current configuration\n * const currentConfig = myModule.getConfig();\n * console.log(currentConfig); // { visible: true, opacity: 0.8 }\n *\n * // When no config is applied\n * myModule.resetConfig();\n * console.log(myModule.getConfig()); // undefined\n * ```\n *\n * @see {@link applyConfig} to modify the configuration\n * @see {@link resetConfig} to clear the configuration\n */\n getConfig() {\n return this.config && { ...this.config };\n }\n\n /**\n * Gets the source and layer identifiers for all sources managed by this module.\n *\n * This property provides access to the MapLibre source and layer IDs that were created\n * and are managed by this module. These IDs can be used to interact directly with\n * MapLibre's API or to identify which layers belong to this module.\n *\n * @returns A record mapping each source name to its corresponding source ID and layer IDs.\n * Each entry contains the MapLibre source identifier and an array of layer identifiers\n * associated with that source.\n *\n * @remarks\n * The returned IDs are useful when you need to:\n * - Directly manipulate layers using MapLibre's native API\n * - Identify which layers on the map belong to this module\n * - Set layer ordering or positioning relative to other layers\n * - Access source or layer properties through MapLibre methods\n *\n * @example\n * ```typescript\n * const ids = myModule.sourceAndLayerIDs;\n * console.log(ids);\n * // {\n * // mySource: {\n * // sourceID: 'my-source-id',\n * // layerIDs: ['layer-1', 'layer-2']\n * // }\n * // }\n *\n * // Use with MapLibre API\n * const map = myModule.mapLibreMap;\n * ids.mySource.layerIDs.forEach(layerId => {\n * map.setLayoutProperty(layerId, 'visibility', 'visible');\n * });\n * ```\n */\n get sourceAndLayerIDs(): Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs> {\n return this._sourceAndLayerIDs;\n }\n}\n","import type { MapGeoJSONFeature } from 'maplibre-gl';\nimport type { EventsProxy } from './EventsProxy';\nimport type { EventHandlerConfig, EventType, SourceWithLayers, UserEventHandler } from './types';\n\n/**\n * Event handling interface for map features.\n *\n * Provides a simple API for attaching and removing event handlers for user interactions\n * with map features such as clicks, hovers, and context menus. Each map module (POIs, Routing,\n * Places, etc.) exposes an `events` property that returns an EventsModule instance.\n *\n * @typeParam T - The feature type returned in event handlers (extends MapGeoJSONFeature)\n *\n * @remarks\n * **Supported Event Types:**\n * - `click`: User clicks/taps on a feature\n * - `contextmenu`: User right-clicks on a feature (or long-press on mobile)\n * - `hover`: Mouse enters a feature\n * - `long-hover`: Mouse hovers over feature for configured duration (300-800ms)\n *\n * **Event Handler Signature:**\n * ```typescript\n * (feature: T, lngLat: LngLat, features: T[]) => void\n * ```\n *\n * **Parameters:**\n * - `feature`: The primary feature under the cursor\n * - `lngLat`: Geographic coordinates of the event\n * - `features`: All features at this location (when multiple overlap)\n *\n * **Key Features:**\n * - Automatic cursor management (pointer on hover)\n * - Smart event handling for overlapping features\n * - Configurable hover delays\n * - Memory-safe cleanup when removing handlers\n *\n * @example\n * ```typescript\n * // POI click handler\n * const pois = map.pois();\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('Clicked POI:', feature.properties.name);\n * console.log('Location:', lngLat);\n * showInfoWindow(feature.properties);\n * });\n *\n * // Route waypoint hover\n * const routing = map.routing();\n * routing.events.waypoints.on('hover', (waypoint) => {\n * showTooltip(`Waypoint ${waypoint.properties.index}`);\n * });\n *\n * // Long-hover for detailed info\n * pois.events.on('long-hover', (feature) => {\n * loadAndShowDetailedInfo(feature.properties.id);\n * });\n *\n * // Context menu (right-click)\n * routing.events.mainLines.on('contextmenu', (route, lngLat) => {\n * showContextMenu(lngLat, [\n * { label: 'Add waypoint here', action: () => addWaypoint(lngLat) },\n * { label: 'View route details', action: () => showDetails(route) }\n * ]);\n * });\n *\n * // Remove all click handlers\n * pois.events.off('click');\n * ```\n *\n * @example\n * ```typescript\n * // Complete interaction example\n * const places = map.places();\n *\n * // Show name on hover\n * places.events.on('hover', (place) => {\n * tooltip.show(place.properties.name);\n * });\n *\n * // Show details on click\n * places.events.on('click', (place, lngLat) => {\n * sidebar.show({\n * title: place.properties.name,\n * address: place.properties.address.freeformAddress,\n * coordinates: lngLat\n * });\n * });\n *\n * // Cleanup on component unmount\n * onUnmount(() => {\n * places.events.off('hover');\n * places.events.off('click');\n * });\n * ```\n *\n * @group User Interaction Events\n */\nexport class EventsModule<T = MapGeoJSONFeature> {\n constructor(\n private readonly eventProxy: EventsProxy,\n private readonly sourceWithLayers: SourceWithLayers,\n private readonly config: EventHandlerConfig | undefined,\n ) {}\n\n /**\n * Register an event handler for user interactions with map features.\n *\n * Attaches a callback function that will be invoked when users interact with\n * features in this module (e.g., clicking on a POI, hovering over a route).\n *\n * @param type The type of event to listen for (click, contextmenu, hover, long-hover)\n * @param handler Callback function invoked when the event occurs\n *\n * @remarks\n * **Handler Parameters:**\n * - `feature`: The primary feature that triggered the event\n * - `lngLat`: Geographic coordinates [longitude, latitude] of the event\n * - `features`: Array of all features at the event location (for overlapping features)\n *\n * **Behavior:**\n * - Only one handler per event type (calling `on()` again replaces the previous handler)\n * - Handlers are preserved across map style changes\n * - Cursor automatically changes to pointer on hover\n * - Events respect module visibility (hidden features don't trigger events)\n *\n * **Performance:**\n * - Hover events use spatial indexing for fast lookup\n * - Long-hover has configurable delay to prevent accidental triggers\n * - Event handlers should be lightweight to maintain smooth interaction\n *\n * @example\n * ```typescript\n * // Basic click handler\n * module.events.on('click', (feature, lngLat, features) => {\n * console.log('Clicked feature:', feature.properties);\n * console.log('Location:', lngLat);\n * console.log('All features here:', features.length);\n * });\n *\n * // Hover with tooltip\n * module.events.on('hover', (feature) => {\n * const name = feature.properties.name || 'Unnamed';\n * tooltip.show(name);\n * });\n *\n * // Long-hover for detailed preview\n * module.events.on('long-hover', (feature) => {\n * // Only triggered after hovering for 300-800ms\n * loadPreview(feature.properties.id);\n * });\n *\n * // Right-click context menu\n * module.events.on('contextmenu', (feature, lngLat) => {\n * event.preventDefault(); // Prevent browser context menu\n * showCustomMenu(lngLat, feature);\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Route-specific handlers\n * const routing = map.routing();\n *\n * // Handle route line clicks\n * routing.events.mainLines.on('click', (route) => {\n * highlightRoute(route.properties.routeID);\n * showRouteSummary(route.properties.summary);\n * });\n *\n * // Handle waypoint interactions\n * routing.events.waypoints.on('hover', (waypoint) => {\n * const index = waypoint.properties.index;\n * showTooltip(`Stop ${index + 1}`);\n * });\n * ```\n */\n on(type: EventType, handler: UserEventHandler<T>) {\n this.eventProxy.addEventHandler(this.sourceWithLayers, handler, type, this.config);\n }\n\n /**\n * Remove all event handlers for a specific event type.\n *\n * Unregisters the callback function for the specified event type, stopping\n * further event notifications. This is important for cleanup to prevent\n * memory leaks and unwanted behavior.\n *\n * @param type The type of event to stop listening for\n *\n * @remarks\n * **Cleanup Behavior:**\n * - Removes only the specified event type (other types remain active)\n * - Resets cursor behavior for this module\n * - Safe to call multiple times (no error if no handler exists)\n * - Does not affect other modules' event handlers\n *\n * **When to Use:**\n * - Component unmounting/cleanup\n * - Switching between interaction modes\n * - Temporarily disabling interactions\n * - Replacing an existing handler (call `off()` then `on()`)\n *\n * **Best Practices:**\n * - Always clean up event handlers when component unmounts\n * - Remove handlers before removing features from the map\n * - Use framework lifecycle hooks for automatic cleanup\n *\n * @example\n * ```typescript\n * // Remove click handlers\n * module.events.off('click');\n *\n * // Remove all handlers\n * module.events.off('click');\n * module.events.off('hover');\n * module.events.off('long-hover');\n * module.events.off('contextmenu');\n * ```\n *\n * @example\n * ```typescript\n * // React component cleanup\n * useEffect(() => {\n * const pois = map.pois();\n *\n * pois.events.on('click', handlePoiClick);\n * pois.events.on('hover', handlePoiHover);\n *\n * return () => {\n * // Cleanup on unmount\n * pois.events.off('click');\n * pois.events.off('hover');\n * };\n * }, [map]);\n * ```\n *\n * @example\n * ```typescript\n * // Replace handler\n * module.events.off('click'); // Remove old handler\n * module.events.on('click', newHandler); // Add new handler\n *\n * // Disable interactions temporarily\n * const savedHandler = currentHandler;\n * module.events.off('click'); // Disable\n * // ... later ...\n * module.events.on('click', savedHandler); // Re-enable\n * ```\n */\n off(type: EventType) {\n this.eventProxy.remove(this.sourceWithLayers, type);\n }\n}\n","import { isEmpty, remove } from 'lodash-es';\nimport type { LayerSpecification, MapGeoJSONFeature } from 'maplibre-gl';\nimport type { EventHandlerConfig, EventType, SourcesWithLayers, SourceWithLayers, UserEventHandler } from './types';\n\n// TODO: add support for multiple handlers per source, layers, and event type?\n// ... (this means multiple handlers for the same module, or repeated \"on\" calls for the same module)\ntype SourceEventTypeHandler = {\n sourceWithLayers: SourceWithLayers;\n layerIDs: string[];\n fn: UserEventHandler<any>;\n config?: EventHandlerConfig;\n};\n\ntype SourceEventHandlers = Partial<Record<EventType, SourceEventTypeHandler[]>>;\n\n// Source ID to event handlers for that source:\n// Example:\n// const handlers = {\n// vectorTiles: {\n// click: [\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [\"Buildings\", \"Roads - Major\"],\n// fn: UserEventsHandler...\n// },\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [\"Water\", \"Landuse - Parks],\n// fn: UserEventsHandler...\n// }\n// ],\n// hover: [\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [...],\n// fn: UserEventsHandler...\n// }\n// ]\n// },\ntype EventHandlers = Record<string, SourceEventHandlers>;\n\nconst matchesLayers = (layers: LayerSpecification[], layerIDs: string[]): boolean =>\n layerIDs.every((layerId, index) => layerId === layers[index].id);\n\n/**\n * @ignore\n */\nexport abstract class AbstractEventProxy {\n protected interactiveLayerIDs: string[] = [];\n protected handlers: EventHandlers = {};\n\n /**\n * Adds the given layers as interactive, so we'll listen to them for hover and click.\n * @param sourceWithLayers The sources and layers to listen to.\n */\n private ensureInteractiveLayerIDsAdded(sourceWithLayers: SourceWithLayers) {\n sourceWithLayers._layerSpecs.forEach((layerSpec) => {\n if (!this.interactiveLayerIDs.includes(layerSpec.id)) {\n this.interactiveLayerIDs.push(layerSpec.id);\n }\n });\n }\n\n /**\n * Register an event listener to the list.\n * @param sourceWithLayers The sources and layers to added.\n * @param handlerFn Function that will handle the event.\n * @param type Type of event to listen to.\n * @param config Optional configuration for the event handler.\n */\n addEventHandler<T = MapGeoJSONFeature>(\n sourceWithLayers: SourceWithLayers,\n handlerFn: UserEventHandler<T>,\n type: EventType,\n config: EventHandlerConfig | undefined,\n ) {\n this.ensureInteractiveLayerIDsAdded(sourceWithLayers);\n const sourceId = sourceWithLayers.source.id;\n\n if (!this.handlers[sourceId]) {\n this.handlers[sourceId] = { [type]: [] };\n }\n this.handlers[sourceId][type] ??= [];\n\n this.handlers[sourceId][type]?.push({\n sourceWithLayers,\n layerIDs: sourceWithLayers._layerSpecs.map((layer) => layer.id),\n fn: handlerFn,\n config,\n });\n }\n\n /**\n * Removes the given sources and layers from the interactive list. When not present, nothing happens.\n * @param type The event type to be removed.\n * @param sourceWithLayers The sources and layers to remove, matched by source and layer IDs.\n */\n remove(sourceWithLayers: SourceWithLayers, type: EventType) {\n const sourceEventTypeHandlers = this.handlers[sourceWithLayers.source.id]?.[type];\n if (sourceEventTypeHandlers) {\n // @ts-ignore\n remove(sourceEventTypeHandlers, (handler) => matchesLayers(sourceWithLayers._layerSpecs, handler.layerIDs));\n // cleaning up empty arrays and objects if necessary:\n if (!sourceEventTypeHandlers.length) {\n delete this.handlers[sourceWithLayers.source.id]?.[type];\n if (isEmpty(this.handlers[sourceWithLayers.source.id])) {\n delete this.handlers[sourceWithLayers.source.id];\n }\n }\n }\n for (const layer of sourceWithLayers._layerSpecs) {\n // @ts-ignore\n remove(this.interactiveLayerIDs, (id) => layer.id === id);\n }\n }\n\n /**\n * Removes all interactive sources and layers.\n */\n removeAll() {\n this.interactiveLayerIDs = [];\n this.handlers = {};\n }\n\n /**\n * Returns whether this source is registered for events (has handlers attached).\n * @param sourceId The source id (should be linked to a SourceWithLayers instance).\n */\n hasSourceID(sourceId: string): boolean {\n return !!this.handlers[sourceId];\n }\n\n /**\n * Updates the given sourcesWithLayers, if they have any handlers.\n * * (This is typically called to refresh any registered, stale sourceWithLayers references after a map style has changed).\n * @param sourcesWithLayers The new sources with layers to replace existing ones.\n */\n updateIfRegistered(sourcesWithLayers: SourcesWithLayers): void {\n for (const sourceWithLayers of Object.values(sourcesWithLayers)) {\n const sourceHandlers = this.handlers[sourceWithLayers.source.id];\n if (sourceHandlers) {\n for (const sourceEventTypeHandlers of Object.values(sourceHandlers)) {\n for (const handler of sourceEventTypeHandlers) {\n if (sourceWithLayers.equalSourceAndLayerIDs(handler.sourceWithLayers)) {\n // We keep the reference fresh:\n handler.sourceWithLayers = sourceWithLayers;\n }\n }\n }\n }\n }\n }\n\n protected findHandlers = (\n types: EventType[],\n sourceId: string | undefined,\n layerId: string | undefined,\n ): SourceEventTypeHandler[] =>\n (sourceId &&\n layerId &&\n types.flatMap((type) => {\n const sourceEventTypeHandlers = this.handlers[sourceId]?.[type];\n return sourceEventTypeHandlers?.length === 1\n ? // if there's only handler for that source and type, we just return it, no need to match layers:\n sourceEventTypeHandlers\n : this.handlers[sourceId]?.[type]?.filter((handler) => handler.layerIDs.includes(layerId)) || [];\n })) ||\n [];\n}\n","import type { Feature } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport type { MapGeoJSONFeature, Point2D } from 'maplibre-gl';\nimport { areBothDefinedAndEqual } from './mapUtils';\nimport { GeoJSONSourceWithLayers } from './SourceWithLayers';\nimport type { EventType, SourceWithLayers } from './types';\n\ntype IndexedFeature<F extends Feature = Feature> = { feature: F; index: number };\n\nconst findFeatureById = (features: Feature[], id: string | number | undefined): IndexedFeature | undefined => {\n for (let i = 0; i < features.length; i++) {\n const feature = features[i];\n if (feature.id === id) {\n return { feature, index: i };\n }\n }\n return undefined;\n};\n\nconst isHighPriority = (eventType: EventType): boolean => eventType === 'click' || eventType === 'contextmenu';\n\n/**\n * Puts or removes the given event state to the right feature in featuresToUpdate based on the\n * @param eventState The new event state to apply or to use as reference.\n * @param featureId The ID of the feature to update within featuresToUpdate.\n * @param featuresToUpdate The features list which will be updated.\n * @param mode If updateInProps, replaces the existing eventState. If removeFromProps, removes the existing eventState.\n * @return The index of the updated feature in the mutated featuresToUpdate array.\n * @ignore\n */\nexport const putEventState = (\n eventState: EventType,\n featureId: string | number | undefined,\n featuresToUpdate: Feature[], // \"featuresToUpdate\" will be mutated\n mode: 'updateInProps' | 'removeFromProps' = 'updateInProps',\n): number | undefined => {\n const { feature, index } = findFeatureById(featuresToUpdate, featureId) || {};\n if (feature && (!isHighPriority(feature.properties?.eventState) || isHighPriority(eventState))) {\n const updatedFeature = {\n ...feature,\n properties:\n mode === 'updateInProps'\n ? { ...feature?.properties, eventState }\n : omit(feature?.properties, 'eventState'),\n };\n featuresToUpdate.splice(index as number, 1, updatedFeature);\n return index;\n }\n return undefined;\n};\n\nconst removeEventStateAndShow = (\n newEventType: EventType,\n rawFeature: MapGeoJSONFeature,\n sourceWithLayers: GeoJSONSourceWithLayers,\n): void => {\n const prevFeaturesToUpdate = [...sourceWithLayers.shownFeatures.features];\n const updatedIndex = putEventState(newEventType, rawFeature.id, prevFeaturesToUpdate, 'removeFromProps');\n if (!isNil(updatedIndex)) {\n sourceWithLayers.show({ ...sourceWithLayers.shownFeatures, features: prevFeaturesToUpdate });\n }\n};\n\n/**\n * Updates the event state props of the given features.\n * @param eventState The type of event that is being done or undone.\n * @param eventFeature The current MapLibre feature affected by the event, if any.\n * If undefined, it means the event is being undone from prevRawFeature without going to any other one.\n * @param prevEventFeature The previous MapLibre feature affected by the event type, if any (e.g. previous one clicked or hovered).\n * @param sourceWithLayers The source with layers of eventFeature, if any.\n * @param prevSourceWithLayers The source with layers of prevEventFeature, if any.\n * @ignore\n */\nexport const updateEventState = (\n eventState: EventType,\n eventFeature: MapGeoJSONFeature | undefined,\n prevEventFeature: MapGeoJSONFeature | undefined,\n sourceWithLayers: SourceWithLayers | undefined,\n prevSourceWithLayers: SourceWithLayers | undefined,\n): Partial<IndexedFeature> => {\n if (eventFeature && sourceWithLayers instanceof GeoJSONSourceWithLayers) {\n const featuresToUpdate = [...sourceWithLayers.shownFeatures.features];\n const updatedIndex = putEventState(eventState, eventFeature.id, featuresToUpdate) as number;\n\n if (prevEventFeature && !areBothDefinedAndEqual(prevEventFeature, eventFeature)) {\n // (we have both current and prev features for this event type)\n if (prevSourceWithLayers === sourceWithLayers) {\n // we undo the event state from prev feature next to the new one (they will be shown below):\n putEventState(eventState, prevEventFeature.id, featuresToUpdate, 'removeFromProps');\n } else if (prevSourceWithLayers instanceof GeoJSONSourceWithLayers) {\n // the prev feature is in other source/layers, so we update and show them:\n removeEventStateAndShow(eventState, prevEventFeature, prevSourceWithLayers);\n }\n }\n\n sourceWithLayers.show({ ...sourceWithLayers.shownFeatures, features: featuresToUpdate });\n\n return { feature: featuresToUpdate[updatedIndex], index: updatedIndex };\n }\n if (prevEventFeature && prevSourceWithLayers instanceof GeoJSONSourceWithLayers) {\n removeEventStateAndShow(eventState, prevEventFeature, prevSourceWithLayers);\n }\n return { feature: eventFeature };\n};\n\n/**\n * Detects whether there's been a hovering change with the given event and state params.\n * @param hoveringPoint The current hovering pixel coordinates.\n * @param hoveringFeature The current feature being hovered, if any.\n * @param prevHoveredPoint The pixel coordinates from the previous hovering event, if any.\n * @param prevHoveredFeature The previous feature being hovered, if any (could be the same as hoveringFeature).\n * @ignore\n */\nexport const detectHoverState = (\n hoveringPoint: Point2D,\n hoveringFeature: MapGeoJSONFeature | undefined,\n prevHoveredPoint: Point2D | undefined,\n prevHoveredFeature: MapGeoJSONFeature | undefined,\n): {\n /**\n * Whether a hover state change happened, such as no-hover -> hover or vice-versa.\n */\n hoverChanged?: boolean;\n /**\n * Whether the mouse is moving along the hovered feature (not stopped on it).\n */\n mouseInMotionOverHoveredFeature?: boolean;\n} => {\n if (hoveringFeature) {\n if (!prevHoveredFeature) {\n return { hoverChanged: true };\n }\n if (\n (hoveringFeature.id && hoveringFeature.id !== prevHoveredFeature.id) ||\n (hoveringFeature.properties.id && hoveringFeature.properties.id !== prevHoveredFeature.properties.id) ||\n hoveringFeature.source !== prevHoveredFeature.source ||\n // comparing by layer ID is needed when two id-less features from the same source but different layers are compared\n // this can happen when e.g. hovering over different layer groups for the base map\n hoveringFeature.layer.id !== prevHoveredFeature.layer.id\n ) {\n // hovering from one feature to another one (from the same or different layer/source):\n return { hoverChanged: true };\n }\n // (else we're hovering along the same feature)\n if (\n prevHoveredPoint &&\n (hoveringPoint.x - prevHoveredPoint.x != 0 || hoveringPoint.y - prevHoveredPoint.y != 0)\n ) {\n return { mouseInMotionOverHoveredFeature: true };\n }\n } else if (prevHoveredFeature) {\n return { hoverChanged: true };\n }\n return {};\n};\n","import type { LngLat, Map, MapGeoJSONFeature, MapMouseEvent, Point2D, PointLike } from 'maplibre-gl';\nimport type { MapEventsConfig } from '../init';\nimport { AbstractEventProxy } from './AbstractEventProxy';\nimport { detectHoverState, updateEventState } from './eventUtils';\nimport { deserializeFeatures } from './mapUtils';\nimport type { ClickEventType, EventHandlerConfig, SourceWithLayers } from './types';\n\n// Default values for events\nconst eventsProxyDefaultConfig: Required<MapEventsConfig> = {\n precisionMode: 'box',\n paddingBoxPx: 5,\n cursorOnHover: 'pointer',\n cursorOnMouseDown: 'grabbing',\n cursorOnMap: 'default',\n /**\n * Delayed hover control:\n * * The first hover we do after the map moves is longer\n */\n longHoverDelayAfterMapMoveMS: 800,\n /* Followup hovers with the same non-moving map are quicker (\"hovering around mode\") */\n longHoverDelayOnStillMapMS: 300,\n};\n\n/**\n * This is the place where we handle the user events on the map (mousemove/hover and click mostly).\n * To have full control on hovers and clicks when multiple overlapping layers are present, that logic must be centralized here.\n * @ignore\n */\nexport class EventsProxy extends AbstractEventProxy {\n private readonly map: Map;\n private readonly mapCanvas: HTMLCanvasElement;\n private enabled = true;\n private hoveringLngLat?: LngLat;\n private hoveringPoint?: Point2D;\n private hoveringFeature?: MapGeoJSONFeature;\n private hoveringFeatures?: MapGeoJSONFeature[];\n private hoveringSourceWithLayers?: SourceWithLayers;\n private longHoverTimeoutHandlerID?: number;\n // Control flag to indicate that the coming hover is the first one since the map is \"quiet\" again:\n private firstDelayedHoverSinceMapMove = true;\n private lastClickedFeature?: MapGeoJSONFeature;\n private lastClickedSourceWithLayers?: SourceWithLayers;\n private lastCursorStyle: string;\n // Configuration\n private readonly config: Required<MapEventsConfig>;\n\n constructor(map: Map, config: MapEventsConfig = {}) {\n super();\n this.map = map;\n this.mapCanvas = map.getCanvas();\n this.config = { ...eventsProxyDefaultConfig, ...config };\n this.mapCanvas.style.cursor = this.config.cursorOnMap;\n this.lastCursorStyle = this.config.cursorOnMap;\n this.listenToEvents();\n }\n\n private listenToEvents() {\n this.map.on('mousemove', (ev) => this.onMouseMove(ev));\n this.map.on('movestart', () => this.onMouseStart());\n this.map.on('mouseout', () => this.onMouseOut());\n this.map.on('mouseover', (ev) => this.onMouseMove(ev));\n this.map.on('mousedown', () => this.onMouseDown());\n this.map.on('mouseup', () => this.onMouseUp());\n this.map.on('click', (ev) => this.onMapClick('click', ev));\n this.map.on('contextmenu', (ev) => this.onMapClick('contextmenu', ev));\n }\n\n // Enable/Disable Events\n public enable(enabled: boolean) {\n this.enabled = enabled;\n if (!enabled) {\n this.clearLongHoverTimeout();\n }\n }\n\n private toPaddedBounds(point: Point2D): [[number, number], [number, number]] {\n const padding = this.config.paddingBoxPx;\n return [\n // sw:\n [point.x - padding, point.y + padding],\n // ne:\n [point.x + padding, point.y - padding],\n ];\n }\n\n private isEnabled() {\n return this.enabled && !this.map.isMoving();\n }\n\n private getRenderedFeatures(point: Point2D): MapGeoJSONFeature[] {\n if (!this.interactiveLayerIDs.length) {\n return [];\n }\n const options = { layers: this.interactiveLayerIDs, validate: false };\n const precision = this.config.precisionMode;\n // first optional attempt right in the given coordinates:\n const renderedFeatures =\n precision === 'point-then-box' || precision === 'point'\n ? this.map.queryRenderedFeatures(point as PointLike, options)\n : [];\n return renderedFeatures.length || precision === 'point'\n ? renderedFeatures\n : // second attempt using 'box' = padded bounds (trying to hit something slightly further from the pointer location)\n this.map.queryRenderedFeatures(this.toPaddedBounds(point), options);\n }\n\n private clearLongHoverTimeout() {\n window.clearTimeout(this.longHoverTimeoutHandlerID);\n }\n\n private restartLongHoverTimeout() {\n this.clearLongHoverTimeout();\n this.longHoverTimeoutHandlerID = window.setTimeout(\n () => this.handleLongHoverTimeout(),\n this.firstDelayedHoverSinceMapMove\n ? this.config.longHoverDelayAfterMapMoveMS\n : this.config.longHoverDelayOnStillMapMS,\n );\n }\n\n private handleLongHoverTimeout() {\n // We avoid firing long hovers when the feature is in clicked state:\n this.firstDelayedHoverSinceMapMove = false;\n\n if (this.hoveringSourceWithLayers) {\n const eventState = updateEventState(\n 'long-hover',\n this.hoveringFeature,\n undefined,\n this.hoveringSourceWithLayers,\n undefined,\n );\n\n this.findHandlers(['long-hover'], this.hoveringFeature?.source, this.hoveringFeature?.layer.id).forEach(\n (handler) =>\n handler.fn(\n eventState.feature,\n this.hoveringLngLat as LngLat,\n this.hoveringFeatures as MapGeoJSONFeature[],\n this.hoveringSourceWithLayers as SourceWithLayers,\n ),\n );\n }\n }\n\n private onMouseStart() {\n this.firstDelayedHoverSinceMapMove = true;\n this.clearLongHoverTimeout();\n }\n\n private onMouseOut() {\n // Preventing accidental de-hover event if we actually leave the map canvas.\n // Since this could potentially be about jumping into a map popup, so we leave that up to the caller.\n this.clearLongHoverTimeout();\n }\n\n private onMouseDown() {\n this.lastCursorStyle = this.mapCanvas.style.cursor;\n this.mapCanvas.style.cursor = this.config.cursorOnMouseDown;\n }\n\n private onMouseUp() {\n this.mapCanvas.style.cursor = this.lastCursorStyle;\n }\n\n private onMouseMove(ev: MapMouseEvent) {\n if (!this.isEnabled()) {\n // We ensure no unwanted hover handling while disabled or the map moves\n return;\n }\n\n this.hoveringFeatures = this.getRenderedFeatures(ev.point);\n deserializeFeatures(this.hoveringFeatures);\n const [hoveredTopFeature] = this.hoveringFeatures;\n\n // Check if the layer has any handlers registered.\n // Since hover is the \"lowest\" event type, having a handler for any event type justifies supporting hover state.\n // However, we'll only fire the hover events if there are handlers for hover specifically.\n if (hoveredTopFeature && !this.hasSourceID(hoveredTopFeature.source)) {\n return;\n }\n\n // hoverChangeDetected: whether a change happened, such as no-hover -> hover or vice versa:\n // mouseInMotionOverHoveredFeature: whether the mouse is moving along the hovered feature (not stopped on it):\n const { hoverChanged, mouseInMotionOverHoveredFeature } = detectHoverState(\n ev.point,\n hoveredTopFeature,\n this.hoveringPoint,\n this.hoveringFeature,\n );\n\n if (hoverChanged || mouseInMotionOverHoveredFeature) {\n this.hoveringLngLat = ev.lngLat;\n this.hoveringPoint = ev.point;\n const prevHoveredFeature = this.hoveringFeature;\n this.hoveringFeature = hoveredTopFeature;\n const prevHoveredSourceWithLayers = this.hoveringSourceWithLayers;\n\n // Hovering basic event states are still processed if any other handlers are registered for that source/layers.\n // We do so because basic hovering states indicate a feature is interactive.\n // (e.g. if there's a click handler, we'll still apply basic hover states, even if we don't fire hover events)\n const firstHandler = this.findHandlers(\n ['hover', 'hover-move', 'long-hover', 'click', 'contextmenu'],\n hoveredTopFeature?.source,\n hoveredTopFeature?.layer.id,\n )?.[0];\n\n // NOTE: handlers overlapping in source and layer IDs won't be supported properly:\n this.hoveringSourceWithLayers = firstHandler?.sourceWithLayers;\n\n if (hoverChanged) {\n this.updateHoverCursor(firstHandler?.config);\n\n const eventState = updateEventState(\n 'hover',\n this.hoveringFeature,\n prevHoveredFeature,\n this.hoveringSourceWithLayers,\n prevHoveredSourceWithLayers,\n );\n\n const hoverHandlers = this.findHandlers(\n ['hover'],\n hoveredTopFeature?.source,\n hoveredTopFeature?.layer.id,\n );\n\n for (const handler of hoverHandlers) {\n handler.fn(eventState.feature, ev.lngLat, this.hoveringFeatures, this.hoveringSourceWithLayers);\n }\n }\n\n if (mouseInMotionOverHoveredFeature) {\n const hoverMoveHandlers = this.findHandlers(\n ['hover-move'],\n this.hoveringFeature?.source,\n this.hoveringFeature?.layer.id,\n );\n\n for (const handler of hoverMoveHandlers) {\n handler.fn(this.hoveringFeature, ev.lngLat, this.hoveringFeatures, this.hoveringSourceWithLayers);\n }\n }\n\n this.restartLongHoverTimeout();\n }\n }\n\n private updateHoverCursor(config: EventHandlerConfig | undefined) {\n if (this.hoveringFeature) {\n this.mapCanvas.style.cursor = config?.cursorOnHover ?? this.config.cursorOnHover;\n } else {\n this.mapCanvas.style.cursor = this.config.cursorOnMap;\n }\n }\n\n private onMapClick(clickType: ClickEventType, ev: MapMouseEvent) {\n if (!this.isEnabled()) {\n // We avoid any accidental click handling while disabled or the map moves\n return;\n }\n\n const clickedFeatures = this.getRenderedFeatures(ev.point);\n // Deserialize Features from maplibre queryRenderedFeatures response\n deserializeFeatures(clickedFeatures);\n\n const prevClickedFeature = this.lastClickedFeature;\n this.lastClickedFeature = clickedFeatures[0];\n const prevClickedSourceWithLayers = this.lastClickedSourceWithLayers;\n const clickHandlers = this.findHandlers(\n [clickType],\n this.lastClickedFeature?.source,\n this.lastClickedFeature?.layer.id,\n );\n\n // NOTE: handlers overlapping in source and layer IDs won't be supported properly:\n this.lastClickedSourceWithLayers = clickHandlers?.[0]?.sourceWithLayers;\n\n const eventState = updateEventState(\n clickType,\n this.lastClickedFeature,\n prevClickedFeature,\n this.lastClickedSourceWithLayers,\n prevClickedSourceWithLayers,\n );\n\n for (const handler of clickHandlers) {\n handler.fn(eventState.feature, ev.lngLat, clickedFeatures, this.lastClickedSourceWithLayers);\n }\n }\n}\n","/**\n * Key layer IDs from the vector map style for positioning custom layers.\n *\n * @remarks\n * This constant provides reference layer IDs from TomTom's vector map styles that are used\n * as anchors when positioning custom layers in the map's layer stack. By referencing these\n * layer IDs, SDK modules and custom implementations can control where their layers appear\n * in the rendering order (visual stacking).\n *\n * **Understanding Layer Order:**\n *\n * \"Lowest\" refers to position in the layer stack, not physical elevation. Layers are rendered\n * from bottom to top - layers lower in the stack are drawn first and appear underneath layers\n * higher in the stack. When you add a layer \"before\" a reference layer, it's inserted lower\n * in the stack and will be drawn underneath that reference layer.\n *\n * **How SDK Modules Use These IDs:**\n *\n * **RoutingModule** - Positions route visualizations below labels for readability:\n * ```typescript\n * // Routes appear below labels but above the base map\n * const routingModule = await RoutingModule.get(map);\n * // Uses mapStyleLayerIDs.lowestLabel by default\n * ```\n *\n * **GeometriesModule** - Configurable layer positioning for polygon areas:\n * ```typescript\n * // Default: below labels\n * const geometriesModule = await GeometriesModule.get(map);\n *\n * // Custom: below buildings to show at ground level\n * const geometriesModule = await GeometriesModule.get(map, {\n * beforeLayerConfig: 'lowestBuilding'\n * });\n *\n * // On top of everything\n * const geometriesModule = await GeometriesModule.get(map, {\n * beforeLayerConfig: 'top'\n * });\n * ```\n *\n * **Custom Layer Implementation:**\n * ```typescript\n * // Add a highlight layer below labels\n * map.addLayer({\n * id: 'search-results',\n * type: 'fill',\n * source: 'results-source',\n * paint: { 'fill-color': '#ffcc00', 'fill-opacity': 0.4 }\n * }, mapStyleLayerIDs.lowestLabel);\n * ```\n *\n * **Style Persistence:**\n *\n * These layer IDs remain consistent across TomTom map style changes (e.g., switching from\n * 'standardLight' to 'standardDark'). The SDK automatically repositions module layers using\n * the corresponding reference layers in the new style, maintaining the intended visual hierarchy.\n *\n * **Important Notes:**\n * - `lowestBuilding` is not available in the Satellite style\n * - If a reference layer doesn't exist, custom layers will be added on top of the style\n * - Both RoutingModule and GeometriesModule default to `lowestLabel` for optimal visibility\n *\n * @see {@link GeometryBeforeLayerConfig} for GeometriesModule layer positioning options\n * @see {@link RouteLayersConfig} for RoutingModule layer configuration\n *\n * @group Map Style\n */\nexport const mapStyleLayerIDs = {\n /**\n * Country name label layer.\n *\n * @remarks\n * Lower in the stack than most labels. Use to position elements below country\n * labels but above other map features.\n */\n country: 'Places - Country name',\n /**\n * The lowest layer for place labels.\n *\n * @remarks\n * Lower in the stack than city labels. Use to position content below all place\n * name labels while keeping it visible above geographic features.\n */\n lowestPlaceLabel: 'Places - Village / Hamlet',\n /**\n * Points of Interest layer.\n *\n * @remarks\n * Use to position custom content in the layer stack relative to POI icons and labels.\n */\n poi: 'POI',\n /**\n * The lowest labels layer in the stack.\n *\n * @remarks\n * **Most commonly used reference layer.** Positioning layers before this ensures\n * content appears below all text labels, maintaining map readability while keeping\n * visualizations prominent.\n *\n * **Default for SDK modules:**\n * - RoutingModule uses this for route lines and waypoints\n * - GeometriesModule uses this as the default for polygon fills and borders\n *\n * This provides optimal balance between visibility and not obscuring map labels.\n */\n lowestLabel: 'Borders - Treaty label',\n /**\n * The lowest road line layer in the stack.\n *\n * @remarks\n * Represents tunnel railway outlines. Use to position content below the road network,\n * useful for showing base layers or features that should appear beneath transportation\n * infrastructure.\n */\n lowestRoadLine: 'Tunnel - Railway outline',\n /**\n * The lowest building layer in the stack.\n *\n * @remarks\n * Use to position content below 3D building extrusions while keeping it above\n * flat map features like roads and terrain.\n *\n * **Note:** Not available in the Satellite style.\n */\n lowestBuilding: 'Buildings - Underground',\n} as const;\n","/**\n * Source identifier for POI (Point of Interest) vector tiles.\n *\n * @remarks\n * Used to reference the POI layer in the map style, which contains\n * business locations, landmarks, and other points of interest.\n *\n * @group POIs\n */\nexport const POI_SOURCE_ID = 'vectorTiles';\n\n/**\n * Source identifier for hillshade terrain visualization.\n *\n * @remarks\n * References the raster source that provides terrain shading to visualize\n * elevation and topography on the map.\n *\n * @group Hillshade\n */\nexport const HILLSHADE_SOURCE_ID = 'hillshade';\n\n/**\n * Source identifier for base map vector tiles.\n *\n * @remarks\n * References the primary vector tile source containing roads, buildings,\n * land use, water bodies, and other fundamental map features.\n *\n * @group Base Map\n */\nexport const BASE_MAP_SOURCE_ID = 'vectorTiles';\n\n/**\n * Source identifier for traffic incidents vector tiles.\n *\n * @remarks\n * References the vector tile source containing real-time traffic incident data\n * such as accidents, road closures, and construction.\n *\n * @group Traffic Incidents\n */\nexport const TRAFFIC_INCIDENTS_SOURCE_ID = 'vectorTilesIncidents';\n\n/**\n * Source identifier for traffic flow vector tiles.\n *\n * @remarks\n * References the vector tile source containing real-time traffic flow data\n * showing current traffic speeds and congestion levels.\n *\n * @group Traffic Flow\n */\nexport const TRAFFIC_FLOW_SOURCE_ID = 'vectorTilesFlow';\n","/**\n * Layer IDs for POIs on the map.\n * @ignore\n */\nexport const poiLayerIDs = ['POI', 'POI - Micro'];\n","import type { POICategory } from '@tomtom-org/maps-sdk/core';\n\n/**\n * POI category mappings that have a direct equivalent in Map Display POI categories.\n * @ignore\n */\nexport const mapDisplayPoiCategoryMappings: Partial<Record<POICategory, string>> = {\n ACCESS_GATEWAY: 'border_control',\n ADVENTURE_SPORTS_FACILITY: 'sport_facility',\n ADVENTURE_SPORTS_VENUE: 'sport_facility',\n AGRICULTURE: 'agriculture_business',\n AIRPORT: 'airport',\n AMUSEMENT_PARK: 'amusement_park',\n AQUATIC_ZOO: 'zoo_or_aquarium',\n ASHRAM: 'religious_site',\n ATM: 'atm',\n AUTOMOTIVE_DEALER: 'vehicle_dealer',\n BANK: 'bank',\n BEACH: 'beach',\n BUS_STOP: 'bus_station',\n BUSINESS_PARK: 'business',\n CAFE_PUB: 'cafe',\n CAMPING_GROUND: 'camping_ground',\n CAR_WASH: 'vehicle_wash',\n CASH_DISPENSER: 'atm',\n CASINO: 'casino_and_gambling',\n CHURCH: 'religious_site',\n CINEMA: 'cinema',\n CLOTHING_SHOP: 'clothing_shop',\n CLUB_ASSOCIATION: 'club_and_association',\n COLLEGE_UNIVERSITY: 'college_or_university',\n COMMERCIAL_BUILDING: 'business',\n COMMUNITY_CENTER: 'cultural_center',\n COMPANY: 'company_or_office',\n CONCERT_HALL: 'nightlife',\n COURTHOUSE: 'courthouse',\n CULTURAL_CENTER: 'cultural_center',\n DENTIST: 'dentist',\n DEPARTMENT_STORE: 'department_store',\n DOCTOR: 'doctor',\n ELECTRIC_VEHICLE_STATION: 'charging_location',\n EMBASSY: 'institution_office',\n EMERGENCY_MEDICAL_SERVICE: 'emergency',\n EMERGENCY_ROOM: 'emergency',\n ENTERTAINMENT: 'entertainment',\n EXCHANGE: 'stock_exchange',\n EXHIBITION_CONVENTION_CENTER: 'cultural_center',\n FERRY_TERMINAL: 'ferry_terminal',\n FIRE_STATION_BRIGADE: 'fire_station',\n FRONTIER_CROSSING: 'border_control',\n FUEL_FACILITIES: 'fuel_station',\n GAS_STATION: 'fuel_station',\n GEOGRAPHIC_FEATURE: 'geographic_feature',\n GOLD_EXCHANGE: 'gold_exchange',\n GOLF_COURSE: 'golf_course',\n GOVERNMENT_OFFICE: 'institution_office',\n GURUDWARA: 'religious_site',\n HEALTH_CARE_SERVICE: 'healthcare_service',\n HELIPAD_HELICOPTER_LANDING: 'heliport_or_helipad',\n HILL: 'peak_high',\n HOLIDAY_RENTAL: 'holiday_home',\n HOSPITAL: 'hospital',\n HOSPITAL_POLYCLINIC: 'hospital',\n HOTEL_MOTEL: 'hotel_or_motel',\n ICE_SKATING_RINK: 'winter_sports',\n IMPORTANT_TOURIST_ATTRACTION: 'tourist_attraction',\n INDUSTRIAL_BUILDING: 'industrial_facility',\n LEISURE_CENTER: 'leisure',\n LIBRARY: 'public_library',\n MANUFACTURING_FACILITY: 'manufacturing_facility',\n MARINA: 'marina',\n MARKET: 'marketplace',\n MEDIA_FACILITY: 'media_facility',\n MILITARY_INSTALLATION: 'military_facility',\n MOSQUE: 'religious_site',\n MOTORING_ORGANIZATION_OFFICE: 'traffic_office',\n MOUNTAIN_PASS: 'mountain_pass',\n MOUNTAIN_PEAK: 'peak',\n MOVIE_THEATER: 'cinema',\n MUSEUM: 'museum',\n NATIVE_RESERVATION: 'aboriginal_land',\n NIGHTLIFE: 'nightlife',\n NON_GOVERNMENTAL_ORGANIZATION: 'ngo_office',\n OPEN_PARKING_AREA: 'parking_facility',\n OPERA_HOUSE: 'theater',\n PAGODA: 'religious_site',\n PARK_RECREATION_AREA: 'park_and_recreation_area',\n PARKING_GARAGE: 'parking_facility',\n PETROL_STATION: 'fuel_station',\n PHARMACY: 'pharmacy',\n PLACE_OF_WORSHIP: 'religion',\n POLICE_STATION: 'police_station',\n PORT_WAREHOUSE_FACILITY: 'industrial_facility',\n POST_OFFICE: 'post_office',\n PRIMARY_RESOURCE_UTILITY: 'industrial_facility',\n PRISON_CORRECTIONAL_FACILITY: 'prison',\n PUBLIC_AMENITY: 'public_amenity',\n PUBLIC_TRANSPORT_STOP: 'public_transport_stop',\n RAILWAY_STATION: 'railway_station',\n RENT_A_CAR_FACILITY: 'car_rental',\n RENT_A_CAR_PARKING: 'rental_car_parking',\n REPAIR_FACILITY: 'vehicle_repair',\n RESEARCH_FACILITY: 'research_facility',\n RESIDENTIAL_ACCOMMODATION: 'business',\n REST_AREA: 'rest_area',\n RESTAURANT: 'restaurant',\n RESTAURANT_AREA: 'restaurant',\n SCENIC_PANORAMIC_VIEW: 'viewpoint',\n SCHOOL: 'education',\n SHOP: 'shop_or_store',\n SHOPPING_CENTER: 'shop_or_store',\n SPORTS_CENTER: 'sport_facility',\n STADIUM: 'stadium',\n SUPERMARKETS_HYPERMARKETS: 'supermarket',\n SWIMMING_POOL: 'swimming_pool',\n SYNAGOG: 'religious_site',\n TAXI_STAND: 'taxi_stand',\n TEMPLE: 'religious_site',\n TENNIS_COURT: 'sport_facility',\n THEATER: 'theater',\n TOLL_GATE: 'toll_gantry',\n TOURIST_INFORMATION_OFFICE: 'tourist_information',\n TRAFFIC_CONTROL_DEPARTMENT: 'traffic_office',\n TRAFFIC_SERVICE_CENTER: 'traffic_office',\n TRAIL_SYSTEM: 'trailhead',\n TRAILS: 'trailhead',\n TRANSPORT_AUTHORITY_VEHICLE_REGISTRATION: 'traffic_office',\n TRUCK_STOP: 'truck_stop',\n VACATION_RENTAL: 'holiday_home',\n VETERINARIAN: 'veterinary',\n WATER_SPORT: 'outdoor',\n WEIGH_STATION: 'weighbridge',\n WELFARE_ORGANIZATION: 'social_service',\n WINERY: 'manufacturing_facility',\n ZOOS_ARBORETA_BOTANICAL_GARDEN: 'zoo_or_aquarium',\n} as const;\n\n/**\n * Complete POI category mappings including all POI categories.\n * Extends the original mappings with additional categories mapped to semantically appropriate values.\n * @ignore\n */\nconst completeMapDisplayPoiCategoryMappings: Record<POICategory, string> = {\n ...(mapDisplayPoiCategoryMappings as Record<POICategory, string>),\n // Additional POI categories not in the original mapping\n SECURED_ENTRANCE: 'border_control',\n AGRICULTURAL_BUSINESS: 'agriculture_business',\n FARM: 'agriculture_business',\n HORTICULTURE: 'agriculture_business',\n PRIMARY_PRODUCER: 'agriculture_business',\n AIRFIELD: 'airport',\n AIRLINE_ACCESS: 'airport',\n MILITARY_AIRPORT: 'airport',\n PRIVATE_AIRPORT: 'airport',\n PUBLIC_AIRPORT: 'airport',\n ATV_DEALER: 'vehicle_dealer',\n BOAT_DEALER: 'boat_dealer',\n BUS_DEALER: 'vehicle_dealer',\n CAR_DEALER: 'vehicle_dealer',\n MOTORCYCLE_DEALER: 'vehicle_dealer',\n RECREATIONAL_VEHICLE_DEALER: 'vehicle_dealer',\n TRUCK_DEALER: 'vehicle_dealer',\n VAN_DEALER: 'vehicle_dealer',\n DIVERSIFIED_FINANCIALS: 'bank',\n SAVINGS_INSTITUTION: 'bank',\n BEACH_CLUB: 'beach',\n BAR: 'bar',\n CAFE: 'cafe',\n COCKTAIL_BAR: 'bar',\n COFFEE_SHOP: 'coffee_shop',\n INTERNET_CAFE: 'cafe',\n PUB: 'pub',\n TEA_HOUSE: 'tea_house',\n WINE_BAR: 'bar',\n CARAVAN_SITE: 'camping_ground',\n RECREATIONAL_CAMPING_GROUND: 'camping_ground',\n REST_CAMP: 'camping_ground',\n TRUCK_WASH: 'vehicle_wash',\n DRIVE_IN_MOVIES: 'cinema',\n CHILDRENS_CLOTHES: 'clothing_shop',\n MENS_CLOTHING: 'clothing_shop',\n SPECIALTY_CLOTHING_SHOP: 'clothing_shop',\n WOMENS_CLOTHING: 'clothing_shop',\n PRIVATE_CLUB: 'club_and_association',\n JUNIOR_COLLEGE_COMMUNITY_COLLEGE: 'college_or_university',\n BUILDING: 'business',\n ADVERTISING_COMPANY: 'company_or_office',\n AGRICULTURAL_TECHNOLOGY: 'company_or_office',\n AIRLINE_COMPANY: 'company_or_office',\n AUTOMOBILE_COMPANY: 'company_or_office',\n BUSINESS_SERVICES: 'company_or_office',\n BUS_CHARTER_COMPANY: 'company_or_office',\n CABLE_TELEPHONE_COMPANY: 'company_or_office',\n CLEANING_SERVICES: 'company_or_office',\n COMPUTER_DATA_SERVICES: 'company_or_office',\n CONSTRUCTION_COMPANY: 'company_or_office',\n DELIVERY_SERVICE: 'company_or_office',\n ELECTRONICS_COMPANY: 'company_or_office',\n EQUIPMENT_RENTAL: 'company_or_office',\n FUNERAL_SERVICE_MORTUARIES: 'company_or_office',\n IMPORT_EXPORT_AND_DISTRIBUTION: 'company_or_office',\n INSURANCE_COMPANY: 'company_or_office',\n INVESTMENT_ADVISOR: 'company_or_office',\n LEGAL_SERVICES: 'company_or_office',\n MINING_COMPANY: 'company_or_office',\n MOVING_STORAGE_COMPANY: 'company_or_office',\n OIL_NATURAL_GAS: 'company_or_office',\n PHARMACEUTICAL_COMPANY: 'company_or_office',\n PUBLIC_HEALTH_TECHNOLOGY_COMPANY: 'company_or_office',\n PUBLISHING_TECHNOLOGIES: 'company_or_office',\n REAL_ESTATE_AGENT: 'company_or_office',\n REAL_ESTATE_COMPANY: 'company_or_office',\n SERVICE_COMPANY: 'company_or_office',\n SOFTWARE_COMPANY: 'company_or_office',\n TAX_SERVICES: 'company_or_office',\n TELECOMMUNICATIONS: 'company_or_office',\n TRANSPORT_COMPANY: 'company_or_office',\n TRAVEL_AGENT: 'company_or_office',\n WEDDING_SERVICES: 'company_or_office',\n GENERAL_PRACTITIONER: 'doctor',\n SPECIALIST: 'doctor',\n RIDGE: 'peak',\n AMBULANCE_UNIT: 'emergency',\n ROAD_RESCUE: 'emergency',\n AMUSEMENT_ARCADE: 'entertainment',\n AMUSEMENT_PLACE: 'entertainment',\n BETTING_STATION: 'entertainment',\n FAIRGROUND: 'entertainment',\n MUSIC_CENTER: 'entertainment',\n CHECKPOINT: 'border_control',\n BAY: 'geographic_feature',\n BRIDGE: 'geographic_feature',\n BRIDGE_TUNNEL_OPERATIONS: 'geographic_feature',\n CAPE: 'geographic_feature',\n COVE: 'geographic_feature',\n DAM: 'geographic_feature',\n DUNE: 'geographic_feature',\n ISLAND: 'geographic_feature',\n LAGOON: 'geographic_feature',\n LAKESHORE: 'geographic_feature',\n LOCALE: 'geographic_feature',\n MARSH: 'geographic_feature',\n OASIS: 'geographic_feature',\n PAN: 'geographic_feature',\n PARKWAY: 'geographic_feature',\n PLAIN_FLAT: 'geographic_feature',\n PLATEAU: 'geographic_feature',\n RAPIDS: 'geographic_feature',\n REEF: 'geographic_feature',\n RESERVOIR: 'geographic_feature',\n RIVER_CROSSING: 'geographic_feature',\n RIVER_SCENIC_AREA: 'geographic_feature',\n ROCKS: 'geographic_feature',\n SEASHORE: 'geographic_feature',\n TUNNEL: 'geographic_feature',\n VALLEY: 'geographic_feature',\n WATER_HOLE: 'geographic_feature',\n WELL: 'geographic_feature',\n BUNGALOW_RENTAL: 'holiday_home',\n CABINS_LODGES: 'holiday_home',\n CHALET_RENTAL: 'holiday_home',\n COTTAGE_RENTAL: 'holiday_home',\n VILLA_RENTAL: 'holiday_home',\n BLOOD_BANK: 'hospital',\n HOSPITAL_FOR_WOMEN_AND_CHILDREN: 'hospital',\n HOSPITAL_OF_CHINESE_MEDICINE: 'hospital',\n SPECIAL_HOSPITAL: 'hospital',\n B_B_GUEST_HOUSE: 'hotel_or_motel',\n HOSTEL: 'hotel_or_motel',\n HOTEL: 'hotel_or_motel',\n MOTEL: 'hotel_or_motel',\n RESORT: 'hotel_or_motel',\n QUARRY: 'industrial_facility',\n AUTOMOBILE_MANUFACTURING: 'manufacturing_facility',\n CHEMICAL_COMPANY: 'manufacturing_facility',\n MANUFACTURING_COMPANY: 'manufacturing_facility',\n MECHANICAL_ENGINEERING: 'manufacturing_facility',\n MICROBREWERY: 'manufacturing_facility',\n OEM: 'manufacturing_facility',\n BOAT_LAUNCHING_RAMP: 'marina',\n HARBOR: 'marina',\n YACHT_BASIN: 'marina',\n FARMERS_MARKET: 'marketplace',\n FOOD_MARKET: 'marketplace',\n INFORMAL_MARKET: 'marketplace',\n PUBLIC_MARKET: 'marketplace',\n PLANETARIUM: 'planetarium',\n CABARET_THEATER: 'nightlife',\n COMEDY_CLUB: 'comedy_club',\n DISCO_CLUB: 'nightlife',\n JAZZ_CLUB: 'nightlife',\n KARAOKE_CLUB: 'nightlife',\n FISHING_HUNTING_AREA: 'park_and_recreation_area',\n FOREST_AREA: 'park_and_recreation_area',\n HISTORICAL_PARK: 'park_and_recreation_area',\n NATURAL_RECREATION_ATTRACTION: 'park_and_recreation_area',\n PARK: 'park_and_recreation_area',\n PICNIC_AREA: 'park_and_recreation_area',\n PRESERVE: 'park_and_recreation_area',\n RECREATION_AREA: 'park_and_recreation_area',\n WILDERNESS_AREA: 'park_and_recreation_area',\n OPEN_CAR_PARKING_AREA: 'parking_facility',\n DRUG_STORE: 'pharmacy',\n MARIJUANA_DISPENSARY: 'pharmacy',\n MEDICINAL_MARIJUANA_DISPENSARY: 'pharmacy',\n RECREATIONAL_MARIJUANA_DISPENSARY: 'pharmacy',\n COURIER_DROP_BOX: 'post_office',\n LOCAL_POST_OFFICE: 'post_office',\n PUBLIC_CALL_BOX: 'public_amenity',\n PUBLIC_TOILET: 'public_amenity',\n BUS_LINES: 'public_transport_stop',\n COACH_STOP: 'public_transport_stop',\n PASSENGER_TRANSPORT_TICKET_OFFICE: 'public_transport_stop',\n PEDESTRIAN_SUBWAY: 'public_transport_stop',\n STREETCAR_STOP: 'public_transport_stop',\n SUBWAY_STATION: 'public_transport_stop',\n INTERNATIONAL_RAILROAD_STATION: 'railway_station',\n NATIONAL_RAILROAD_STATION: 'railway_station',\n RAILROAD_SIDING: 'railway_station',\n STATION_ACCESS: 'railway_station',\n URBAN_STATION: 'railway_station',\n BODYSHOP: 'vehicle_repair',\n CAR_GLASS_REPLACEMENT_SHOP: 'vehicle_repair',\n CAR_REPAIR_AND_SERVICE: 'vehicle_repair',\n HOME_APPLIANCE_REPAIR: 'vehicle_repair',\n MOTORCYCLE_REPAIR: 'vehicle_repair',\n OTHER_REPAIR_SHOPS: 'vehicle_repair',\n REPAIR_SHOP: 'vehicle_repair',\n TIRE_SERVICE: 'vehicle_repair',\n TRUCK_REPAIR_AND_SERVICE: 'vehicle_repair',\n AFGHAN_RESTAURANT: 'restaurant',\n AFRICAN_RESTAURANT: 'restaurant',\n ALGERIAN_RESTAURANT: 'restaurant',\n AMERICAN_RESTAURANT: 'restaurant',\n ARABIAN_RESTAURANT: 'restaurant',\n ARGENTINIAN_RESTAURANT: 'restaurant',\n ARMENIAN_RESTAURANT: 'restaurant',\n ASIAN_RESTAURANT: 'restaurant',\n AUSTRALIAN_RESTAURANT: 'restaurant',\n AUSTRIAN_RESTAURANT: 'restaurant',\n BANQUET_ROOMS: 'restaurant',\n BARBECUE_RESTAURANT: 'restaurant',\n BASQUE_RESTAURANT: 'restaurant',\n BELGIAN_RESTAURANT: 'restaurant',\n BISTRO: 'restaurant',\n BOLIVIAN_RESTAURANT: 'restaurant',\n BOSNIAN_RESTAURANT: 'restaurant',\n BRAZILIAN_RESTAURANT: 'restaurant',\n BRITISH_RESTAURANT: 'restaurant',\n BUFFET_RESTAURANT: 'restaurant',\n BULGARIAN_RESTAURANT: 'restaurant',\n BURMESE_RESTAURANT: 'restaurant',\n CAFETERIA: 'restaurant',\n CALIFORNIAN_RESTAURANT: 'restaurant',\n CAMBODIAN_RESTAURANT: 'restaurant',\n CANADIAN_RESTAURANT: 'restaurant',\n CARIBBEAN_RESTAURANT: 'restaurant',\n CATERING_SERVICES: 'restaurant',\n CHICKEN_RESTAURANT: 'restaurant',\n CHILEAN_RESTAURANT: 'restaurant',\n CHINESE_RESTAURANT: 'restaurant',\n COLOMBIAN_RESTAURANT: 'restaurant',\n CORSICAN_RESTAURANT: 'restaurant',\n CREOLE_RESTAURANT: 'restaurant',\n CREPERIE: 'restaurant',\n CUBAN_RESTAURANT: 'restaurant',\n CYPRIOT_RESTAURANT: 'restaurant',\n CZECH_RESTAURANT: 'restaurant',\n DANISH_RESTAURANT: 'restaurant',\n DINNER_THEATER: 'restaurant',\n DOMINICAN_RESTAURANT: 'restaurant',\n DONGBEI_RESTAURANT: 'restaurant',\n DOUGHNUT_RESTAURANT: 'restaurant',\n DUTCH_RESTAURANT: 'restaurant',\n EGYPTIAN_RESTAURANT: 'restaurant',\n ENGLISH_RESTAURANT: 'restaurant',\n EROTIC_RESTAURANT: 'restaurant',\n ETHIOPIAN_RESTAURANT: 'restaurant',\n EXOTIC_RESTAURANT: 'restaurant',\n FAST_FOOD: 'restaurant',\n FINNISH_RESTAURANT: 'restaurant',\n FONDUE_RESTAURANT: 'restaurant',\n FRENCH_RESTAURANT: 'restaurant',\n FUSION_RESTAURANT: 'restaurant',\n GERMAN_RESTAURANT: 'restaurant',\n GREEK_RESTAURANT: 'restaurant',\n GRILL_RESTAURANT: 'restaurant',\n GUANGDONG_RESTAURANT: 'restaurant',\n HAMBURGER_RESTAURANT: 'restaurant',\n HAWAIIAN_RESTAURANT: 'restaurant',\n HOT_POT_RESTAURANT: 'restaurant',\n HUNAN_RESTAURANT: 'restaurant',\n HUNGARIAN_RESTAURANT: 'restaurant',\n ICE_CREAM_PARLOR: 'restaurant',\n INDIAN_RESTAURANT: 'restaurant',\n INDONESIAN_RESTAURANT: 'restaurant',\n INTERNATIONAL_RESTAURANT: 'restaurant',\n IRANIAN_RESTAURANT: 'restaurant',\n IRISH_RESTAURANT: 'restaurant',\n ISRAELI_RESTAURANT: 'restaurant',\n ITALIAN_RESTAURANT: 'restaurant',\n JAMAICAN_RESTAURANT: 'restaurant',\n JAPANESE_RESTAURANT: 'restaurant',\n JEWISH_RESTAURANT: 'restaurant',\n KOREAN_RESTAURANT: 'restaurant',\n KOSHER_RESTAURANT: 'restaurant',\n LATIN_AMERICAN_RESTAURANT: 'restaurant',\n LEBANESE_RESTAURANT: 'restaurant',\n LUXEMBOURGIAN_RESTAURANT: 'restaurant',\n MACROBIOTIC_RESTAURANT: 'restaurant',\n MAGHRIB_RESTAURANT: 'restaurant',\n MALTESE_RESTAURANT: 'restaurant',\n MAURITIAN_RESTAURANT: 'restaurant',\n MEDITERRANEAN_RESTAURANT: 'restaurant',\n MEXICAN_RESTAURANT: 'restaurant',\n MIDDLE_EASTERN_RESTAURANT: 'restaurant',\n MONGOLIAN_RESTAURANT: 'restaurant',\n MOROCCAN_RESTAURANT: 'restaurant',\n MUSSELS_RESTAURANT: 'restaurant',\n NEPALESE_RESTAURANT: 'restaurant',\n NORWEGIAN_RESTAURANT: 'restaurant',\n ORGANIC_FOOD_RESTAURANT: 'restaurant',\n ORIENTAL_RESTAURANT: 'restaurant',\n PAKISTANI_RESTAURANT: 'restaurant',\n PERUVIAN_RESTAURANT: 'restaurant',\n PHILIPPINE_RESTAURANT: 'restaurant',\n PIZZERIA: 'restaurant',\n POLISH_RESTAURANT: 'restaurant',\n POLYNESIAN_RESTAURANT: 'restaurant',\n PORTUGUESE_RESTAURANT: 'restaurant',\n PROVENCAL_RESTAURANT: 'restaurant',\n PUB_FOOD: 'restaurant',\n ROADSIDE_RESTAURANT: 'restaurant',\n ROMANIAN_RESTAURANT: 'restaurant',\n RUSSIAN_RESTAURANT: 'restaurant',\n SALAD_BAR: 'restaurant',\n SANDWICH_RESTAURANT: 'restaurant',\n SAVOY_RESTAURANT: 'restaurant',\n SCANDINAVIAN_RESTAURANT: 'restaurant',\n SCOTTISH_RESTAURANT: 'restaurant',\n SEAFOOD: 'restaurant',\n SHANDONG_RESTAURANT: 'restaurant',\n SHANGHAI_RESTAURANT: 'restaurant',\n SICHUAN_RESTAURANT: 'restaurant',\n SICILIAN_RESTAURANT: 'restaurant',\n SLAVIC_RESTAURANT: 'restaurant',\n SLOVAK_RESTAURANT: 'restaurant',\n SNACKS_RESTAURANT: 'restaurant',\n SOUL_FOOD: 'restaurant',\n SOUP_RESTAURANT: 'restaurant',\n SPANISH_RESTAURANT: 'restaurant',\n STEAK_HOUSE: 'restaurant',\n SUDANESE_RESTAURANT: 'restaurant',\n SURINAMESE_RESTAURANT: 'restaurant',\n SUSHI_RESTAURANT: 'restaurant',\n SWEDISH_RESTAURANT: 'restaurant',\n SWISS_RESTAURANT: 'restaurant',\n SYRIAN_RESTAURANT: 'restaurant',\n TAIWANESE_RESTAURANT: 'restaurant',\n TAKEOUT_FOOD: 'restaurant',\n TAPAS_RESTAURANT: 'restaurant',\n TEPPANYAKI_RESTAURANT: 'restaurant',\n THAI_RESTAURANT: 'restaurant',\n TIBETAN_RESTAURANT: 'restaurant',\n TUNISIAN_RESTAURANT: 'restaurant',\n TURKISH_RESTAURANT: 'restaurant',\n URUGUAYAN_RESTAURANT: 'restaurant',\n VEGETARIAN_RESTAURANT: 'restaurant',\n VENEZUELAN_RESTAURANT: 'restaurant',\n VIETNAMESE_RESTAURANT: 'restaurant',\n WELSH_RESTAURANT: 'restaurant',\n WESTERN_RESTAURANT: 'restaurant',\n YOGURT_JUICE_BAR: 'restaurant',\n ART_SCHOOL: 'education',\n CHILD_CARE_FACILITY: 'education',\n CULINARY_SCHOOL: 'education',\n DANCE_STUDIO_SCHOOL: 'education',\n DRIVING_SCHOOL: 'education',\n HIGH_SCHOOL: 'education',\n LANGUAGE_SCHOOL: 'education',\n MIDDLE_SCHOOL: 'education',\n PRE_SCHOOL: 'education',\n PRIMARY_SCHOOL: 'education',\n SENIOR_HIGH_SCHOOL: 'education',\n SPECIAL_SCHOOL: 'education',\n SPORT_SCHOOL: 'education',\n TECHNICAL_SCHOOL: 'education',\n VOCATIONAL_SCHOOL: 'education',\n AGRICULTURAL_SUPPLIES: 'shop_or_store',\n ANTIQUE_ART_SHOP: 'shop_or_store',\n BAGS_LEATHERWEAR: 'shop_or_store',\n BAKERY: 'shop_or_store',\n BEAUTY_SALON: 'shop_or_store',\n BEAUTY_SUPPLIES: 'shop_or_store',\n BOATING_EQUIPMENT_ACCESSORIES: 'shop_or_store',\n BOOK_SHOP: 'shop_or_store',\n BUTCHER: 'shop_or_store',\n CAMERAS_PHOTOGRAPHY: 'shop_or_store',\n CARPET_FLOOR_COVERINGS: 'shop_or_store',\n CAR_ACCESSORIES: 'shop_or_store',\n CHRISTMAS_HOLIDAY_SHOP: 'shop_or_store',\n COMPUTER_COMPUTER_SUPPLIES: 'shop_or_store',\n CONSTRUCTION_MATERIAL_EQUIPMENT: 'shop_or_store',\n CONSUMER_ELECTRONICS: 'shop_or_store',\n CONVENIENCE_STORE: 'shop_or_store',\n CURTAINS_TEXTILES: 'shop_or_store',\n C_DS_DVD_VIDEOS: 'shop_or_store',\n DELICATESSEN: 'shop_or_store',\n DO_IT_YOURSELF_CENTERS: 'shop_or_store',\n DRIVE_THROUGH_BOTTLE_SHOP: 'shop_or_store',\n DRY_CLEANER: 'shop_or_store',\n ELECTRICAL_APPLIANCES_SHOP: 'shop_or_store',\n FACTORY_OUTLET: 'shop_or_store',\n FISHMONGER: 'shop_or_store',\n FLORISTS: 'shop_or_store',\n FOOTWEAR_SHOE_REPAIRS: 'shop_or_store',\n FURNITURE_HOME_FURNISHINGS: 'shop_or_store',\n GARDEN_CENTERS_SERVICES: 'shop_or_store',\n GIFTS_CARDS_NOVELTIES_SOUVENIRS: 'shop_or_store',\n GLASSWARE_CERAMIC_SHOP: 'shop_or_store',\n GLASS_WINDOWS_STORE: 'shop_or_store',\n GREENGROCER: 'shop_or_store',\n GROCERY_STORE: 'shop_or_store',\n HAIRDRESSER: 'shop_or_store',\n HARDWARE_STORE: 'shop_or_store',\n HOBBY_SHOP: 'shop_or_store',\n HOUSE_GARDEN_FURNITURE_FITTINGS: 'shop_or_store',\n JEWELRY_CLOCKS_WATCHES: 'shop_or_store',\n KITCHENS_BATHROOMS: 'shop_or_store',\n LAUNDRY: 'shop_or_store',\n LIGHTING_SHOPS: 'shop_or_store',\n LOCAL_SPECIALITIES_SHOP: 'shop_or_store',\n LOTTERY_SHOP: 'shop_or_store',\n MARINE_ELECTRONIC_EQUIPMENT: 'shop_or_store',\n MEDICAL_SUPPLIES_EQUIPMENT: 'shop_or_store',\n MOBILE_PHONE_SHOP: 'shop_or_store',\n MUSIC_INSTRUMENTS_STORE: 'shop_or_store',\n NAIL_SALON: 'shop_or_store',\n NEWSAGENTS_TOBACCONISTS: 'shop_or_store',\n OFFICE_EQUIPMENT: 'shop_or_store',\n OPTICIAN: 'shop_or_store',\n OTHER_FOOD_SHOPS: 'shop_or_store',\n PAINTING_DECORATING: 'shop_or_store',\n PAWN_SHOP: 'shop_or_store',\n PERSONAL_CARE_FACILITY: 'shop_or_store',\n PERSONAL_SERVICE: 'shop_or_store',\n PET_SUPPLIES: 'shop_or_store',\n PHOTOCOPY_SHOP: 'shop_or_store',\n PHOTO_LAB_DEVELOPMENT: 'shop_or_store',\n RECYCLING_SHOP: 'shop_or_store',\n RETAIL_OUTLET: 'shop_or_store',\n SAUNA_SOLARIUM_MASSAGE: 'shop_or_store',\n SECURITY_PRODUCTS: 'shop_or_store',\n SHOPPING_SERVICE: 'shop_or_store',\n SPECIALTY_FOODS: 'shop_or_store',\n SPORTS_EQUIPMENT_CLOTHING: 'shop_or_store',\n STAMP_SHOP: 'shop_or_store',\n TAILOR_SHOP: 'shop_or_store',\n TOYS_GAMES_SHOP: 'shop_or_store',\n VARIETY_STORE: 'shop_or_store',\n VIDEO_RENTAL_SHOP: 'shop_or_store',\n WHOLESALE_CLUB: 'shop_or_store',\n WINE_SPIRITS: 'shop_or_store',\n ATHLETICS_TRACK: 'sport_facility',\n BASEBALL_PARK: 'sport_facility',\n BASKETBALL_ARENA: 'sport_facility',\n BOWLING_CENTER: 'sport_facility',\n CRICKET_GROUND: 'sport_facility',\n FITNESS_CLUB_CENTER: 'sport_facility',\n FLYING_CLUB: 'sport_facility',\n HOCKEY_CLUB: 'sport_facility',\n HORSE_RACING_TRACK: 'sport_facility',\n HORSE_RIDING_CENTER: 'sport_facility',\n ICE_HOCKEY_ARENA: 'sport_facility',\n OTHER_WINTER_SPORT: 'sport_facility',\n RACE_TRACK: 'sport_facility',\n RUGBY_GROUND: 'sport_facility',\n SKI_RESORT: 'sport_facility',\n SNOOKER_POOL_BILLIARD: 'sport_facility',\n THEMATIC_SPORT_CENTER: 'sport_facility',\n FOOTBALL_STADIUM: 'stadium',\n MOTOR_RACING_STADIUM: 'stadium',\n MULTI_PURPOSE_STADIUM: 'stadium',\n NETBALL_STADIUM: 'stadium',\n SOCCER_STADIUM: 'stadium',\n STOCK_EXCHANGE: 'stock_exchange',\n TAXI_LIMOUSINE_SHUTTLE_SERVICE: 'taxi_stand',\n AMPHITHEATER: 'theater',\n ARCH: 'tourist_attraction',\n BATTLEFIELD: 'tourist_attraction',\n CAVE: 'tourist_attraction',\n CEMETERY: 'tourist_attraction',\n HISTORIC_SITE: 'tourist_attraction',\n MAUSOLEUM_GRAVE: 'tourist_attraction',\n MEMORIAL: 'tourist_attraction',\n MINERAL_HOT_SPRINGS: 'tourist_attraction',\n MONUMENT: 'tourist_attraction',\n NATURAL_TOURIST_ATTRACTION: 'tourist_attraction',\n OBSERVATORY: 'tourist_attraction',\n STATUE: 'tourist_attraction',\n TOURIST_ATTRACTION: 'tourist_attraction',\n TOWER: 'tourist_attraction',\n ROAD_TRAFFIC_CONTROL_CENTER: 'traffic_office',\n ADVENTURE_VEHICLE_TRAIL: 'trailhead',\n HIKING_TRAIL: 'trailhead',\n HORSE_RIDING_TRAIL: 'trailhead',\n MOUNTAIN_BIKE_TRAIL: 'trailhead',\n ROCK_CLIMBING_TRAIL: 'trailhead',\n APARTMENT_RENTAL: 'business',\n CONDOMINIUM_COMPLEX: 'business',\n FLATS_APARTMENT_COMPLEX: 'business',\n RESIDENTIAL_ESTATE: 'business',\n RETIREMENT_COMMUNITY: 'business',\n TOWNHOUSE_COMPLEX: 'business',\n ANIMAL_SERVICES: 'veterinary',\n ANIMAL_SHELTER: 'animal_shelter',\n WEIGH_SCALES: 'weighbridge',\n WILDLIFE_PARK: 'zoo_or_aquarium',\n ZOO: 'zoo_or_aquarium',\n} as const;\n\n/**\n * Map style POI category type.\n *\n * @remarks\n * Represents all available POI (Point of Interest) categories used in the map style.\n * These categories correspond to Search API classification codes and are used for\n * filtering, styling, and displaying POI icons on the map.\n *\n * Each category maps to a specific icon sprite and can be used with custom icon configurations.\n *\n * @example\n * ```ts\n * const category: MapStylePOICategory = 'RESTAURANT';\n * ```\n *\n * @see {@link POICategoryGroup} - For grouped collections of related categories\n *\n * @group POIs\n */\nexport type MapStylePOICategory = keyof typeof mapDisplayPoiCategoryMappings;\n\n/**\n * @ignore\n */\nexport const toBaseMapPOICategory = (category: POICategory): string => completeMapDisplayPoiCategoryMappings[category];\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * Bold font face used in the TomTom map style.\n *\n * @remarks\n * This is the primary bold font used for prominent labels and headings in the map.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_BOLD_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_BOLD_FONT],\n * 'text-field': ['get', 'name']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_BOLD_FONT = 'Noto-Bold';\n\n/**\n * Regular font face used in the TomTom map style.\n *\n * @remarks\n * This is the standard font used for most text labels on the map.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_REGULAR_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_REGULAR_FONT],\n * 'text-field': ['get', 'description']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_REGULAR_FONT = 'Noto-Regular';\n\n/**\n * Medium weight font face used in the TomTom map style.\n *\n * @remarks\n * This font provides a middle ground between regular and bold weights.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_MEDIUM_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_MEDIUM_FONT],\n * 'text-field': ['get', 'title']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_MEDIUM_FONT = 'Noto-Medium';\n\n/**\n * Italic font face used in the TomTom map style.\n *\n * @remarks\n * This is the italic variant used for emphasized or secondary text labels.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_ITALIC_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_ITALIC_FONT],\n * 'text-field': ['get', 'subtitle']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_ITALIC_FONT = 'NotoSans-MediumItalic';\n\n/**\n * Default text size expression used in the TomTom map style.\n *\n * @remarks\n * This MapLibre expression defines zoom-dependent text sizing that scales from 12px at zoom 10\n * to 14px at zoom 16. Use this for consistent text sizing across zoom levels.\n *\n * @example\n * ```typescript\n * import { DEFAULT_TEXT_SIZE } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-size': DEFAULT_TEXT_SIZE,\n * 'text-field': ['get', 'name']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const DEFAULT_TEXT_SIZE: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 10, 14, 18, 16];\n\n/**\n * Array of all available font faces in the TomTom map style.\n *\n * @remarks\n * Contains all font variants available in the map style. Useful for font selection\n * or validation when creating custom layers.\n *\n * @example\n * ```typescript\n * import { mapFonts } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Check if a font is available\n * if (mapFonts.includes('Noto-Bold')) {\n * // Use the font\n * }\n *\n * // Use as fallback list\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [...mapFonts]\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const mapFonts = [MAP_REGULAR_FONT, MAP_ITALIC_FONT, MAP_BOLD_FONT, MAP_MEDIUM_FONT] as const;\n\n/**\n * Type representing valid font faces available in the TomTom map style.\n *\n * @remarks\n * Use this type to ensure type-safe font selection when working with text layers.\n * Restricts values to only the fonts available in the map style.\n *\n * @example\n * ```typescript\n * import type { MapFont } from '@tomtom-international/maps-sdk-js/map';\n *\n * function createTextLayer(font: MapFont) {\n * return {\n * type: 'symbol',\n * layout: {\n * 'text-font': [font],\n * 'text-field': ['get', 'name']\n * }\n * };\n * }\n *\n * // Type-safe: only accepts valid map fonts\n * const layer = createTextLayer('Noto-Bold');\n * ```\n *\n * @group Map Style\n */\nexport type MapFont = (typeof mapFonts)[number];\n\n/**\n * Default pin icon scale at max zoom level (zoom 22).\n * This is the icon-size scale factor - 1.0 would be the original icon size.\n * @ignore\n */\nexport const DEFAULT_MAX_PIN_SCALE = 0.8;\n\n/**\n * @ignore\n */\nexport const PIN_ICON_SIZE: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 8,\n 0.6,\n 22,\n DEFAULT_MAX_PIN_SCALE,\n];\n\n/**\n * @ignore\n */\nexport const SELECTED_PIN_ICON_SIZE: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 8, 0.8, 22, 1];\n","import { SymbolLayerSpecification } from 'maplibre-gl';\nimport { LayerSpecTemplate } from '../types';\nimport { DEFAULT_TEXT_SIZE, MAP_BOLD_FONT, PIN_ICON_SIZE } from './commonLayerProps';\n\n/**\n * @ignore\n */\nexport const TITLE = 'title';\n\n/**\n * @ignore\n */\nexport const ICON_ID = 'iconID';\n\n/**\n * Y-offset multiplier for text positioned below icons (top anchor).\n * In ems, relative to icon scale.\n * @ignore\n */\nexport const DEFAULT_TEXT_OFFSET_Y = 0.7;\n\n/**\n * X-offset multiplier for text positioned beside icons (left/right anchors).\n * In ems, relative to icon scale.\n * @ignore\n */\nexport const DEFAULT_TEXT_OFFSET_X = 1.4;\n\n/**\n * @ignore\n */\nexport const DEFAULT_PLACE_ICON_ID = 'default_place';\n\n/**\n * @ignore\n */\nexport const pinIconBaseLayout: SymbolLayerSpecification['layout'] = {\n 'icon-image': ['get', ICON_ID],\n 'icon-anchor': 'bottom',\n 'icon-size': PIN_ICON_SIZE,\n 'icon-allow-overlap': true,\n 'icon-padding': 0,\n};\n\n/**\n * @ignore\n */\nexport const pinIconBasePaint: SymbolLayerSpecification['paint'] = {\n 'icon-translate': [0, 5],\n 'icon-translate-anchor': 'viewport',\n};\n\n/**\n * @ignore\n */\nexport const pinTextBaseLayout: SymbolLayerSpecification['layout'] = {\n 'text-optional': true,\n 'text-font': [MAP_BOLD_FONT],\n 'text-field': ['get', TITLE],\n 'text-justify': 'auto',\n 'text-variable-anchor': ['top', 'left', 'right'],\n // NOTE: make sure to text against pins and waypoints, in a way that there's enough distance from the pin so the text doesn't disappear\n 'text-variable-anchor-offset': [\n 'top',\n [0, DEFAULT_TEXT_OFFSET_Y],\n 'left',\n [DEFAULT_TEXT_OFFSET_X, -DEFAULT_TEXT_OFFSET_X],\n 'right',\n [-DEFAULT_TEXT_OFFSET_X, -DEFAULT_TEXT_OFFSET_X],\n ],\n 'text-size': DEFAULT_TEXT_SIZE,\n 'text-padding': 5,\n};\n\n/**\n * @ignore\n */\nexport const pinTextBasePaint: SymbolLayerSpecification['paint'] = {\n 'text-color': '#333333',\n 'text-halo-color': '#FFFFFF',\n 'text-halo-width': ['interpolate', ['linear'], ['zoom'], 6, 1, 10, 1.5],\n 'text-translate-anchor': 'viewport',\n};\n\n/**\n * Pin places, base layer with mostly the icon portion.\n * @ignore\n */\nexport const pinLayerBaseSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n layout: { ...pinIconBaseLayout, ...pinTextBaseLayout },\n paint: { ...pinIconBasePaint, ...pinTextBasePaint },\n};\n","/**\n * Returns a text with a number suffixed after a hyphen.\n * @ignore\n */\nexport const suffixNumber = (text: string, numberToSuffix: number): string => `${text}-${numberToSuffix}`;\n","import { suffixNumber } from '../../shared/layers/utils';\nimport { parseSvg } from '../../shared/resources';\nimport type { PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\n\n/**\n * Map of icon IDs to their text offset scale factors.\n * @ignore\n */\nexport type IconScalesMap = Map<string, { heightScale: number; widthScale: number }>;\n\n/**\n * Default pin dimensions (from base pin.svg)\n */\nconst DEFAULT_PIN_HEIGHT_PX = 140;\nconst DEFAULT_PIN_WIDTH_PX = 120;\n\n/**\n * Default base-map POI icon dimensions (from base-theme POI icons)\n */\nconst DEFAULT_MAP_POI_HEIGHT_PX = 54;\nconst DEFAULT_MAP_POI_WIDTH_PX = 54;\n\n/**\n * Extracts dimensions from an SVG string or HTMLImageElement.\n * For SVGs, parses the viewBox or width/height attributes.\n * For images, uses naturalWidth/naturalHeight.\n * @ignore\n */\nexport const extractImageDimensions = (image: string | HTMLImageElement): { width: number; height: number } | null => {\n try {\n if (typeof image === 'string') {\n if (image.includes('<svg')) {\n // Parse SVG to extract dimensions\n const svgElement = parseSvg(image);\n\n // Try viewBox first (format: \"minX minY width height\")\n const viewBox = svgElement.getAttribute('viewBox');\n if (viewBox) {\n const parts = viewBox.split(/\\s+/);\n if (parts.length === 4) {\n return {\n width: Number.parseFloat(parts[2]),\n height: Number.parseFloat(parts[3]),\n };\n }\n }\n\n // Fallback to width/height attributes\n const width = svgElement.getAttribute('width');\n const height = svgElement.getAttribute('height');\n if (width && height) {\n return {\n width: Number.parseFloat(width),\n height: Number.parseFloat(height),\n };\n }\n }\n // For URL strings, we can't extract dimensions synchronously\n return null;\n } else {\n // HTMLImageElement - check if loaded\n if (image.complete && image.naturalWidth > 0) {\n return {\n width: image.naturalWidth,\n height: image.naturalHeight,\n };\n }\n return null;\n }\n } catch (error) {\n console.warn('Failed to extract image dimensions:', error);\n return null;\n }\n};\n\n/**\n * Calculate the scale factors for a custom icon based on its dimensions relative to standard icon sizes.\n *\n * Calculates both height and width scales independently:\n * - Height scale is used for vertical text offset\n * - Width scale is used for horizontal text offset\n *\n *\n * @param image The image to extract dimensions from\n * @param theme The places theme ('base-map' for circles, 'pin' for pins)\n * @returns Object with heightScale and widthScale, or undefined if icon is standard-sized (within tolerance)\n * @ignore\n */\nexport const calculateIconScale = (\n image: string | HTMLImageElement | undefined,\n theme?: PlacesTheme,\n): { heightScale: number; widthScale: number } | undefined => {\n if (!image) {\n return undefined;\n }\n\n const dimensions = extractImageDimensions(image);\n if (!dimensions) {\n return undefined;\n }\n\n const isBaseMapTheme = theme === 'base-map';\n\n // For base-map theme, we need to scale relative to POI icons\n if (isBaseMapTheme) {\n const heightScale = dimensions.height / DEFAULT_MAP_POI_HEIGHT_PX;\n const widthScale = dimensions.width / DEFAULT_MAP_POI_WIDTH_PX;\n\n return { heightScale, widthScale };\n } else {\n // For pin theme, check if it's different from default pin\n const heightScale = dimensions.height / DEFAULT_PIN_HEIGHT_PX;\n const widthScale = dimensions.width / DEFAULT_PIN_WIDTH_PX;\n\n return { heightScale, widthScale };\n }\n};\n\n/**\n * Builds a map of icon IDs to their text offset scales for custom icons.\n * @ignore\n */\nexport const buildCustomIconScalesMap = (\n config: PlacesModuleConfig | undefined,\n instanceIndex: number,\n): IconScalesMap => {\n const iconTextOffsetScales = new Map<string, { heightScale: number; widthScale: number }>();\n const customIcons = config?.icon?.categoryIcons ?? [];\n\n for (const icon of customIcons) {\n if (icon.image) {\n const scales = calculateIconScale(icon.image, config?.theme);\n if (scales !== undefined) {\n // Base icon ID with instance suffix\n const suffixedIconId = suffixNumber(icon.id, instanceIndex);\n iconTextOffsetScales.set(suffixedIconId, scales);\n\n // If this icon has an availability level, also add the availability-suffixed version\n // (e.g., \"ELECTRIC_VEHICLE_STATION-available-0\")\n if (icon.availabilityLevel) {\n const availabilitySuffixedId = suffixNumber(`${icon.id}-${icon.availabilityLevel}`, instanceIndex);\n iconTextOffsetScales.set(availabilitySuffixedId, scales);\n }\n }\n }\n }\n\n return iconTextOffsetScales;\n};\n","import type { ChargingPark, ChargingParkWithAvailability, Place, POICategory } from '@tomtom-org/maps-sdk/core';\nimport type { ExpressionSpecification } from 'maplibre-gl';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport type { AvailabilityLevel } from '../../shared/types/image';\nimport type { EVAvailabilityConfig, PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\n\n/**\n * Type guard to check if a charging park has availability data.\n * @ignore\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n\n/**\n * Check if a place is an EV charging station with availability data.\n * @ignore\n */\nexport const isEVStationWithAvailability = (place: Place): boolean => {\n const category = place.properties.poi?.classifications?.[0]?.code;\n return category === 'ELECTRIC_VEHICLE_STATION' && hasChargingAvailability(place.properties.chargingPark);\n};\n\n/**\n * Calculate charging point availability information.\n * @ignore\n */\nexport const getChargingPointAvailability = (\n place: Place,\n): { availableCount: number; totalCount: number; ratio: number } | undefined => {\n const chargingPark = place.properties.chargingPark;\n if (hasChargingAvailability(chargingPark)) {\n const availability = chargingPark.availability.chargingPointAvailability;\n const available = availability.statusCounts.Available ?? 0;\n return {\n availableCount: available,\n totalCount: availability.count,\n ratio: available / availability.count,\n };\n }\n return undefined;\n};\n\n/**\n * Default threshold for EV availability - available vs occupied.\n * @ignore\n */\nexport const DEFAULT_EV_AVAILABILITY_THRESHOLD = 0;\n\n/**\n * Default formatter for EV availability text.\n * @ignore\n */\nexport const defaultFormatAvailabilityText = (available: number, total: number): string => `${available}/${total}`;\n\n/**\n * Build availability text for a place with EV availability data.\n * @ignore\n */\nexport const buildAvailabilityText = (place: Place, config?: EVAvailabilityConfig): string => {\n const availability = getChargingPointAvailability(place);\n if (!availability) {\n return '';\n }\n\n const formatFn = config?.formatText ?? defaultFormatAvailabilityText;\n return formatFn(availability.availableCount, availability.totalCount);\n};\n\n/**\n * Build availability ratio for a place with EV availability data.\n * @ignore\n */\nexport const getAvailabilityRatio = (place: Place): number => {\n const availability = getChargingPointAvailability(place);\n return availability?.ratio ?? 0;\n};\n\n/**\n * Get the color expression for EV availability based on ratio.\n * @ignore\n */\nexport const getAvailabilityColorExpression = (config?: EVAvailabilityConfig): ExpressionSpecification => {\n const threshold = config?.threshold ?? DEFAULT_EV_AVAILABILITY_THRESHOLD;\n\n return ['case', ['>=', ['get', 'evAvailabilityRatio'], threshold], 'green', 'red'] as ExpressionSpecification;\n};\n\n/**\n * Handles icon selection for EV stations with availability data.\n * Returns icon ID if availability-specific icon should be used, or undefined to fall through to regular selection.\n * @ignore\n */\nexport const getEVAvailabilityIconID = (\n place: Place,\n poiCategory: POICategory,\n instanceIndex: number,\n config: PlacesModuleConfig,\n iconTheme: PlacesTheme,\n): string | undefined => {\n if (!config.evAvailability?.enabled || !isEVStationWithAvailability(place)) {\n return undefined;\n }\n\n const ratio = getAvailabilityRatio(place);\n const threshold = config.evAvailability.threshold ?? 0.3;\n const requiredLevel: AvailabilityLevel = ratio >= threshold ? 'available' : 'occupied';\n const hasCustomIcons = config.icon?.categoryIcons && config.icon.categoryIcons.length > 0;\n\n const customIconWithAvailability = config.icon?.categoryIcons?.find(\n (customIcon) => customIcon.id === poiCategory && customIcon.availabilityLevel === requiredLevel,\n );\n\n // If a custom icon with the required availability level exists, use it\n if (customIconWithAvailability) {\n return suffixNumber(`${customIconWithAvailability.id}-${requiredLevel}`, instanceIndex);\n }\n\n // For pin theme: use CDN availability sprites when no custom icons are defined\n if (!hasCustomIcons && iconTheme === 'pin') {\n return `7309-${requiredLevel}`;\n }\n\n // Otherwise, fall through to regular icon selection\n return undefined;\n};\n","import type {\n DataDrivenPropertyValueSpecification,\n ExpressionSpecification,\n SymbolLayerSpecification,\n} from 'maplibre-gl';\nimport { DEFAULT_MAX_PIN_SCALE } from '../../shared/layers/commonLayerProps';\nimport { DEFAULT_TEXT_OFFSET_X, DEFAULT_TEXT_OFFSET_Y, ICON_ID } from '../../shared/layers/symbolLayers';\nimport type { PlacesTheme } from '../types/placesModuleConfig';\n\ntype VariableAnchorOffset = NonNullable<SymbolLayerSpecification['layout']>['text-variable-anchor-offset'];\n\n/**\n * Extracts the maximum icon scale factor from an icon-size expression.\n * For interpolate expressions, returns the last value (max zoom scale).\n * @ignore\n */\nconst extractMaxIconScale = (expression: DataDrivenPropertyValueSpecification<number> | undefined): number => {\n if (!expression) {\n return DEFAULT_MAX_PIN_SCALE;\n }\n\n if (typeof expression === 'number') {\n return expression;\n }\n\n if (Array.isArray(expression)) {\n const lastValue = expression.at(-1);\n if (typeof lastValue === 'number') {\n return lastValue;\n }\n }\n\n return DEFAULT_MAX_PIN_SCALE;\n};\n\n/**\n * Layout properties for text offset - can be spread directly into layer layout.\n */\ntype TextOffsetLayout = {\n 'text-offset'?: [number, number];\n 'text-variable-anchor-offset'?: VariableAnchorOffset;\n};\n\n/**\n * Builds anchor offsets for variable-anchor-offset property.\n * @ignore\n */\nconst buildAnchorOffsets = (\n topOffset: number,\n sideOffset: number,\n pinVerticalAdjustment: number,\n customTextOffset?: number,\n): { top: [number, number]; left: [number, number]; right: [number, number] } => {\n const hasCustomOffset = customTextOffset !== undefined;\n // For pin theme with custom offset, override only the primary direction for each anchor\n // top anchor → custom offset applies to vertical, left/right → custom offset applies to horizontal\n return {\n top: hasCustomOffset ? [0, customTextOffset] : [0, topOffset],\n left: hasCustomOffset ? [customTextOffset, pinVerticalAdjustment] : [sideOffset, pinVerticalAdjustment],\n right: hasCustomOffset ? [-customTextOffset, pinVerticalAdjustment] : [-sideOffset, pinVerticalAdjustment],\n };\n};\n\n/**\n * Calculates text offset for place labels\n *\n * @param iconSizeExpression The icon-size property from the layer specification\n * @param iconTextOffsetScales Map of icon IDs to their scale factors (heightScale for vertical, widthScale for horizontal)\n * @param theme The places theme ('base-map' for circles, 'pin' for pins)\n * @param customTextOffset Custom text offset multiplier (overrides default TEXT_OFFSET constants)\n * @returns Configuration object with the MapLibre property type and value to apply\n * @ignore\n */\nexport const getTextOffset = (\n iconSizeExpression: DataDrivenPropertyValueSpecification<number> | undefined,\n iconTextOffsetScales: Map<string, { heightScale: number; widthScale: number }>,\n theme?: PlacesTheme,\n customTextOffset?: number,\n): TextOffsetLayout => {\n const maxIconScale = extractMaxIconScale(iconSizeExpression);\n const iconScaleMultiplier = maxIconScale / DEFAULT_MAX_PIN_SCALE;\n const isBaseMapTheme = theme === 'base-map';\n const hasCustomOffset = customTextOffset !== undefined;\n\n // For base-map theme with custom offset, use simple text-offset (circles are centered)\n if (isBaseMapTheme && hasCustomOffset) {\n return {\n 'text-offset': [customTextOffset, customTextOffset],\n };\n }\n\n // Calculate fallback offsets (used when no custom icons or as default case)\n const fallbackTopOffset = DEFAULT_TEXT_OFFSET_Y * iconScaleMultiplier;\n const fallbackSideOffset = DEFAULT_TEXT_OFFSET_X * iconScaleMultiplier;\n const fallbackPinVerticalAdjustment = isBaseMapTheme ? 0 : -fallbackSideOffset;\n const fallbackOffsets = buildAnchorOffsets(\n fallbackTopOffset,\n fallbackSideOffset,\n fallbackPinVerticalAdjustment,\n customTextOffset,\n );\n const fallbackAnchorOffset = [\n 'top',\n fallbackOffsets.top,\n 'left',\n fallbackOffsets.left,\n 'right',\n fallbackOffsets.right,\n ];\n\n // No custom icons - return literal value directly (no case expression needed)\n if (iconTextOffsetScales.size === 0) {\n return {\n 'text-variable-anchor-offset': fallbackAnchorOffset as VariableAnchorOffset,\n };\n }\n\n // Build case expression for custom icons\n const offsetCaseExpression: (string | number | ExpressionSpecification)[] = ['case'];\n\n for (const [iconId, scales] of iconTextOffsetScales.entries()) {\n // Base-map POI layer uses larger vertical offsets than pin theme to match native map styling\n const baseTopOffset = isBaseMapTheme ? DEFAULT_TEXT_OFFSET_Y * 2 : DEFAULT_TEXT_OFFSET_Y;\n const topOffset = baseTopOffset * scales.heightScale;\n const sideOffset = DEFAULT_TEXT_OFFSET_X * scales.widthScale;\n\n // Base-map theme uses circles (centered) → no vertical adjustment for side anchors\n // Pin theme uses pins (bottom-anchored) → shift labels upward to align with visual center\n const pinVerticalAdjustment = isBaseMapTheme ? 0 : -sideOffset;\n\n const offsets = buildAnchorOffsets(topOffset, sideOffset, pinVerticalAdjustment, customTextOffset);\n offsetCaseExpression.push(\n ['==', ['get', ICON_ID], iconId],\n ['literal', ['top', offsets.top, 'left', offsets.left, 'right', offsets.right]],\n );\n }\n\n // Add fallback for icons not in the custom scales map\n offsetCaseExpression.push(['literal', fallbackAnchorOffset]);\n\n return {\n 'text-variable-anchor-offset': offsetCaseExpression as VariableAnchorOffset,\n };\n};\n","import type { DataDrivenPropertyValueSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { TITLE } from '../../shared/layers/symbolLayers';\nimport type { LightDark } from '../../shared/types/style';\nimport type { PlaceLayerName, PlacesModuleConfig } from '../types/placesModuleConfig';\nimport type { IconScalesMap } from './customIconScales';\nimport { getAvailabilityColorExpression } from './evAvailabilityHelpers';\nimport { getTextOffset } from './textOffsetCalculator';\nimport { getThemeAdaptiveTextColors } from './themeAdaptation';\n\n/**\n * Builds the text field expression for place labels\n * Supports EV availability text when enabled.\n * @ignore\n */\nexport const buildTextFieldExpression = (\n config: PlacesModuleConfig | undefined,\n evAvailabilityEnabled: boolean,\n): DataDrivenPropertyValueSpecification<string> => {\n if (!evAvailabilityEnabled) {\n return ['get', TITLE];\n }\n\n return [\n 'case',\n ['has', 'evAvailabilityText'],\n // If has EV availability, show two-line format with colored availability\n [\n 'format',\n ['get', TITLE],\n {},\n '\\n',\n {},\n ['get', 'evAvailabilityText'],\n {\n 'font-scale': 1.1,\n 'text-color': getAvailabilityColorExpression(config?.evAvailability),\n },\n ],\n // Otherwise, show normal title\n ['get', TITLE],\n ];\n};\n\n/**\n * Builds the layout configuration\n * @ignore\n */\nexport const buildLayoutConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n textField: DataDrivenPropertyValueSpecification<string>,\n iconTextOffsetScales?: IconScalesMap,\n): SymbolLayerSpecification['layout'] => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const hasCustomIcons = iconTextOffsetScales && iconTextOffsetScales.size > 0;\n\n // Start with base layout\n const baseLayout = { ...layerSpec.layout };\n\n // Remove offset properties we'll replace\n if (hasCustomIcons || textConfig?.offset !== undefined) {\n delete baseLayout['text-offset'];\n delete baseLayout['text-variable-anchor-offset'];\n delete baseLayout['text-radial-offset'];\n }\n\n const layout = {\n ...baseLayout,\n ...customLayer?.layout,\n ...(textConfig?.size && { 'text-size': textConfig.size }),\n ...(textConfig?.font && { 'text-font': textConfig.font }),\n 'text-field': textField,\n };\n\n // Apply offset configuration\n if (hasCustomIcons || textConfig?.offset !== undefined) {\n // Dynamic offset calculation handles custom icons and/or custom offset\n // For pin theme, this ensures proper vertical adjustments for left/right anchors\n const iconSize = layerSpec.layout?.['icon-size'];\n const scales = iconTextOffsetScales ?? new Map();\n Object.assign(layout, getTextOffset(iconSize, scales, config?.theme, textConfig?.offset));\n }\n\n return layout;\n};\n\n/**\n * Builds the paint configuration with theme-adaptive colors.\n * @ignore\n */\nexport const buildPaintConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n lightDark: LightDark,\n): SymbolLayerSpecification['paint'] => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const { textColor: baseTextColor, haloColor: baseHaloColor } = getThemeAdaptiveTextColors(lightDark);\n return {\n ...layerSpec.paint,\n // Apply theme-adaptive colors as defaults\n ...(!textConfig?.color && { 'text-color': baseTextColor }),\n ...(!textConfig?.haloColor && { 'text-halo-color': baseHaloColor }),\n // User config takes precedence\n ...(textConfig?.color && { 'text-color': textConfig.color }),\n ...(textConfig?.haloColor && { 'text-halo-color': textConfig.haloColor }),\n ...(textConfig?.haloWidth && { 'text-halo-width': textConfig.haloWidth }),\n ...customLayer?.paint,\n };\n};\n","import { LightDark } from '../../shared/types/style';\n/**\n * Calculates theme-adaptive text colors based on light/dark mode.\n * @param lightDark Whether the current theme is light or dark\n * @returns Object with text and halo colors appropriate for the theme\n * @ignore\n *\n * TODO: Should these colors adapt to the POI context colors,\n * or should they remain as fixed defaults?\n */\nexport const getThemeAdaptiveTextColors = (\n lightDark: LightDark,\n): {\n textColor: string;\n haloColor: string;\n} => {\n return {\n textColor: lightDark === 'dark' ? '#FFFFFF' : '#333333',\n haloColor: lightDark === 'dark' ? '#333333' : '#FFFFFF',\n };\n};\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * @ignore\n */\nexport const isClickEventState: ExpressionSpecification = [\n 'in',\n ['get', 'eventState'],\n ['literal', ['click', 'contextmenu']],\n];\n","import type { DataDrivenPropertyValueSpecification, Map as MapLibreMap, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { isClickEventState } from '../../shared/layers/eventState';\nimport { ICON_ID, TITLE } from '../../shared/layers/symbolLayers';\n\n/**\n * Replaces placeholders in text size spec with the actual title property.\n * @ignore\n */\nexport const getTextSizeSpec = (\n textSize?: DataDrivenPropertyValueSpecification<number>,\n): DataDrivenPropertyValueSpecification<number> => {\n return JSON.parse(JSON.stringify(textSize)?.replaceAll('name', TITLE));\n};\n\n/**\n * Builds a POI-like layer spec that matches the base map style.\n * @ignore\n */\nexport const buildPoiLikeLayerSpec = (map: MapLibreMap): LayerSpecTemplate<SymbolLayerSpecification> => {\n const poiLayer = (map.getStyle().layers.find((layer) => layer.id === 'POI') as SymbolLayerSpecification) || {};\n const textSize = poiLayer.layout?.['text-size'];\n return {\n filter: ['!', isClickEventState],\n type: 'symbol',\n paint: poiLayer.paint,\n layout: {\n ...poiLayer.layout,\n 'text-field': ['get', TITLE],\n 'icon-image': ['get', ICON_ID],\n ...(textSize && { 'text-size': getTextSizeSpec(textSize) }),\n },\n };\n};\n","import type { ExpressionSpecification, Map as MapLibreMap, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate, LightDark } from '../../shared';\nimport { SELECTED_PIN_ICON_SIZE } from '../../shared/layers/commonLayerProps';\nimport { pinLayerBaseSpec } from '../../shared/layers/symbolLayers';\nimport type { PlaceLayerName, PlaceLayersConfig, PlacesModuleConfig } from '../types/placesModuleConfig';\nimport { buildCustomIconScalesMap, type IconScalesMap } from '../utils/customIconScales';\nimport { buildLayoutConfig, buildPaintConfig, buildTextFieldExpression } from '../utils/layerConfiguration';\nimport { buildPoiLikeLayerSpec } from '../utils/layerSpecBuilders';\n\n/**\n * @ignore\n */\nexport const hasEventState: ExpressionSpecification = ['has', 'eventState'];\n\n/**\n * @ignore\n */\nexport const SELECTED_COLOR = '#3f9cd9';\n\n/**\n * @ignore\n */\nexport const pinLayerSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...pinLayerBaseSpec,\n filter: ['!', hasEventState],\n};\n\n/**\n * We use an extra layer for highlighted text since it's not easy to enforce z-ordering with icons and text\n * while text has different collision rules.\n * @ignore\n */\nexport const selectedPinLayerSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...pinLayerBaseSpec,\n filter: hasEventState,\n layout: {\n ...pinLayerBaseSpec.layout,\n 'icon-size': SELECTED_PIN_ICON_SIZE,\n 'text-allow-overlap': true,\n },\n paint: {\n ...pinLayerBaseSpec.paint,\n 'text-color': SELECTED_COLOR,\n },\n};\n\n/**\n * Applies configuration to a layer specification template.\n * @ignore\n */\nconst withConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n lightDark: LightDark,\n iconTextOffsetScales?: IconScalesMap,\n): LayerSpecTemplate<SymbolLayerSpecification> => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const evAvailabilityEnabled = config?.evAvailability?.enabled === true;\n\n const textFieldExpression = buildTextFieldExpression(config, evAvailabilityEnabled);\n const textField =\n textConfig?.title && typeof textConfig?.title !== 'function' ? textConfig.title : textFieldExpression;\n\n return {\n ...layerSpec,\n layout: buildLayoutConfig(layerSpec, config, layerName, textField, iconTextOffsetScales),\n paint: buildPaintConfig(layerSpec, config, layerName, lightDark),\n ...customLayer,\n };\n};\n\n/**\n * Builds layer specifications for places display.\n * @ignore\n */\nexport const buildPlacesLayerSpecs = (\n config: PlacesModuleConfig | undefined,\n mapLibreMap: MapLibreMap,\n styleLightDarkTheme: LightDark,\n instanceIndex: number,\n): PlaceLayersConfig => {\n const iconTextOffsetScales = buildCustomIconScalesMap(config, instanceIndex);\n\n let main: LayerSpecTemplate<SymbolLayerSpecification>;\n let selected: LayerSpecTemplate<SymbolLayerSpecification>;\n\n if (config?.theme === 'base-map') {\n const poiLikeLayerSpec = buildPoiLikeLayerSpec(mapLibreMap);\n main = poiLikeLayerSpec;\n selected = {\n ...poiLikeLayerSpec,\n filter: hasEventState,\n layout: {\n ...poiLikeLayerSpec.layout,\n 'text-allow-overlap': true,\n },\n paint: {\n ...poiLikeLayerSpec.paint,\n 'text-color': SELECTED_COLOR,\n },\n };\n } else {\n // pin / circle\n main = pinLayerSpec;\n selected = selectedPinLayerSpec;\n }\n\n return {\n main: withConfig(main, config, 'main', styleLightDarkTheme, iconTextOffsetScales),\n selected: withConfig(selected, config, 'selected', styleLightDarkTheme, iconTextOffsetScales),\n ...config?.layers?.additional,\n };\n};\n","import { SVGIconStyleOptions } from '../../shared';\nimport { isDOMImageSupported, svgToImg } from '../../shared/imageUtils';\nimport { pinSvg } from '../../shared/resources';\n\n/**\n * Default pin for selected images without a specific category on it.\n * @ignore\n */\nexport const defaultPin = (options?: SVGIconStyleOptions): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return svgToImg(pinSvg(options));\n};\n","// Supported sub-categories for map display pins\n// See: https://github.com/tomtom-internal/mdt-backend-mapbox-gl-js-styles/blob/orbis-preview/src/orbis/sprites/poi_light/config.json\n// For the rest we'll use the main categories (which are the first 4 digits of a category)\nconst supportedPinSubcategories: Set<number> = new Set([\n 7339002, 8099002, 7369004, 7321003, 7376006, 9362003, 9362004, 9160004, 9376007, 7315015, 9376006, 9376002, 7315078,\n 7315149, 9376005, 7372003, 9352045, 9352032, 9361048, 9902003, 9378005, 7383004, 9910004, 7320002, 9362016, 7320003,\n 9362025, 9942002, 9942003, 7380005, 9663003, 9361021, 9379009, 9379004, 7315147, 9376003, 9160002, 9160003, 9352008,\n 9361006, 7389004, 9910006,\n]);\n\n/**\n * @ignore\n */\nexport const toPinImageID = (categoryID: number | undefined): string | undefined => {\n if (!categoryID) {\n return undefined;\n }\n\n // Check if the category ID is in our supported subcategories:\n if (supportedPinSubcategories.has(categoryID)) {\n return categoryID.toString();\n }\n\n // If not, fall back to the main category (first 4 digits of the category ID):\n return categoryID.toString().substring(0, 4);\n};\n","import { generateId, Place, Places, POICategory, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { toBaseMapPOICategory } from '../../pois/poiCategoryMapping';\nimport { DEFAULT_PLACE_ICON_ID } from '../../shared/layers/symbolLayers';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport type { DisplayPlaceProps } from '../types/placeDisplayProps';\nimport type { PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\nimport {\n buildAvailabilityText,\n getAvailabilityRatio,\n getEVAvailabilityIconID,\n isEVStationWithAvailability,\n} from './evAvailabilityHelpers';\nimport { toPinImageID } from './toPinImageID';\n\n/**\n * Builds the title of the place to display it on the map.\n * @param place The place to display.\n * @ignore\n */\nexport const buildPlaceTitle = (place: Place): string =>\n place.properties.poi?.name ?? place.properties.address.freeformAddress;\n\n/**\n * Resolves the image ID to use for the given POI category and icon theme.\n */\nconst toImageID = (poiCategory: POICategory, iconTheme: PlacesTheme, defaultPlaceIconID: string): string => {\n if (iconTheme === 'pin') {\n const imageID = toPinImageID(poiCategoriesToID[poiCategory]);\n return imageID ?? defaultPlaceIconID;\n } else {\n const imageID = toBaseMapPOICategory(poiCategory);\n return imageID ? `poi-${imageID}` : defaultPlaceIconID;\n }\n};\n\n/**\n * Gets the map style sprite image ID to display on the map for the give place.\n * @ignore\n */\nexport const getIconIDForPlace = (place: Place, instanceIndex: number, config: PlacesModuleConfig = {}): string => {\n const iconTheme = config.theme ?? 'pin';\n const defaultPlaceIconID = suffixNumber(DEFAULT_PLACE_ICON_ID, instanceIndex);\n\n const imageMapping = config.icon?.mapping;\n // First, try custom mapping if provided:\n if (imageMapping) {\n if (imageMapping.to === 'imageID') {\n // Direct image ID mapping\n return imageMapping.fn(place);\n } else {\n // POI category mapping - resolve category to icon ID\n return toImageID(imageMapping.fn(place), iconTheme, defaultPlaceIconID);\n }\n }\n\n // Next, try to match any custom icon:\n const poiCategory = place.properties.poi?.classifications?.[0]?.code as POICategory;\n\n // Check for EV availability-specific icon selection\n const evAvailabilityIconID = getEVAvailabilityIconID(place, poiCategory, instanceIndex, config, iconTheme);\n if (evAvailabilityIconID) {\n return evAvailabilityIconID;\n }\n\n // Regular custom icon matching (no availability)\n const matchingCustomIcon = config.icon?.categoryIcons?.find((customIcon) => customIcon.id === poiCategory);\n if (matchingCustomIcon) {\n return suffixNumber(matchingCustomIcon.id, instanceIndex);\n }\n\n // Else: if no custom icon matched, we map to the map style icons or default:\n const baseIconID = toImageID(poiCategory, iconTheme, defaultPlaceIconID);\n return baseIconID;\n};\n\n/**\n * Maps a Place category to the poi layer one, so the latter's style can apply it.\n * @ignore\n */\nexport const getPOILayerCategoryForPlace = (place: Place): string | undefined => {\n const category = place.properties.poi?.classifications?.[0]?.code;\n // if it's one of the different categories between search and poi layer, use poi layer category\n return category && toBaseMapPOICategory(category);\n};\n\n/**\n * Transforms the input of a \"show\" call to FeatureCollection \"Places\".\n * @ignore\n */\nexport const toPlaces = (places: Place | Place[] | Places): Places => {\n if (Array.isArray(places)) {\n return { type: 'FeatureCollection', features: places };\n }\n return places.type === 'Feature' ? { type: 'FeatureCollection', features: [places] } : places;\n};\n\n/**\n * Merges EV availability props into extraFeatureProps if enabled.\n * This makes EV stations use the same mechanism as any other custom properties.\n * @ignore\n */\nconst mergeEVAvailabilityProps = (\n extraFeatureProps: PlacesModuleConfig['extraFeatureProps'],\n evAvailabilityConfig: PlacesModuleConfig['evAvailability'],\n places: Places,\n): PlacesModuleConfig['extraFeatureProps'] => {\n if (evAvailabilityConfig?.enabled !== true) {\n return extraFeatureProps;\n }\n\n // Check if any EV stations exist but lack availability data\n let hasEVStations = false;\n let hasEVStationsWithAvailability = false;\n\n for (const place of places.features) {\n const isEVStation = place.properties.poi?.classifications?.[0]?.code === 'ELECTRIC_VEHICLE_STATION';\n if (isEVStation) {\n hasEVStations = true;\n if (isEVStationWithAvailability(place)) {\n hasEVStationsWithAvailability = true;\n break;\n }\n }\n }\n\n if (hasEVStations && !hasEVStationsWithAvailability) {\n console.warn(\n 'PlacesModule: evAvailability is enabled but no availability data found. ' +\n 'Did you call getPlacesWithEVAvailability()?',\n );\n }\n\n return {\n ...extraFeatureProps,\n evAvailabilityText: (place: Place) =>\n isEVStationWithAvailability(place) ? buildAvailabilityText(place, evAvailabilityConfig) : '',\n evAvailabilityRatio: (place: Place) => (isEVStationWithAvailability(place) ? getAvailabilityRatio(place) : 0),\n };\n};\n\n/**\n * prepare places features to be displayed on map by adding needed properties for title, icon and style\n * @ignore\n */\nexport const preparePlacesForDisplay = (\n placesInput: Place | Place[] | Places,\n instanceIndex: number,\n config: PlacesModuleConfig = {},\n): Places<DisplayPlaceProps> => {\n const places = toPlaces(placesInput);\n\n // Only merge EV availability props when explicitly enabled\n const mergedExtraFeatureProps =\n config.evAvailability?.enabled === true\n ? mergeEVAvailabilityProps(config.extraFeatureProps, config.evAvailability, places)\n : config.extraFeatureProps;\n\n return {\n ...places,\n features: places.features.map((place) => {\n const title =\n typeof config?.text?.title === 'function' ? config?.text?.title(place) : buildPlaceTitle(place);\n\n const extraFeatureProps = mergedExtraFeatureProps\n ? Object.fromEntries(\n Object.entries(mergedExtraFeatureProps).map(([prop, value]) => [\n prop,\n typeof value === 'function' ? value(place) : value,\n ]),\n )\n : {};\n\n const id = place.id ?? generateId();\n\n return {\n ...place,\n id,\n geometry: { ...place.geometry, bbox: place.bbox },\n properties: {\n ...place.properties,\n id, // we need id in properties due to promoteId feature\n title,\n iconID: getIconIDForPlace(place, instanceIndex, config),\n ...(config?.theme === 'base-map' && { category: getPOILayerCategoryForPlace(place) }),\n ...extraFeatureProps,\n },\n };\n }),\n };\n};\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport type {\n CleanEventStateOptions,\n CleanEventStatesOptions,\n PutEventStateOptions,\n SymbolLayerSpecWithoutSource,\n} from '../shared';\nimport { AbstractMapModule, EventsModule, GeoJSONSourceWithLayers } from '../shared';\nimport { DEFAULT_PLACE_ICON_ID } from '../shared/layers/symbolLayers';\nimport { suffixNumber } from '../shared/layers/utils';\nimport { addOrUpdateImage, changeLayersProps, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { buildPlacesLayerSpecs } from './layers/placesLayers';\nimport { defaultPin } from './resources';\nimport type { DisplayPlaceProps } from './types/placeDisplayProps';\nimport {\n PlaceIconConfig,\n PlaceLayerName,\n PlacesModuleConfig,\n PlacesTheme,\n PlaceTextConfig,\n} from './types/placesModuleConfig';\nimport { preparePlacesForDisplay } from './utils/preparePlacesForDisplay';\n\ntype PlacesSourcesAndLayers = {\n /**\n * Places source id with corresponding layers ids.\n */\n places: GeoJSONSourceWithLayers<Places<DisplayPlaceProps>>;\n};\n\n/**\n * Map module for displaying and managing place markers.\n *\n * The PlacesModule provides functionality to display location markers (pins) on the map\n * for points of interest, search results, or custom locations. It supports various marker\n * styles, custom icons, text labels, and interactive events.\n *\n * @remarks\n * **Features:**\n * - Multiple marker styles (pin, circle, POI-like)\n * - Custom icons per POI category\n * - Text labels with styling options\n * - Data-driven styling via MapLibre expressions\n * - Interactive events (click, hover, etc.)\n * - Support for custom feature properties\n * - EV charging station availability display (opt-in)\n *\n * **Marker Styles:**\n * - `pin`: Traditional teardrop-shaped map pins\n * - `circle`: Simple circular markers\n * - `base-map`: Mimics built-in POI layer styling\n *\n * **EV Charging Station Availability:**\n * When displaying EV charging stations with availability data from\n * {@link getPlacesWithEVAvailability}, the module can:\n * - Show available/total charging points (e.g., \"3/10\")\n * - Color-code availability (green = good, orange = limited, red = none/low)\n * - Display as formatted text within the station's label\n *\n * This feature is disabled by default. To enable it, set `evAvailability.enabled` to `true`\n * in the configuration.\n *\n * **Common Use Cases:**\n * - Search result visualization\n * - Custom location markers\n * - Store locators\n * - EV charging station maps with real-time availability\n * - Delivery/pickup points\n * - Saved locations display\n *\n * @example\n * ```typescript\n * // Create places module with pin markers\n * const placesModule = await PlacesModule.get(map, {\n * icon: {\n * categoryIcons: []\n * },\n * text: {\n * field: (place) => place.properties.poi?.name || 'Unknown'\n * },\n * theme: 'pin'\n * });\n *\n * // Display places from search\n * await placesModule.show(searchResults);\n *\n * // EV Charging Stations - Opt-in to availability display\n * const evStations = await PlacesModule.get(map, {\n * evAvailability: { enabled: true }\n * });\n * const results = await search({ poiCategories: ['ELECTRIC_VEHICLE_STATION'] });\n * evStations.show(await getPlacesWithEVAvailability(results)); // Shows availability\n *\n * // Granular control: Enable for searched stations only, background stations without\n * const bgStations = await PlacesModule.get(map); // EV availability disabled\n * const searched = await PlacesModule.get(map, {\n * evAvailability: { enabled: true }\n * });\n *\n * // Handle clicks\n * placesModule.events.on('click', (feature) => {\n * console.log('Clicked:', feature.properties);\n * });\n *\n * placesModule.events.on('hover', (feature) => {\n * showTooltip(feature.properties.poi?.name);\n * });\n * ```\n *\n * @see [Places Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/places)\n *\n * @group Places\n */\nexport class PlacesModule extends AbstractMapModule<PlacesSourcesAndLayers, PlacesModuleConfig> {\n private static lastInstanceIndex = -1;\n private layerSpecs!: Record<PlaceLayerName, SymbolLayerSpecWithoutSource>;\n private sourceID!: string;\n private layerIDPrefix!: string;\n /**\n * The index of this instance, to generate unique source and layer IDs.\n * * Starts with 0 and each instance increments it by one.\n * @private\n */\n private instanceIndex!: number;\n private defaultPlaceIconID!: string;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n */\n static async get(tomtomMap: TomTomMap, config?: PlacesModuleConfig): Promise<PlacesModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new PlacesModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: PlacesModuleConfig) {\n super('geojson', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: PlacesModuleConfig, restore?: boolean): PlacesSourcesAndLayers {\n // Only increment the instance index for new instances, not for restore operations\n if (!restore) {\n PlacesModule.lastInstanceIndex++;\n this.instanceIndex = PlacesModule.lastInstanceIndex;\n this.sourceID = `places-${this.instanceIndex}`;\n this.layerIDPrefix = this.sourceID;\n this.defaultPlaceIconID = suffixNumber(DEFAULT_PLACE_ICON_ID, this.instanceIndex);\n }\n\n // Update each layer id with the instance-specific prefix\n this.layerSpecs = this.buildLayerSpecs(config);\n\n return {\n places: new GeoJSONSourceWithLayers(this.mapLibreMap, this.sourceID, [\n this.layerSpecs.main,\n this.layerSpecs.selected,\n ]),\n };\n }\n\n private buildLayerSpecs(config?: PlacesModuleConfig) {\n const layerSpecTemplates = buildPlacesLayerSpecs(\n config,\n this.tomtomMap.mapLibreMap,\n this.tomtomMap.styleLightDarkTheme,\n this.instanceIndex,\n );\n\n // Update each layer id with the instance-specific prefix\n return Object.fromEntries(\n Object.entries(layerSpecTemplates).map(([key, spec]) => [\n key,\n { ...spec, id: `${this.layerIDPrefix}-${key}` },\n ]),\n ) as Record<PlaceLayerName, SymbolLayerSpecWithoutSource>;\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: PlacesModuleConfig | undefined) {\n this.updateLayersAndData(config);\n return config;\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previousShownFeatures = this.sourcesWithLayers.places.shownFeatures;\n this.initSourcesWithLayers(this.config, true);\n this.config && this._applyConfig(this.config);\n this.show(previousShownFeatures);\n }\n\n /**\n * Updates the visual theme for displayed places.\n *\n * @param theme - The theme style to apply to place markers.\n *\n * @remarks\n * **Available Themes:**\n * - `pin`: Traditional teardrop-shaped map pins\n * - `circle`: Simple circular markers\n * - `base-map`: Mimics the map's built-in POI layer style with category icons\n *\n * Changes apply immediately to all currently shown places. Other configuration\n * properties (icon config, text config) remain unchanged.\n *\n * @example\n * ```typescript\n * // Switch to pin markers\n * placesModule.applyTheme('pin');\n *\n * // Use simple circles\n * placesModule.applyTheme('circle');\n *\n * // Match map's POI style (ideal to blend in)\n * placesModule.applyTheme('base-map');\n * ```\n */\n applyTheme(theme: PlacesTheme): void {\n this.applyConfigPart({ theme });\n }\n\n /**\n * Updates the icon configuration for displayed places.\n *\n * @param iconConfig - New icon configuration settings.\n *\n * @remarks\n * - Changes apply immediately to currently shown places\n * - Custom icons are loaded if not already in style\n * - Other configuration properties remain unchanged\n *\n * @example\n * ```typescript\n * placesModule.applyIconConfig({\n * categoryIcons: [\n * { category: 'RESTAURANT', id: 'restaurant-icon', image: '/icons/food.png' }\n * ]\n * });\n * ```\n */\n applyIconConfig(iconConfig: PlaceIconConfig): void {\n this.applyConfigPart({ icon: iconConfig });\n }\n\n /**\n * Updates the text/label configuration for displayed places.\n *\n * @param textConfig - New text configuration settings.\n *\n * @remarks\n * Supports both functions and MapLibre expressions for dynamic text.\n *\n * @example\n * ```typescript\n * // Use function\n * placesModule.applyTextConfig({\n * field: (place) => place.properties.poi?.name || 'Unknown'\n * });\n *\n * // Use MapLibre expression\n * placesModule.applyTextConfig({\n * field: ['get', 'title'],\n * size: 14,\n * color: '#333'\n * });\n * ```\n */\n applyTextConfig(textConfig: PlaceTextConfig): void {\n this.applyConfigPart({ text: textConfig });\n }\n\n private applyConfigPart(partialConfig: Partial<PlacesModuleConfig>): void {\n const config = { ...this.config, ...partialConfig };\n this.updateLayersAndData(config);\n this.config = config;\n }\n\n /**\n * Applies additional feature properties to displayed places.\n *\n * @param extraFeatureProps - Object mapping property names to values or functions.\n *\n * @remarks\n * Useful for adding computed properties or metadata for styling/filtering.\n *\n * @example\n * ```typescript\n * placesModule.applyExtraFeatureProps({\n * category: (place) => place.properties.poi?.categories?.[0],\n * rating: (place) => place.properties.poi?.rating || 0,\n * isOpen: true\n * });\n * ```\n */\n applyExtraFeatureProps(extraFeatureProps: { [key: string]: any }): void {\n const config = { ...this.config, extraFeatureProps };\n this.updateData(config);\n this.config = config;\n }\n\n private updateLayersAndData(config: PlacesModuleConfig | undefined): void {\n this.setupImages(config);\n const newLayerSpecs = this.buildLayerSpecs(config);\n // Convert layerSpecs objects to arrays for changeLayersProps\n const newLayerSpecsArray = [newLayerSpecs.main, newLayerSpecs.selected];\n const oldLayerSpecsArray = [this.layerSpecs.main, this.layerSpecs.selected];\n changeLayersProps(newLayerSpecsArray, oldLayerSpecsArray, this.mapLibreMap);\n this.layerSpecs = newLayerSpecs;\n this.updateData(config);\n }\n\n private setupImages(config: PlacesModuleConfig | undefined): void {\n // Ensure default pin is added:\n if (config?.icon) {\n // If we have custom icons, ensure they're added to the map style:\n for (const customIcon of config.icon.categoryIcons ?? []) {\n // Create unique ID for each custom icon, including availability level if present\n const iconID = customIcon.availabilityLevel\n ? `${customIcon.id}-${customIcon.availabilityLevel}`\n : customIcon.id;\n\n addOrUpdateImage(\n 'if-not-in-sprite',\n suffixNumber(iconID, this.instanceIndex),\n customIcon.image as string | HTMLImageElement,\n this.mapLibreMap,\n {\n pixelRatio: customIcon.pixelRatio ?? 2,\n },\n );\n }\n\n if (config.icon.default) {\n if (config.icon.default.image) {\n addOrUpdateImage(\n 'if-not-in-sprite',\n this.defaultPlaceIconID,\n config.icon.default.image.image as string | HTMLImageElement,\n this.mapLibreMap,\n {\n pixelRatio: config.icon.default.image.pixelRatio ?? 2,\n },\n );\n }\n if (config.icon.default.style) {\n addOrUpdateImage(\n 'if-not-in-sprite',\n this.defaultPlaceIconID,\n defaultPin(config.icon.default.style),\n this.mapLibreMap,\n { pixelRatio: 2 },\n );\n }\n }\n } else {\n // Ensure default pin is added:\n addOrUpdateImage('if-not-in-sprite', this.defaultPlaceIconID, defaultPin(), this.mapLibreMap, {\n pixelRatio: 2,\n });\n }\n }\n\n private updateData(config: PlacesModuleConfig | undefined): void {\n this.sourcesWithLayers.places.source.runtimeSource?.setData(\n preparePlacesForDisplay(this.sourcesWithLayers.places.shownFeatures, this.instanceIndex, config),\n );\n }\n\n /**\n * Displays the given places on the map.\n *\n * @param places - Place data to display. Can be a single Place, array of Places,\n * or a Places FeatureCollection.\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown places\n * - Applies current module styling configuration\n * - Automatically generates labels if text config is set\n * - Waits for module to be ready before displaying\n *\n * **Data Sources:**\n * - TomTom Search API results\n * - Custom place objects matching the Place interface\n * - GeoJSON Point features\n *\n * @example\n * Display search results:\n * ```typescript\n * import { search } from '@tomtom-international/maps-sdk-js/services';\n *\n * const results = await search({ query: 'coffee' });\n * await placesModule.show(results);\n * ```\n *\n * @example\n * Display single place:\n * ```typescript\n * await placesModule.show({\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [4.9041, 52.3676] },\n * properties: {\n * address: { freeformAddress: 'Amsterdam' },\n * poi: { name: 'Amsterdam Central' }\n * }\n * });\n * ```\n *\n * @example\n * Display multiple places:\n * ```typescript\n * await placesModule.show([place1, place2, place3]);\n * ```\n */\n async show(places: Place | Place[] | Places) {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.places.show(preparePlacesForDisplay(places, this.instanceIndex, this.config));\n }\n\n /**\n * Removes all places from the map.\n *\n * @remarks\n * - Clears all displayed places\n * - Does not reset styling configuration\n * - Module remains initialized and ready for new data\n *\n * @example\n * ```typescript\n * await placesModule.clear();\n * ```\n */\n async clear() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.places.clear();\n }\n\n /**\n * Returns the currently shown places.\n *\n * @returns The places currently displayed on the map.\n *\n * @remarks\n * Returns the exact data that was passed to the `show()` method.\n *\n * @example\n * ```typescript\n * const shown = placesModule.getShown();\n * console.log(`Showing ${shown.places.features.length} places`);\n * ```\n */\n getShown() {\n return {\n places: this.sourcesWithLayers.places.shownFeatures,\n };\n }\n\n /**\n * Programmatically sets an event state on a specific place.\n *\n * @param options - Configuration for the event state to apply.\n *\n * @remarks\n * Use this to make places appear clicked or hovered programmatically.\n *\n * @example\n * ```typescript\n * // Make first place appear clicked\n * placesModule.putEventState({\n * index: 0,\n * state: 'click',\n * mode: 'put'\n * });\n * ```\n */\n putEventState(options: PutEventStateOptions) {\n this.sourcesWithLayers.places.putEventState(options);\n }\n\n /**\n * Removes an event state from a specific place.\n *\n * @param options - Configuration for which event state to remove.\n *\n * @example\n * ```typescript\n * placesModule.cleanEventState({ index: 0 });\n * ```\n */\n cleanEventState(options: CleanEventStateOptions): void {\n this.sourcesWithLayers.places.cleanEventState(options);\n }\n\n /**\n * Removes event states from multiple places.\n *\n * @param options - Optional filter for which states to remove.\n *\n * @example\n * ```typescript\n * // Remove all event states\n * placesModule.cleanEventStates();\n *\n * // Remove only hover states\n * placesModule.cleanEventStates({ states: ['hover'] });\n * ```\n */\n cleanEventStates(options?: CleanEventStatesOptions) {\n this.sourcesWithLayers.places.cleanEventStates(options);\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule<Place<DisplayPlaceProps>>(\n this.eventsProxy,\n this.sourcesWithLayers.places,\n this.config?.events,\n );\n }\n}\n","import type { ExpressionFilterSpecification, FilterSpecification, LegacyFilterSpecification } from 'maplibre-gl';\nimport type { FilterShowMode, FilterSyntaxVersion, MultiSyntaxFilter, ValuesFilter } from './types';\n\n/**\n * @ignore\n */\nconst isExpressionFilter = (filter: FilterSpecification): filter is ExpressionFilterSpecification => {\n if (filter === true || filter === false) {\n return true;\n }\n\n if (!Array.isArray(filter) || filter.length === 0) {\n return false;\n }\n switch (filter[0]) {\n case 'has':\n return filter.length >= 2 && filter[1] !== '$id' && filter[1] !== '$type';\n\n case 'in':\n return filter.length >= 3 && (typeof filter[1] !== 'string' || Array.isArray(filter[2]));\n\n case '!in':\n case '!has':\n case 'none':\n return false;\n\n case '==':\n case '!=':\n case '>':\n case '>=':\n case '<':\n case '<=':\n return filter.length !== 3 || Array.isArray(filter[1]) || Array.isArray(filter[2]);\n\n case 'any':\n case 'all':\n for (const f of filter.slice(1)) {\n if (!isExpressionFilter(f as FilterSpecification) && typeof f !== 'boolean') {\n return false;\n }\n }\n return true;\n\n default:\n return true;\n }\n};\n\n/**\n * @ignore\n */\nexport const getSyntaxVersion = (expression: FilterSpecification): FilterSyntaxVersion =>\n isExpressionFilter(expression) ? 'expression' : 'legacy';\n\n/**\n * @ignore\n */\nexport const getMergedAnyFilter = (filters: MultiSyntaxFilter[]): MultiSyntaxFilter | null => {\n if (!filters?.length) {\n return null;\n }\n if (filters.length === 1) {\n return filters[0];\n }\n return {\n expression: ['any', ...filters.map((filter) => filter?.expression)],\n legacy: ['any', ...filters.map((filter) => filter?.legacy)],\n };\n};\n\n/**\n * @ignore\n */\nexport const getMergedAllFilter = (\n filterToAdd: MultiSyntaxFilter,\n originalFilter: FilterSpecification | undefined,\n): FilterSpecification => {\n if (originalFilter) {\n return ['all', filterToAdd[getSyntaxVersion(originalFilter)], originalFilter] as FilterSpecification;\n }\n return filterToAdd.expression;\n};\n\n/**\n * @ignore\n */\nexport const buildMappedValuesFilter = <T>(\n propName: string,\n showMode: FilterShowMode,\n values: T[],\n): MultiSyntaxFilter => {\n if (values.length === 1) {\n const comparator = showMode === 'only' ? '==' : '!=';\n return {\n expression: [comparator, ['get', propName], values[0]] as ExpressionFilterSpecification,\n legacy: [comparator, propName, values[0]] as LegacyFilterSpecification,\n };\n }\n const filterArrayNew = ['in', ['get', propName], ['literal', values]];\n if (showMode === 'only') {\n return {\n expression: filterArrayNew as ExpressionFilterSpecification,\n legacy: ['in', propName, ...values] as LegacyFilterSpecification,\n };\n }\n return {\n expression: ['!', filterArrayNew as ExpressionFilterSpecification],\n legacy: ['!in', propName, ...values] as LegacyFilterSpecification,\n };\n};\n\n/**\n * @ignore\n */\nexport const buildValuesFilter = <T>(\n propName: string,\n filter: ValuesFilter<T>,\n valuesMapping?: (value: T) => unknown,\n): MultiSyntaxFilter =>\n buildMappedValuesFilter(propName, filter.show, valuesMapping ? filter.values.map(valuesMapping) : filter.values);\n","import type { MapStylePOICategory } from '../places';\n\n/**\n * Predefined groups of related POI categories for convenient filtering.\n *\n * @remarks\n * This object maps group names to arrays of {@link MapStylePOICategory} values.\n * Each group contains POI categories that share a common theme or purpose,\n * making it easier to filter multiple related POI types with a single identifier.\n *\n * **Available Groups:**\n * - `FOOD_DRINKS_GROUP` - Dining and beverage establishments (restaurants, cafes, pubs, etc.)\n * - `SHOPPING_GROUP` - Retail stores and shopping centers (shops, markets, supermarkets, etc.)\n * - `TRANSPORTATION_GROUP` - Transportation hubs and stops (airports, stations, terminals, etc.)\n * - `HEALTH_GROUP` - Healthcare facilities and services (hospitals, clinics, pharmacies, etc.)\n * - `PARKING_GROUP` - Parking facilities (garages and open parking areas)\n * - `HOLIDAY_TOURISM_GROUP` - Tourist attractions and recreational sites (museums, parks, beaches, etc.)\n * - `EV_CHARGING_STATIONS_GROUP` - Electric vehicle charging locations\n * - `GAS_STATIONS_GROUP` - Fuel stations for traditional vehicles\n * - `ACCOMMODATION_GROUP` - Lodging facilities (hotels, motels, camping grounds, etc.)\n * - `ENTERTAINMENT_GROUP` - Entertainment venues (cinemas, theaters, nightlife, casinos, etc.)\n * - `SPORTS_LEISURE_GROUP` - Sports and leisure facilities (stadiums, gyms, pools, golf courses, etc.)\n * - `EDUCATION_GROUP` - Educational institutions (schools, universities, libraries, etc.)\n * - `GOVERNMENT_GROUP` - Government and public safety facilities (offices, courts, embassies, police, fire stations)\n *\n * @example\n * Filter to show only food-related POIs:\n * ```ts\n * import { poiCategoryGroups } from '@tomtom-international/maps-sdk-js/map';\n *\n * const foodCategories = poiCategoryGroups.FOOD_DRINKS_GROUP;\n * console.log(foodCategories);\n * // ['RESTAURANT', 'FAST_FOOD', 'CAFE_PUB', 'PUB', 'WINERY', ...]\n * ```\n *\n * @example\n * Use with POI module filtering:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'show',\n * values: ['FOOD_DRINKS_GROUP', 'ENTERTAINMENT_GROUP']\n * }\n * });\n * ```\n *\n * @see {@link POICategoryGroup} - Type representing all available group names\n * @see {@link MapStylePOICategory} - Individual POI category identifiers\n *\n * @group POIs\n */\nexport const poiCategoryGroups: Record<string, MapStylePOICategory[]> = {\n FOOD_DRINKS_GROUP: [\n 'RESTAURANT',\n 'FAST_FOOD',\n 'CAFE_PUB',\n 'PUB',\n 'WINERY',\n 'PUB_FOOD',\n 'SOUL_FOOD',\n 'DELICATESSEN',\n ],\n SHOPPING_GROUP: [\n 'SHOP',\n 'SHOPPING_CENTER',\n 'CLOTHING_SHOP',\n 'MARKET',\n 'FOOD_MARKET',\n 'SUPERMARKETS_HYPERMARKETS',\n 'DEPARTMENT_STORE',\n 'CONVENIENCE_STORE',\n 'GROCERY_STORE',\n 'HARDWARE_STORE',\n 'ELECTRICAL_APPLIANCES_SHOP',\n ],\n TRANSPORTATION_GROUP: [\n 'AIRPORT',\n 'FERRY_TERMINAL',\n 'HELIPAD_HELICOPTER_LANDING',\n 'PUBLIC_TRANSPORT_STOP',\n 'RAILWAY_STATION',\n 'BUS_STOP',\n 'TAXI_STAND',\n ],\n HEALTH_GROUP: [\n 'DOCTOR',\n 'EMERGENCY_MEDICAL_SERVICE',\n 'EMERGENCY_ROOM',\n 'HEALTH_CARE_SERVICE',\n 'HOSPITAL',\n 'HOSPITAL_POLYCLINIC',\n 'PHARMACY',\n 'DENTIST',\n 'WELFARE_ORGANIZATION',\n ],\n PARKING_GROUP: ['PARKING_GARAGE', 'OPEN_PARKING_AREA'],\n HOLIDAY_TOURISM_GROUP: [\n 'AMUSEMENT_PARK',\n 'BEACH',\n 'HOLIDAY_RENTAL',\n 'GEOGRAPHIC_FEATURE',\n 'IMPORTANT_TOURIST_ATTRACTION',\n 'LEISURE_CENTER',\n 'MOUNTAIN_PASS',\n 'MOUNTAIN_PEAK',\n 'MUSEUM',\n 'SCENIC_PANORAMIC_VIEW',\n 'TOURIST_INFORMATION_OFFICE',\n ],\n EV_CHARGING_STATIONS_GROUP: ['ELECTRIC_VEHICLE_STATION'],\n GAS_STATIONS_GROUP: ['GAS_STATION', 'PETROL_STATION'],\n ACCOMMODATION_GROUP: ['CAMPING_GROUND', 'HOTEL_MOTEL', 'HOLIDAY_RENTAL'],\n ENTERTAINMENT_GROUP: [\n 'CINEMA',\n 'THEATER',\n 'MOVIE_THEATER',\n 'NIGHTLIFE',\n 'CONCERT_HALL',\n 'ENTERTAINMENT',\n 'CLUB_ASSOCIATION',\n 'CASINO',\n ],\n SPORTS_LEISURE_GROUP: [\n 'SPORTS_CENTER',\n 'WATER_SPORT',\n 'SWIMMING_POOL',\n 'GOLF_COURSE',\n 'STADIUM',\n 'BEACH',\n 'ICE_SKATING_RINK',\n 'LEISURE_CENTER',\n 'MOUNTAIN_PASS',\n 'MOUNTAIN_PEAK',\n ],\n EDUCATION_GROUP: ['SCHOOL', 'COLLEGE_UNIVERSITY', 'LIBRARY', 'CULTURAL_CENTER'],\n GOVERNMENT_GROUP: ['GOVERNMENT_OFFICE', 'COURTHOUSE', 'EMBASSY', 'FIRE_STATION_BRIGADE', 'POLICE_STATION'],\n};\n\n/**\n * POI category group type.\n *\n * @remarks\n * Represents predefined groups of related POI categories for convenient filtering.\n * Each group contains multiple {@link MapStylePOICategory} values that share a common theme.\n *\n * Using category groups simplifies filtering by allowing you to show or hide\n * multiple related POI types with a single filter value.\n *\n * **Available groups:**\n * - `FOOD_DRINKS_GROUP` - Restaurants, cafes, fast food, wineries, etc.\n * - `SHOPPING_GROUP` - Stores, malls, markets, supermarkets, etc.\n * - `TRANSPORTATION_GROUP` - Airports, train stations, bus stops, ferry terminals, etc.\n * - `HEALTH_GROUP` - Hospitals, clinics, pharmacies, doctors, dentists, etc.\n * - `PARKING_GROUP` - Parking garages and open parking areas\n * - `HOLIDAY_TOURISM_GROUP` - Tourist attractions, museums, beaches, scenic views, etc.\n * - `EV_CHARGING_STATIONS_GROUP` - Electric vehicle charging stations\n * - `GAS_STATIONS_GROUP` - Gas and petrol stations\n * - `ACCOMMODATION_GROUP` - Hotels, motels, camping grounds, etc.\n * - `ENTERTAINMENT_GROUP` - Cinemas, theaters, nightlife, casinos, etc.\n * - `SPORTS_LEISURE_GROUP` - Stadiums, sports centers, swimming pools, golf courses, etc.\n * - `EDUCATION_GROUP` - Schools, universities, libraries, cultural centers\n * - `GOVERNMENT_GROUP` - Government offices, courthouses, embassies, police, fire stations\n *\n * @example\n * Filter to show only food-related POIs:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'show',\n * values: ['FOOD_DRINKS_GROUP']\n * }\n * });\n * ```\n *\n * @example\n * Hide parking and gas stations:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'hide',\n * values: ['PARKING_GROUP', 'GAS_STATIONS_GROUP']\n * }\n * });\n * ```\n *\n * @example\n * Combine multiple groups for tourism use case:\n * ```ts\n * const tourismFilter = {\n * mode: 'show',\n * values: [\n * 'HOLIDAY_TOURISM_GROUP',\n * 'ACCOMMODATION_GROUP',\n * 'FOOD_DRINKS_GROUP',\n * 'ENTERTAINMENT_GROUP'\n * ]\n * };\n * ```\n *\n * @see {@link poiCategoryGroups} - The object containing all group definitions\n * @see {@link MapStylePOICategory} - For individual POI categories\n *\n * @group POIs\n */\nexport type POICategoryGroup = keyof typeof poiCategoryGroups;\n","import { POICategory } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport { toBaseMapPOICategory } from '../places';\nimport type { ValuesFilter } from '../shared';\nimport { AbstractMapModule, EventsModule, POI_SOURCE_ID, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { buildMappedValuesFilter, getMergedAllFilter } from '../shared/mapLibreFilterUtils';\nimport { waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { poiLayerIDs } from './layers/poisLayers';\nimport { poiCategoryGroups } from './poiCategoryGroups';\nimport type { FilterablePOICategory, POIsModuleConfig, POIsModuleFeature } from './types/poisModuleConfig';\n\n/**\n * Gets the specified filtered categories icon IDs to be used in map filtering.\n * @param categories list of filtered categories.\n * @ignore\n */\nexport const getStyleCategories = (categories: FilterablePOICategory[]): string[] => {\n const categoryIds: string[] = [];\n categories.forEach((category: FilterablePOICategory) => {\n if (category in poiCategoryGroups) {\n categoryIds.push(...poiCategoryGroups[category].map(toBaseMapPOICategory));\n } else {\n categoryIds.push(toBaseMapPOICategory(category as POICategory));\n }\n });\n return [...new Set(categoryIds)];\n};\n\n/**\n * IDs of sources and layers for places of interest module.\n */\ntype PoIsSourcesAndLayers = {\n /**\n * Places of interest with corresponding layer ids.\n * TODO: technically source ID is vectorTiles if POIs stay included in base map for Orbis\n */\n poi: StyleSourceWithLayers;\n};\n\n/**\n * POIs Module for controlling Points of Interest displayed in the map style.\n *\n * This module manages the built-in POI layer from the vector map, allowing you to\n * show/hide POIs and filter them by category. POIs are already part of the map style\n * and include businesses, landmarks, and other points of interest.\n *\n * @remarks\n * **Features:**\n * - Toggle POI visibility on/off\n * - Filter by POI categories or category groups\n * - Event handling for POI interactions\n * - Based on vector tile data in the map style\n *\n * **POI Categories:**\n * - Individual categories (e.g., RESTAURANT, HOTEL_MOTEL, PARKING_GARAGE)\n * - Category groups (e.g., FOOD_DRINKS_GROUP, SHOPPING_GROUP, TRANSPORTATION_GROUP)\n *\n * **Difference from PlacesModule:**\n * - POIsModule: Controls existing POIs in the map style\n * - PlacesModule: Displays custom place data from Search API or other sources\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { POIsModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module\n * const poisModule = await POIsModule.get(map);\n *\n * // Toggle visibility\n * poisModule.setVisible(false);\n * poisModule.setVisible(true);\n * ```\n *\n * @example\n * Filter specific categories:\n * ```typescript\n * // Show only restaurants and hotels\n * poisModule.filterCategories({\n * show: 'only',\n * values: ['RESTAURANT', 'HOTEL_MOTEL']\n * });\n *\n * // Hide parking garages\n * poisModule.filterCategories({\n * show: 'all_except',\n * values: ['PARKING_GARAGE', 'OPEN_PARKING_AREA']\n * });\n * ```\n *\n * @example\n * Filter using category groups:\n * ```typescript\n * // Show only food and shopping POIs\n * poisModule.filterCategories({\n * show: 'only',\n * values: ['FOOD_DRINKS_GROUP', 'SHOPPING_GROUP']\n * });\n *\n * // Hide transportation POIs\n * pois.filterCategories({\n * show: 'all_except',\n * values: ['TRANSPORTATION_GROUP']\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('Clicked POI:', feature.properties.name);\n * console.log('Category:', feature.properties.category);\n * });\n * ```\n *\n * @see [POIs Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/pois)\n *\n * @group POIs\n */\nexport class POIsModule extends AbstractMapModule<PoIsSourcesAndLayers, POIsModuleConfig> {\n private categoriesFilter?: ValuesFilter<FilterablePOICategory> | null;\n private originalFilter?: FilterSpecification;\n\n /**\n * Retrieves a POIsModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional initial configuration for visibility and filters.\n *\n * @returns A promise that resolves to the initialized POIsModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `filters.categories`: Category filter to apply on initialization\n *\n * @throws Error if the POI source is not found in the map style\n *\n * @example\n * Default initialization:\n * ```typescript\n * const poisModule = await POIsModule.get(map);\n * ```\n *\n * @example\n * With initial filter:\n * ```typescript\n * const poisModule = await POIsModule.get(map, {\n * visible: true,\n * filters: {\n * categories: {\n * show: 'only',\n * values: ['RESTAURANT', 'CAFE_PUB']\n * }\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: POIsModuleConfig): Promise<POIsModule> {\n await waitUntilMapIsReady(map);\n return new POIsModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: POIsModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const poiRuntimeSource = this.mapLibreMap.getSource(POI_SOURCE_ID);\n if (!poiRuntimeSource) {\n throw notInTheStyle(`init ${POIsModule.name} with source ID ${POI_SOURCE_ID}`);\n }\n const poi = new StyleSourceWithLayers(this.mapLibreMap, poiRuntimeSource, (layer) =>\n poiLayerIDs.includes(layer.id),\n );\n // TODO: check if the layers are present in the style, and if not, throw exception?\n const mainLayer = poi.sourceAndLayerIDs.layerIDs[0];\n if (this.mapLibreMap.getLayer(mainLayer)) {\n this.originalFilter = this.mapLibreMap.getFilter(mainLayer) as FilterSpecification;\n }\n return { poi };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: POIsModuleConfig | undefined) {\n if (config && !isNil(config.visible)) {\n this.setVisible(config.visible);\n } else if (!this._initializing && !this.isVisible()) {\n // applying default:\n this.setVisible(true);\n }\n\n this.filterCategories(config?.filters?.categories);\n return config;\n }\n\n /**\n * Checks if POI layers are currently visible.\n *\n * @returns `true` if at least one POI layer is visible, `false` if all are hidden.\n *\n * @example\n * ```typescript\n * if (pois.isVisible()) {\n * console.log('POIs are displayed');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.poi.isAnyLayerVisible();\n }\n\n /**\n * Sets the visibility of POI layers.\n *\n * @param visible - `true` to show POIs, `false` to hide them.\n *\n * @remarks\n * Changes are applied immediately if the map is ready.\n *\n * @example\n * ```typescript\n * pois.setVisible(false); // Hide all POIs\n * pois.setVisible(true); // Show all POIs\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = {\n ...this.config,\n visible,\n };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.poi.setLayersVisible(visible);\n }\n }\n\n /**\n * Filters POIs by category or category group.\n *\n * @param categoriesFilter - Filter configuration specifying which categories to show/hide.\n * Pass `undefined` to reset to default (show all).\n *\n * @remarks\n * **Filter Modes:**\n * - `only`: Show only the specified categories, hide all others\n * - `all_except`: Show all categories except the specified ones\n *\n * **Category Types:**\n * - Individual categories (e.g., 'RESTAURANT', 'HOTEL_MOTEL')\n * - Category groups (e.g., 'FOOD_DRINKS_GROUP', 'SHOPPING_GROUP')\n *\n * **Available Category Groups:**\n * - FOOD_DRINKS_GROUP\n * - SHOPPING_GROUP\n * - TRANSPORTATION_GROUP\n * - HEALTH_GROUP\n * - PARKING_GROUP\n * - HOLIDAY_TOURISM_GROUP\n * - EV_CHARGING_STATIONS_GROUP\n * - GAS_STATIONS_GROUP\n * - ACCOMMODATION_GROUP\n * - ENTERTAINMENT_GROUP\n * - SPORTS_LEISURE_GROUP\n * - EDUCATION_GROUP\n * - GOVERNMENT_GROUP\n *\n * @example\n * Show only restaurants:\n * ```typescript\n * pois.filterCategories({\n * show: 'only',\n * values: ['RESTAURANT']\n * });\n * ```\n *\n * @example\n * Hide parking:\n * ```typescript\n * pois.filterCategories({\n * show: 'all_except',\n * values: ['PARKING_GROUP']\n * });\n * ```\n *\n * @example\n * Reset filter (show all):\n * ```typescript\n * pois.filterCategories(undefined);\n * ```\n */\n filterCategories(categoriesFilter?: ValuesFilter<FilterablePOICategory> | undefined): void {\n if (categoriesFilter) {\n if (this.tomtomMap.mapReady) {\n const poiFilter = buildMappedValuesFilter(\n 'category',\n categoriesFilter.show,\n getStyleCategories(categoriesFilter.values),\n );\n\n this.mapLibreMap.setFilter('POI', getMergedAllFilter(poiFilter, this.originalFilter));\n }\n this.config = {\n ...this.config,\n filters: {\n categories: categoriesFilter,\n },\n };\n } else if (this.categoriesFilter) {\n // reset categories config to default\n this.config = {\n ...this.config,\n filters: {\n categories: {\n show: 'all_except',\n values: [],\n },\n },\n };\n if (this.tomtomMap.mapReady) {\n // Applies default:\n this.mapLibreMap.setFilter('POI', this.originalFilter);\n }\n }\n\n this.categoriesFilter = categoriesFilter;\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible POI features with properly typed properties.\n *\n * @example\n * ```typescript\n * const shown = poisModule.getShown();\n * console.log(`Visible POIs: ${shown.poi.length}`);\n * shown.poi.forEach(poi => {\n * console.log(poi.properties.name, poi.properties.category);\n * });\n * ```\n */\n getShown() {\n return {\n poi: this.mapLibreMap.queryRenderedFeatures({\n layers: this.sourcesWithLayers.poi.sourceAndLayerIDs.layerIDs,\n validate: false,\n }) as unknown as POIsModuleFeature[],\n };\n }\n\n /**\n * Gets the events interface for handling user interactions with POIs.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a POI\n * - `contextmenu`: User right-clicks on a POI\n * - `hover`: Mouse enters a POI\n * - `long-hover`: Mouse hovers over POI for extended time\n *\n * **Event Feature Properties:**\n * - `id`: Unique POI identifier\n * - `name`: POI name in native language\n * - `category`: POI category\n * - `iconID`: Icon sprite ID\n * - `group`: Category group\n * - `priority`: Importance level (lower = more important)\n *\n * @example\n * ```typescript\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('POI:', feature.properties.name);\n * console.log('Category:', feature.properties.category);\n * console.log('ID:', feature.properties.id);\n * });\n *\n * pois.events.on('hover', (feature) => {\n * showTooltip(feature.properties.name);\n * });\n * ```\n */\n get events() {\n return new EventsModule<POIsModuleFeature>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.poi,\n this.config?.events,\n );\n }\n}\n","import type { LayerSpecification } from 'maplibre-gl';\nimport { poiLayerIDs } from '../pois';\nimport type { LayerSpecFilter } from '../shared';\nimport type { BaseMapLayerGroupName, BaseMapLayerGroups } from './types/baseMapModuleConfig';\n\ntype LayerGroupMapping = {\n layerIDMatches: string[];\n layerTypes: LayerSpecification['type'][];\n};\n\nconst layerGroupMappings: Record<BaseMapLayerGroupName, LayerGroupMapping> = {\n land: {\n layerIDMatches: ['lulc'],\n layerTypes: ['fill'],\n },\n borders: {\n layerIDMatches: ['borders'],\n layerTypes: ['line'],\n },\n water: {\n layerIDMatches: ['water'],\n layerTypes: ['fill', 'line'],\n },\n buildings2D: {\n layerIDMatches: ['building'],\n layerTypes: ['fill', 'line'],\n },\n buildings3D: {\n layerIDMatches: ['building'],\n layerTypes: ['fill-extrusion'],\n },\n houseNumbers: {\n layerIDMatches: ['house number'],\n layerTypes: ['symbol'],\n },\n roadLines: {\n layerIDMatches: ['road', 'tunnel', 'bridge', 'surface'],\n layerTypes: ['fill', 'line'],\n },\n roadLabels: {\n layerIDMatches: ['road', 'tunnel', 'bridge', 'surface'],\n layerTypes: ['symbol'],\n },\n roadShields: {\n layerIDMatches: ['shield'],\n layerTypes: ['symbol'],\n },\n placeLabels: {\n layerIDMatches: ['places'],\n layerTypes: ['symbol'],\n },\n smallerTownLabels: {\n layerIDMatches: ['town', 'village', 'neighbourhood'],\n layerTypes: ['symbol'],\n },\n cityLabels: {\n layerIDMatches: ['city', 'capital'],\n layerTypes: ['symbol'],\n },\n capitalLabels: {\n layerIDMatches: ['capital'],\n layerTypes: ['symbol'],\n },\n stateLabels: {\n layerIDMatches: ['state'],\n layerTypes: ['symbol'],\n },\n countryLabels: {\n layerIDMatches: ['places - country'],\n layerTypes: ['symbol'],\n },\n};\n\nconst isMatching = (group: BaseMapLayerGroupName, layer: LayerSpecification) => {\n const mapping = layerGroupMappings[group];\n return (\n mapping.layerIDMatches.some((part) => layer.id.toLowerCase().includes(part)) &&\n mapping.layerTypes.includes(layer.type)\n );\n};\n\n/**\n * @ignore\n */\nexport const buildLayerGroupFilter = (layerGroups: BaseMapLayerGroups): LayerSpecFilter => {\n const mode = layerGroups.mode;\n const groups = layerGroups.names;\n if (mode === 'include') {\n return (layer) => groups.some((group) => isMatching(group, layer));\n }\n if (mode === 'exclude') {\n return (layer) => !groups.some((group) => isMatching(group, layer));\n }\n // No filtering if we don't recognize the mode:\n console.error('Unrecognized layer group mode:', mode);\n return () => true;\n};\n\n/**\n * @ignore\n */\nexport const filterLayerByGroups = (layer: LayerSpecification, layerGroups?: BaseMapLayerGroups): boolean => {\n const mode = layerGroups?.mode;\n const groups = layerGroups?.names;\n if (mode && groups?.length) {\n if (mode === 'include') {\n return groups.some((group) => isMatching(group, layer));\n }\n if (mode === 'exclude') {\n return !groups.some((group) => isMatching(group, layer));\n }\n }\n return true;\n};\n\n/**\n * @ignore\n */\nexport const buildBaseMapLayerGroupFilter =\n (layerGroupsFilter?: BaseMapLayerGroups): LayerSpecFilter =>\n (layer: LayerSpecification): boolean =>\n (!layerGroupsFilter || filterLayerByGroups(layer, layerGroupsFilter)) && !poiLayerIDs.includes(layer.id);\n","import { isNil } from 'lodash-es';\n\nimport { AbstractMapModule, BASE_MAP_SOURCE_ID, EventsModule, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { waitUntilMapIsReady } from '../shared/mapUtils';\nimport { TomTomMap } from '../TomTomMap';\nimport { buildBaseMapLayerGroupFilter, buildLayerGroupFilter } from './layerGroups';\nimport type { BaseMapLayerGroups, BaseMapModuleConfig, BaseMapModuleInitConfig } from './types/baseMapModuleConfig';\n\ntype BaseSourceAndLayers = {\n vectorTiles: StyleSourceWithLayers;\n};\n\n/**\n * Base Map Module for controlling standard map layers and their visibility.\n *\n * This module manages the fundamental map layers including background, water, land, roads,\n * buildings, labels, and other vector tile layers from the base map style.\n *\n * @remarks\n * **Managed Layers:**\n * - Background and terrain\n * - Water bodies and coastlines\n * - Country and administrative borders\n * - Buildings (2D and 3D)\n * - Road lines, labels, and shields\n * - Place labels at various zoom levels\n * - House numbers\n *\n * **Does NOT Include:**\n * - Traffic flow/incidents (use {@link TrafficFlowModule} or {@link TrafficIncidentsModule})\n * - Points of Interest/POIs (use {@link POIsModule})\n * - Hillshade/terrain shading (use {@link HillshadeModule})\n *\n * **Use Cases:**\n * - Toggle base map visibility on/off\n * - Show only specific layer groups (e.g., roads only)\n * - Create custom map appearances by hiding certain elements\n * - Build overlay maps with selective base layers\n *\n * @example\n * Basic usage:\n * ```typescript\n * // Get module with default configuration\n * const baseMap = await BaseMapModule.get(map);\n *\n * // Toggle visibility\n * baseMap.setVisible(false); // Hide all base layers\n * baseMap.setVisible(true); // Show all base layers\n *\n * // Check current state\n * if (baseMap.isVisible()) {\n * console.log('Base map is visible');\n * }\n * ```\n *\n * @example\n * Working with layer groups:\n * ```typescript\n * // Show only roads and borders\n * const baseMap = await BaseMapModule.get(map, {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'borders']\n * }\n * });\n *\n * // Hide buildings and labels\n * baseMap.setVisible(false, {\n * layerGroups: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D', 'placeLabels']\n * }\n * });\n *\n * // Show only water and land\n * baseMap.setVisible(true, {\n * layerGroups: {\n * mode: 'include',\n * names: ['water', 'land']\n * }\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map);\n *\n * // Listen for clicks on base map features\n * baseMap.events.on('click', (feature, lngLat) => {\n * console.log('Clicked base map feature:', feature);\n * });\n *\n * // Remove event listeners\n * baseMap.events.off('click');\n * ```\n *\n * @see [Base Map Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/base-map)\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n *\n * @group Base Map\n */\nexport class BaseMapModule extends AbstractMapModule<BaseSourceAndLayers, BaseMapModuleConfig> {\n /**\n * Asynchronously retrieves a BaseMapModule instance for the given map.\n *\n * This is the recommended way to create a BaseMapModule. It ensures the map\n * is fully loaded before initializing the module.\n *\n * @param tomtomMap - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for module initialization.\n *\n * @returns A promise that resolves to the initialized BaseMapModule.\n *\n * @remarks\n * **Initialization:**\n * - Waits for map to be ready before creating module\n * - Validates that required sources exist in the map style\n * - Applies initial configuration if provided\n *\n * **Configuration Options:**\n * - `visible`: Initial visibility state\n * - `layerGroupsFilter`: Which layer groups to include/exclude\n * - `layerGroupsVisibility`: Fine-grained visibility per group\n *\n * @throws Error if the base map source is not found in the style\n *\n * @example\n * Default initialization:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map);\n * ```\n *\n * @example\n * With configuration:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map, {\n * visible: true,\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings3D', 'houseNumbers']\n * }\n * });\n * ```\n *\n * @example\n * Show only specific groups:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map, {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land', 'borders']\n * },\n * visible: true\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: BaseMapModuleInitConfig): Promise<BaseMapModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new BaseMapModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: BaseMapModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config: BaseMapModuleInitConfig | undefined) {\n const source = this.mapLibreMap.getSource(BASE_MAP_SOURCE_ID);\n if (!source) {\n throw notInTheStyle(`init ${BaseMapModule.name} with source ID ${BASE_MAP_SOURCE_ID}`);\n }\n\n return {\n vectorTiles: new StyleSourceWithLayers(\n this.mapLibreMap,\n source,\n buildBaseMapLayerGroupFilter(config?.layerGroupsFilter),\n ),\n };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: BaseMapModuleConfig | undefined) {\n if (config && !isNil(config.visible)) {\n this.setVisible(config.visible);\n } else if (!this._initializing && !this.isVisible()) {\n // applying default:\n this.setVisible(true);\n }\n\n if (config?.layerGroupsVisibility) {\n this.setVisible(config.layerGroupsVisibility.visible, { layerGroups: config.layerGroupsVisibility });\n }\n\n // We merge the given config with the previous one to ensure init config parameters are kept:\n // (the init config can have more parameters than the runtime one)\n return this.config || config ? { ...this.config, ...config } : undefined;\n }\n\n /**\n * Checks if any base map layers are currently visible.\n *\n * @returns `true` if at least one base map layer is visible, `false` if all are hidden.\n *\n * @remarks\n * This checks the actual visibility state of layers in the map, not just the\n * module's configuration setting.\n *\n * @example\n * ```typescript\n * if (baseMap.isVisible()) {\n * console.log('Base map is rendered');\n * } else {\n * console.log('Base map is hidden');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.vectorTiles.isAnyLayerVisible();\n }\n\n /**\n * Sets the visibility of base map layers.\n *\n * @param visible - `true` to show layers, `false` to hide them.\n * @param options - Optional settings for fine-grained control.\n * @param options.layerGroups - Target specific layer groups instead of all layers.\n *\n * @remarks\n * **Behavior:**\n * - Without `options.layerGroups`: Affects all base map layers\n * - With `options.layerGroups`: Affects only specified layer groups\n * - Changes are applied immediately if map is ready\n *\n * **Layer Groups:**\n * Available groups: `land`, `water`, `borders`, `buildings2D`, `buildings3D`,\n * `houseNumbers`, `roadLines`, `roadLabels`, `roadShields`, `placeLabels`,\n * `smallerTownLabels`, `cityLabels`, `capitalLabels`, `stateLabels`, `countryLabels`\n *\n * @example\n * Show/hide all layers:\n * ```typescript\n * baseMap.setVisible(false); // Hide everything\n * baseMap.setVisible(true); // Show everything\n * ```\n *\n * @example\n * Control specific groups:\n * ```typescript\n * // Hide only buildings\n * baseMap.setVisible(false, {\n * layerGroups: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D']\n * }\n * });\n *\n * // Show everything except labels\n * baseMap.setVisible(true, {\n * layerGroups: {\n * mode: 'exclude',\n * names: ['placeLabels', 'cityLabels', 'countryLabels']\n * }\n * });\n * ```\n *\n * @example\n * Toggle visibility:\n * ```typescript\n * const isVisible = baseMap.isVisible();\n * baseMap.setVisible(!isVisible); // Toggle\n * ```\n */\n setVisible(visible: boolean, options?: { layerGroups?: BaseMapLayerGroups }): void {\n if (!options?.layerGroups) {\n // We remove the layer groups visibility from the config if it was there:\n delete this.config?.layerGroupsVisibility;\n this.config = { ...this.config, visible };\n } else {\n this.config = { ...this.config, layerGroupsVisibility: { ...options.layerGroups, visible } };\n }\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.vectorTiles.setLayersVisible(\n visible,\n options?.layerGroups && buildLayerGroupFilter(options.layerGroups),\n );\n }\n }\n\n /**\n * Gets the events interface for this module to handle user interactions.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a base map feature\n * - `contextmenu`: User right-clicks on a feature\n * - `hover`: Mouse enters a feature\n * - `long-hover`: Mouse hovers over a feature for extended time\n *\n * **Event Handler Signature:**\n * ```typescript\n * (feature: MapGeoJSONFeature, lngLat: LngLat, allFeatures: MapGeoJSONFeature[]) => void\n * ```\n *\n * @example\n * Register click handler:\n * ```typescript\n * baseMap.events.on('click', (feature, lngLat) => {\n * console.log('Clicked on:', feature.properties);\n * console.log('At coordinates:', lngLat);\n * });\n * ```\n *\n * @example\n * Multiple event types:\n * ```typescript\n * // Show tooltip on hover\n * baseMap.events.on('hover', (feature) => {\n * showTooltip(feature.properties.name);\n * });\n *\n * // Handle clicks\n * baseMap.events.on('click', (feature) => {\n * selectFeature(feature.id);\n * });\n *\n * // Clean up\n * baseMap.events.off('hover');\n * baseMap.events.off('click');\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.vectorTiles, this.config?.events);\n }\n}\n","import type { MapModuleCommonConfig } from '../../shared';\n\n/**\n * Available base map layer group identifiers.\n *\n * @remarks\n * Use these names with {@link BaseMapModule} to control layer visibility.\n *\n * @see {@link BaseMapLayerGroupName}\n * @see {@link BaseMapModuleInitConfig.layerGroupsFilter}\n *\n * @group Base Map\n */\nexport const baseMapLayerGroupNames = [\n 'land',\n 'water',\n 'borders',\n 'buildings2D',\n 'buildings3D',\n 'houseNumbers',\n 'roadLines',\n 'roadLabels',\n 'roadShields',\n 'placeLabels',\n 'smallerTownLabels',\n 'cityLabels',\n 'capitalLabels',\n 'stateLabels',\n 'countryLabels',\n] as const;\n\n/**\n * Name of a base map layer group.\n *\n * Identifies specific categories of base map layers that can be controlled together.\n *\n * @remarks\n * **Available Layer Groups:**\n * - `land` - Land areas and terrain\n * - `water` - Water bodies (oceans, lakes, rivers)\n * - `borders` - Country and administrative boundaries\n * - `buildings2D` - 2D building footprints\n * - `buildings3D` - 3D building models\n * - `houseNumbers` - House number labels\n * - `roadLines` - Road line geometries\n * - `roadLabels` - Street name labels\n * - `roadShields` - Highway shields (e.g., I-95, A1)\n * - `placeLabels` - General place labels\n * - `smallerTownLabels` - Small town/village labels\n * - `cityLabels` - City labels\n * - `capitalLabels` - Capital city labels\n * - `stateLabels` - State/province labels\n * - `countryLabels` - Country name labels\n *\n * @example\n * ```typescript\n * const group: BaseMapLayerGroupName = 'roadLines';\n * const labels: BaseMapLayerGroupName[] = ['cityLabels', 'countryLabels'];\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroupName = (typeof baseMapLayerGroupNames)[number];\n\n/**\n * Layer group visibility configuration with explicit visible state.\n *\n * Extends {@link BaseMapLayerGroups} to include a visibility flag, allowing\n * you to show or hide specific groups of base map layers.\n *\n * @example\n * ```typescript\n * // Hide all buildings\n * const config: BaseMapLayerGroupsVisibility = {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * };\n *\n * // Show only roads\n * const roadsOnly: BaseMapLayerGroupsVisibility = {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels'],\n * visible: true\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroupsVisibility = BaseMapLayerGroups & { visible: boolean };\n\n/**\n * Layer group filter for selective base map display.\n *\n * Defines which layer groups to include or exclude from the base map module.\n * Can be expressed as explicit inclusions (show only these) or exclusions\n * (show all except these).\n *\n * @remarks\n * **Filter Modes:**\n * - `include`: Only the specified groups are shown, all others are hidden\n * - `exclude`: All groups are shown except the specified ones\n *\n * **Common Use Cases:**\n * - Show only roads and labels (minimal map)\n * - Hide buildings for cleaner appearance\n * - Show only water and land (base terrain)\n * - Remove labels for overlay maps\n *\n * @example\n * ```typescript\n * // Show only roads and borders\n * const roadsOnly: BaseMapLayerGroups = {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'borders']\n * };\n *\n * // Show everything except buildings\n * const noBuildings: BaseMapLayerGroups = {\n * mode: 'exclude',\n * names: ['buildings2D', 'buildings3D']\n * };\n *\n * // Show only terrain (no labels, no roads)\n * const terrainOnly: BaseMapLayerGroups = {\n * mode: 'include',\n * names: ['land', 'water']\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroups = {\n /**\n * Filter mode determining whether groups are included or excluded.\n *\n * @remarks\n * - `include`: Only the specified groups are considered, all others are ignored\n * - `exclude`: All base map groups except the specified ones are considered\n *\n * @example\n * ```typescript\n * mode: 'include' // Whitelist approach\n * mode: 'exclude' // Blacklist approach\n * ```\n */\n mode: 'include' | 'exclude';\n\n /**\n * Names of the layer groups to include or exclude.\n *\n * @remarks\n * The meaning depends on the `mode`:\n * - In `include` mode: Only these groups will be shown\n * - In `exclude` mode: These groups will be hidden, all others shown\n *\n * @example\n * ```typescript\n * // Show only these\n * names: ['roadLines', 'roadLabels', 'water']\n *\n * // Hide these\n * names: ['buildings2D', 'buildings3D', 'houseNumbers']\n * ```\n */\n names: BaseMapLayerGroupName[];\n};\n\n/**\n * Configuration for the BaseMapModule (initialization or runtime).\n *\n * Controls visibility and behavior of base map layer groups. Can be used both\n * during module initialization and for runtime updates.\n *\n * @remarks\n * This configuration allows fine-grained control over which base map elements\n * are displayed, enabling you to create custom map appearances for different\n * use cases.\n *\n * @example\n * ```typescript\n * // Hide specific layer groups\n * const config: BaseMapModuleConfig = {\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * }\n * };\n *\n * // Show only certain groups\n * const minimalConfig: BaseMapModuleConfig = {\n * visible: true,\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['roadLines', 'water', 'land'],\n * visible: true\n * }\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapModuleConfig = MapModuleCommonConfig & {\n /**\n * Controls the visibility of all layers associated with this module.\n *\n * @default true\n */\n visible?: boolean;\n\n /**\n * Optional visibility configuration for specific layer groups.\n *\n * @remarks\n * **Important:** The layer groups specified here must be included in the\n * module (not excluded by `layerGroupsFilter` during initialization).\n *\n * Use this to control visibility of layer groups at runtime without\n * reinitializing the module.\n *\n * @example\n * ```typescript\n * // Hide all building layers\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * }\n *\n * // Show only labels\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['placeLabels', 'cityLabels', 'countryLabels'],\n * visible: true\n * }\n * ```\n */\n layerGroupsVisibility?: BaseMapLayerGroupsVisibility;\n};\n\n/**\n * Initialization configuration for the BaseMapModule.\n *\n * Extends {@link BaseMapModuleConfig} with additional options available only\n * during module initialization.\n *\n * @remarks\n * **Initialization vs Runtime:**\n * - `layerGroupsFilter`: Only available at init - determines which groups to load\n * - `layerGroupsVisibility`: Available at init and runtime - controls visibility\n *\n * **Use Cases:**\n * - Create minimal maps with only essential layers\n * - Build custom map styles by excluding certain features\n * - Optimize performance by not loading unnecessary layers\n *\n * @example\n * ```typescript\n * // Initialize with only roads and water\n * const initConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'water', 'land']\n * },\n * visible: true\n * };\n *\n * // Exclude buildings from initialization\n * const noBuildingsConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings2D', 'buildings3D', 'houseNumbers']\n * }\n * };\n *\n * // Minimal map for data overlay\n * const overlayBaseConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land', 'borders']\n * },\n * visible: true\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapModuleInitConfig = BaseMapModuleConfig & {\n /**\n * Layer groups to include/exclude during module initialization.\n *\n * @remarks\n * **One-time configuration:** This can only be set during initialization.\n * Once the module is created, you cannot change which groups are loaded,\n * only their visibility via `layerGroupsVisibility`.\n *\n * @example\n * ```typescript\n * // Load only essential layers\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['land', 'water', 'roadLines', 'borders']\n * }\n *\n * // Load everything except 3D buildings\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings3D']\n * }\n *\n * // Minimal overlay map\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land']\n * }\n * ```\n */\n layerGroupsFilter?: BaseMapLayerGroups;\n};\n","import type { Anything, CommonPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport type { SupportsEvents } from '../../shared';\n\n/**\n * Extra properties to display color and title for a geometry on the map.\n *\n * Provides customization options for rendering polygon geometries, including\n * visual styling and labeling.\n *\n * @remarks\n * **Use Cases:**\n * - Search result boundaries (e.g., city limits, postal codes)\n * - Reachable range polygons\n * - Delivery zones\n * - Custom area highlights\n *\n * These properties override default styling and allow per-feature customization.\n *\n * @example\n * ```typescript\n * const geometryProps: ExtraGeometryDisplayProps = {\n * title: 'Amsterdam City Center',\n * color: '#FF5733',\n * eventState: 'click'\n * };\n *\n * // With custom properties\n * const customProps: ExtraGeometryDisplayProps = {\n * title: 'Delivery Zone A',\n * color: '#00FF00',\n * zoneId: 'zone-a',\n * capacity: 100\n * };\n * ```\n *\n * @group Geometries\n */\nexport type ExtraGeometryDisplayProps = {\n /**\n * Display title for the geometry.\n *\n * @remarks\n * Optional text label displayed at the center of the polygon.\n * If not provided, no label will be shown.\n *\n * **Common Uses:**\n * - Area names (e.g., \"Downtown\", \"Zone A\")\n * - Statistics (e.g., \"30 min reachable\")\n * - Custom labels\n *\n * @example\n * ```typescript\n * title: 'Amsterdam City Center'\n * title: 'Zone A'\n * title: '30 min driving range'\n * title: undefined // No label\n * ```\n */\n title?: string;\n\n /**\n * Fill color for the geometry.\n *\n * @remarks\n * Overrides the default fill color from the module configuration.\n * Accepts any valid CSS color value.\n *\n * **Color Formats:**\n * - Hex: '#FF5733'\n * - RGB: 'rgb(255, 87, 51)'\n * - RGBA: 'rgba(255, 87, 51, 0.5)'\n * - Named: 'red', 'blue', 'green'\n *\n * @example\n * ```typescript\n * color: '#FF5733'\n * color: 'rgb(0, 128, 255)'\n * color: 'rgba(255, 0, 0, 0.5)'\n * color: 'red'\n * ```\n */\n color?: string;\n} & SupportsEvents &\n Anything;\n\n/**\n * Geometry base and display properties.\n *\n * Combines complete place information with geometry-specific display properties\n * for rendering polygon features on the map.\n *\n * @remarks\n * Used by the GeometriesModule for rendering:\n * - Search API geometry results\n * - Reachable range polygons\n * - Custom polygon overlays\n * - Administrative boundaries\n *\n * Includes both geographic/metadata and visual styling properties.\n *\n * @example\n * ```typescript\n * const geometry: DisplayGeometryProps = {\n * type: 'Polygon',\n * id: 'geom-123',\n * title: 'Amsterdam',\n * color: '#0080FF',\n * address: {\n * municipalitySubdivision: 'Amsterdam',\n * countryCode: 'NL'\n * }\n * };\n * ```\n *\n * @group Geometries\n */\nexport type DisplayGeometryProps = CommonPlaceProps & ExtraGeometryDisplayProps;\n\n/**\n * @ignore\n */\nexport const GEOMETRY_TITLE_PROP = 'title';\n\n/**\n * @ignore\n */\nexport const GEOMETRY_COLOR_PROP = 'color';\n","import type { FillLayerSpecification, LineLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { GEOMETRY_COLOR_PROP } from '../types/geometryDisplayProps';\n\nexport const colorPalettes = {\n warm: [\n '#793F0D',\n '#AC703D',\n '#C38E63',\n '#E49969',\n '#E5AE86',\n '#EEC5A9',\n '#6E7649',\n '#9D9754',\n '#C7C397',\n '#B4A851',\n '#DFD27C',\n '#E7E3B5',\n '#846D74',\n '#B7A6AD',\n '#D3C9CE',\n ],\n browns: [\n '#edc4b3',\n '#e6b8a2',\n '#deab90',\n '#d69f7e',\n '#cd9777',\n '#c38e70',\n '#b07d62',\n '#9d6b53',\n '#8a5a44',\n '#774936',\n ],\n cold: ['#344464', '#548ca4', '#549cac', '#2c445c', '#a4ccd4', '#acbccc', '#b4c4d4', '#acd4cc', '#5c8ca4'],\n fadedBlues: [\n '#152033',\n '#1d2d44',\n '#2e455d',\n '#3e5c76',\n '#4c6884',\n '#597491',\n '#67809e',\n '#6e86a5',\n '#7b91ad',\n '#879bb4',\n ],\n blues: ['#03045e', '#023e8a', '#0077b6', '#0096c7', '#00b4d8', '#48cae4', '#90e0ef', '#ade8f4', '#caf0f8'],\n greens: ['#004b23', '#006400', '#007200', '#008000', '#38b000', '#70e000', '#9ef01a', '#ccff33'],\n fadedGreenToBlue: [\n '#d9ed92',\n '#b5e48c',\n '#99d98c',\n '#76c893',\n '#52b69a',\n '#34a0a4',\n '#168aad',\n '#1a759f',\n '#1e6091',\n '#184e77',\n ],\n blueToRed: [\n '#033270',\n '#1368aa',\n '#4091c9',\n '#9dcee2',\n '#fedfd4',\n '#f29479',\n '#f26a4f',\n '#ef3c2d',\n '#cb1b16',\n '#65010c',\n ],\n greenToYellow: [\n '#007f5f',\n '#2b9348',\n '#55a630',\n '#80b918',\n '#aacc00',\n '#bfd200',\n '#d4d700',\n '#dddf00',\n '#eeef20',\n '#ffff3f',\n ],\n pastel: [\n '#fec5bb',\n '#fcd5ce',\n '#fae1dd',\n '#f8edeb',\n '#e8e8e4',\n '#d8e2dc',\n '#ece4db',\n '#ffe5d9',\n '#ffd7ba',\n '#fec89a',\n ],\n retro: [\n '#f94144',\n '#f3722c',\n '#f8961e',\n '#f9844a',\n '#f9c74f',\n '#90be6d',\n '#43aa8b',\n '#4d908e',\n '#577590',\n '#277da1',\n ],\n contrastRetro: [\n '#001219',\n '#005f73',\n '#0a9396',\n '#94d2bd',\n '#e9d8a6',\n '#ee9b00',\n '#ca6702',\n '#bb3e03',\n '#ae2012',\n '#9b2226',\n ],\n fadedRainbow: ['#ffadad', '#ffd6a5', '#fdffb6', '#caffbf', '#9bf6ff', '#a0c4ff', '#bdb2ff', '#ffc6ff', '#fffffc'],\n pastelRainbow: [\n '#54478c',\n '#2c699a',\n '#048ba8',\n '#0db39e',\n '#16db93',\n '#83e377',\n '#b9e769',\n '#efea5a',\n '#f1c453',\n '#f29e4c',\n ],\n};\n\nexport type ColorPaletteOptions = keyof typeof colorPalettes;\n\nconst defaultColor = '#0A3653';\n\n/**\n * @ignore\n */\nexport const geometryFillSpec: LayerSpecTemplate<FillLayerSpecification> = {\n type: 'fill',\n paint: {\n 'fill-color': ['coalesce', ['get', GEOMETRY_COLOR_PROP], defaultColor],\n 'fill-opacity': 0.15,\n 'fill-antialias': false,\n },\n};\n\n/**\n * @ignore\n */\nexport const geometryOutlineSpec: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n paint: {\n 'line-color': defaultColor,\n 'line-opacity': 0.45,\n 'line-width': 2,\n },\n};\n","import { bboxCenter, bboxFromCoordsArray, generateId, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { Feature, FeatureCollection, GeoJsonProperties, MultiPolygon, Point, Polygon, Position } from 'geojson';\nimport { isNil } from 'lodash-es';\nimport type { DataDrivenPropertyValueSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { SymbolLayerSpecWithoutSource } from '../shared';\nimport { MAP_BOLD_FONT } from '../shared/layers/commonLayerProps';\nimport type { ColorPaletteOptions } from './layers/geometryLayers';\nimport { colorPalettes, geometryFillSpec, geometryOutlineSpec } from './layers/geometryLayers';\nimport type { GeometriesModuleConfig } from './types/geometriesModuleConfig';\nimport type { DisplayGeometryProps, ExtraGeometryDisplayProps } from './types/geometryDisplayProps';\nimport { GEOMETRY_TITLE_PROP } from './types/geometryDisplayProps';\n\n/**\n * Builds Geometry layer specifications for fill and outline layers.\n * @ignore\n */\nexport const buildGeometryLayerSpecs = (\n fillLayerId: string,\n outlineLayerId: string,\n config?: GeometriesModuleConfig,\n): [SymbolLayerSpecWithoutSource, SymbolLayerSpecWithoutSource] => {\n const colorConfig = config?.colorConfig;\n const lineConfig = config?.lineConfig;\n\n const fillLayerSpec = {\n ...geometryFillSpec,\n id: fillLayerId,\n paint: {\n ...geometryFillSpec.paint,\n ...(!isNil(colorConfig?.fillOpacity) && { 'fill-opacity': colorConfig?.fillOpacity }),\n ...(colorConfig?.fillColor && { 'fill-color': ['get', 'color'] }),\n },\n } as unknown as SymbolLayerSpecWithoutSource;\n\n const outlineLayerSpec = {\n ...geometryOutlineSpec,\n id: outlineLayerId,\n paint: {\n ...geometryOutlineSpec.paint,\n ...(!isNil(lineConfig?.lineColor) && { 'line-color': lineConfig?.lineColor }),\n ...(!isNil(lineConfig?.lineWidth) && { 'line-width': lineConfig?.lineWidth }),\n ...(!isNil(lineConfig?.lineOpacity) && { 'line-opacity': lineConfig?.lineOpacity }),\n },\n } as unknown as SymbolLayerSpecWithoutSource;\n\n return [fillLayerSpec, outlineLayerSpec];\n};\n\n/**\n * Build geometry Title. The type can be a string or a Maplibre expression.\n * @param feature - Geometry\n * @param config - Geometry module configuration\n * @returns\n */\nconst buildTitle = (\n feature: Feature<Polygon | MultiPolygon, DisplayGeometryProps | GeoJsonProperties>,\n config: GeometriesModuleConfig,\n): DataDrivenPropertyValueSpecification<string> | string | undefined => {\n if (config.textConfig?.textField) {\n return config.textConfig.textField;\n }\n\n return feature.properties?.address?.freeformAddress;\n};\n\n/**\n * Builds a geometry color string or MapLibre expression.\n * @param config - Geometry module configuration\n * @param index - Number to use as index to pick color from palette option\n */\nconst buildColor = (\n config: GeometriesModuleConfig,\n index: number,\n): DataDrivenPropertyValueSpecification<string> | string | undefined => {\n const color = config?.colorConfig?.fillColor;\n\n if (typeof color === 'string' && colorPalettes[color as ColorPaletteOptions]) {\n const palette = colorPalettes[color as ColorPaletteOptions];\n return palette[index % palette.length];\n }\n\n return color;\n};\n\n/**\n * Build geometry layer specification for title\n * @param layerID\n * @param config\n * @returns\n * @ignore\n */\nexport const buildGeometryTitleLayerSpec = (\n layerId: string,\n config?: GeometriesModuleConfig,\n): Omit<SymbolLayerSpecification, 'source'> => {\n const textConfig = config?.textConfig;\n\n return {\n type: 'symbol',\n id: layerId,\n layout: {\n 'text-field': ['get', GEOMETRY_TITLE_PROP],\n ...(textConfig?.textField && { 'text-field': textConfig.textField }),\n 'text-padding': 5,\n 'text-size': 12,\n 'text-font': [MAP_BOLD_FONT],\n 'symbol-placement': 'point',\n },\n paint: {\n 'text-color': '#333333',\n 'text-halo-color': '#FFFFFF',\n 'text-halo-width': ['interpolate', ['linear'], ['zoom'], 6, 1, 10, 1.5],\n 'text-translate-anchor': 'viewport',\n },\n };\n};\n\n/**\n * Prepare geometry for display.\n * If colorConfig is set, it will apply the property \"color\" to \"properties\" in each feature.\n * @param geometry\n * @param config\n * @returns\n * @ignore\n */\nexport const prepareGeometryForDisplay = (\n geometry: PolygonFeatures<GeoJsonProperties | DisplayGeometryProps>,\n config: GeometriesModuleConfig = {},\n): PolygonFeatures<ExtraGeometryDisplayProps> => ({\n ...geometry,\n features: geometry.features.map((feature, index) => {\n const title = feature.properties?.title ?? buildTitle(feature, config);\n const color = feature.properties?.color ?? buildColor(config, index);\n return {\n ...feature,\n properties: {\n ...feature.properties,\n ...(title && { title }),\n ...(color && { color }),\n id: feature.properties?.id ?? generateId(),\n },\n };\n }),\n});\n\n/**\n * Find the biggest array length inside an array.\n * Used to find the biggest array in a Multi-Polygon feature.\n * @param coordinates\n * @returns\n */\nconst getLongestArray = (coordinates: Position[][][]) =>\n coordinates.flat().reduce((result, coord) => (coord.length > result.length ? coord : result), []);\n\n/**\n * Create a Feature<Point> with coordinates where title will be placed.\n * If feature properties contains a coordinates value, it will use it.\n * In case there is not coordinates value, it will get the biggest Polygon inside a feature and calculate\n * the bounding box for those coordinates and finally calculate the bounding box center to place the title.\n * @param geometries\n * @returns\n * @ignore\n */\nexport const prepareTitleForDisplay = (geometries: PolygonFeatures): FeatureCollection<Point> => {\n const features = geometries.features.map((feature) => {\n let coordinates: Position[] | Position | null;\n\n if (feature.properties?.placeCoordinates) {\n coordinates = feature.properties?.placeCoordinates;\n } else if (feature.geometry.type === 'MultiPolygon') {\n const biggestPolygon = getLongestArray(feature.geometry.coordinates);\n const bbox = bboxFromCoordsArray(biggestPolygon);\n coordinates = (bbox && bboxCenter(bbox)) || null;\n } else {\n coordinates = feature.geometry.coordinates.flat();\n }\n\n const id = feature.id ?? feature.properties?.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...feature.properties,\n id, // we need id in properties due to promoteId feature\n },\n } as Feature<Point>;\n });\n\n return { type: 'FeatureCollection', bbox: geometries.bbox, features };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { FeatureCollection, Point } from 'geojson';\nimport type { SymbolLayerSpecification } from 'maplibre-gl';\nimport type { SymbolLayerSpecWithoutSource, ToBeAddedLayerSpec } from '../shared';\nimport { AbstractMapModule, EventsModule, GeoJSONSourceWithLayers, mapStyleLayerIDs } from '../shared';\nimport { changeLayerProps, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport {\n buildGeometryLayerSpecs,\n buildGeometryTitleLayerSpec,\n prepareGeometryForDisplay,\n prepareTitleForDisplay,\n} from './prepareGeometryForDisplay';\nimport type {\n GeometriesModuleConfig,\n GeometryBeforeLayerConfig,\n GeometryTextConfig,\n} from './types/geometriesModuleConfig';\n\n/**\n * IDs of sources and layers from a geometry module.\n */\ntype GeometrySourcesWithLayers = {\n geometry: GeoJSONSourceWithLayers<PolygonFeatures>;\n geometryLabel: GeoJSONSourceWithLayers<FeatureCollection<Point>>;\n};\n\n/**\n * Geometries Module for displaying polygon areas with custom styling on the map.\n *\n * This module enables visualization of geographic areas (polygons) with customizable\n * colors, borders, and labels. Ideal for displaying search results, administrative\n * boundaries, service areas, or any polygon-based geographic data.\n *\n * @remarks\n * **Features:**\n * - Display single or multiple polygon geometries\n * - Customizable fill colors and opacity\n * - Configurable borders (color, width, opacity)\n * - Optional text labels for geometries\n * - Support for data-driven styling via MapLibre expressions\n * - Layer ordering control\n * - Event handling for user interactions\n *\n * **Data Format:**\n * - Accepts GeoJSON Polygon and MultiPolygon features\n * - Supports FeatureCollection for multiple geometries\n * - Compatible with TomTom Search API geometry results\n *\n * **Styling:**\n * - Use predefined color palettes or custom colors\n * - Apply MapLibre expressions for dynamic styling\n * - Per-feature styling via feature properties\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { GeometriesModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Initialize module\n * const geometriesModule = await GeometriesModule.get(map);\n *\n * // Display a polygon\n * await geometriesModule.show({\n * type: 'Feature',\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.88, 52.37], [4.89, 52.37], [4.89, 52.38], [4.88, 52.38], [4.88, 52.37]]]\n * },\n * properties: {\n * title: 'Area of Interest'\n * }\n * });\n * ```\n *\n * @example\n * Custom styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * fillColor: '#FF5733',\n * fillOpacity: 0.3\n * },\n * lineConfig: {\n * lineColor: '#C70039',\n * lineWidth: 3\n * },\n * textConfig: {\n * textField: ['get', 'name']\n * }\n * });\n *\n * await geometriesModule.show(polygonFeatures);\n * ```\n *\n * @example\n * Multiple geometries with different colors:\n * ```typescript\n * await geometriesModule.show({\n * type: 'FeatureCollection',\n * features: [\n * {\n * type: 'Feature',\n * geometry: { type: 'Polygon', coordinates: [...] },\n * properties: { color: '#FF0000', title: 'Red Zone' }\n * },\n * {\n * type: 'Feature',\n * geometry: { type: 'Polygon', coordinates: [...] },\n * properties: { color: '#00FF00', title: 'Green Zone' }\n * }\n * ]\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * geometriesModule.events.on('click', (feature, lngLat) => {\n * console.log('Clicked geometry:', feature.properties.title);\n * console.log('At coordinates:', lngLat);\n * });\n *\n * geometriesModule.events.on('hover', (feature) => {\n * showTooltip(feature.properties.title);\n * });\n * ```\n *\n * @see [Geometries Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/geometries)\n *\n * @group Geometries\n */\nexport class GeometriesModule extends AbstractMapModule<GeometrySourcesWithLayers, GeometriesModuleConfig> {\n private static lastInstanceIndex = -1;\n\n private titleLayerSpecs!: SymbolLayerSpecWithoutSource;\n private geometryFillLayerSpecs!: SymbolLayerSpecWithoutSource;\n private geometryOutlineLayerSpecs!: SymbolLayerSpecWithoutSource;\n\n private sourceID!: string;\n private fillLayerID!: string;\n private outlineLayerID!: string;\n\n private titleSourceID!: string;\n private titleLayerID!: string;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n *\n * @remarks\n * **Configuration Options:**\n * - `colorConfig`: Fill color and opacity settings\n * - `lineConfig`: Border/outline styling\n * - `textConfig`: Label display configuration\n * - `beforeLayerConfig`: Layer ordering (place above/below other layers)\n *\n * **Multiple Instances:**\n * You can create multiple GeometriesModule instances on the same map,\n * each managing different sets of geometries with different styles.\n *\n * @example\n * Default initialization:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map);\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * fillColor: 'blue',\n * fillOpacity: 0.25\n * },\n * lineConfig: {\n * lineColor: 'darkblue',\n * lineWidth: 2,\n * lineOpacity: 0.8\n * },\n * textConfig: {\n * textField: ['get', 'title']\n * },\n * beforeLayerConfig: 'top'\n * });\n * ```\n *\n * @example\n * Data-driven styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * // Color based on feature properties\n * fillColor: [\n * 'match',\n * ['get', 'type'],\n * 'residential', '#FFEB3B',\n * 'commercial', '#2196F3',\n * 'industrial', '#9E9E9E',\n * '#E0E0E0' // default\n * ],\n * fillOpacity: 0.4\n * }\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: GeometriesModuleConfig): Promise<GeometriesModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new GeometriesModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: GeometriesModuleConfig) {\n super('geojson', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: GeometriesModuleConfig, restore?: boolean): GeometrySourcesWithLayers {\n if (!restore) {\n GeometriesModule.lastInstanceIndex++;\n this.sourceID = `geometry-${GeometriesModule.lastInstanceIndex}`;\n this.titleSourceID = `geometryTitle-${GeometriesModule.lastInstanceIndex}`;\n const layerIdPrefix = `geometry-${GeometriesModule.lastInstanceIndex}`;\n this.fillLayerID = `${layerIdPrefix}_Fill`;\n this.outlineLayerID = `${layerIdPrefix}_Outline`;\n this.titleLayerID = `${layerIdPrefix}_Title`;\n }\n\n const [geometryFillSpec, geometryOutlineSpec] = buildGeometryLayerSpecs(\n this.fillLayerID,\n this.outlineLayerID,\n config,\n );\n const titleLayerSpec = buildGeometryTitleLayerSpec(this.titleLayerID, config);\n this.titleLayerSpecs = titleLayerSpec;\n this.geometryFillLayerSpecs = geometryFillSpec;\n this.geometryOutlineLayerSpecs = geometryOutlineSpec;\n\n return {\n geometry: new GeoJSONSourceWithLayers(this.mapLibreMap, this.sourceID, [\n { ...geometryFillSpec },\n { ...geometryOutlineSpec },\n ]),\n geometryLabel: new GeoJSONSourceWithLayers(this.mapLibreMap, this.titleSourceID, [\n titleLayerSpec as ToBeAddedLayerSpec<SymbolLayerSpecification>,\n ]),\n };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: GeometriesModuleConfig | undefined) {\n if (config?.textConfig || config?.colorConfig || config?.lineConfig) {\n this.updateLayerAndData(config);\n }\n if (config?.beforeLayerConfig) {\n this.moveBeforeLayer(config.beforeLayerConfig);\n }\n return config;\n }\n\n private moveBeforeLayerID(beforeLayerId?: string) {\n for (const layer of this.sourcesWithLayers.geometry.sourceAndLayerIDs.layerIDs) {\n this.mapLibreMap.moveLayer(layer, beforeLayerId);\n }\n }\n\n /**\n * Positions the geometry layers relative to other map layers.\n *\n * @param layerConfig - Layer positioning configuration.\n * Can be `'top'` to place above all layers, or a specific layer ID.\n *\n * @remarks\n * **Use Cases:**\n * - Place geometries above base map but below labels\n * - Ensure geometries appear above/below specific features\n * - Control visual hierarchy of multiple data layers\n *\n * **Available Layer IDs:**\n * Use predefined layer IDs from `mapStyleLayerIDs` or custom layer IDs.\n *\n * @example\n * ```typescript\n * import { mapStyleLayerIDs } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Place below labels\n * geometries.moveBeforeLayer(mapStyleLayerIDs.lowestLabel);\n *\n * // Place on top\n * geometries.moveBeforeLayer('top');\n * ```\n */\n moveBeforeLayer(layerConfig: GeometryBeforeLayerConfig) {\n this.config = { ...this.config, beforeLayerConfig: layerConfig };\n this.moveBeforeLayerID(layerConfig === 'top' ? this.titleLayerID : mapStyleLayerIDs[layerConfig]);\n }\n\n /**\n * Updates the text/label configuration for displayed geometries.\n *\n * @param textConfig - New text configuration settings.\n *\n * @remarks\n * **Configuration:**\n * - `textField`: MapLibre expression for label text content\n * - Supports dynamic text based on feature properties\n * - Changes apply to currently shown and future geometries\n *\n * @example\n * ```typescript\n * // Show feature property as label\n * geometries.applyTextConfig({\n * textField: ['get', 'name']\n * });\n *\n * // Conditional labels\n * geometries.applyTextConfig({\n * textField: [\n * 'case',\n * ['has', 'label'],\n * ['get', 'label'],\n * ['get', 'title']\n * ]\n * });\n * ```\n */\n applyTextConfig(textConfig: GeometryTextConfig) {\n const config = { ...this.config, textConfig };\n this.updateLayerAndData(config);\n // TODO: is this consistent with _applyConfig?\n this.sourcesWithLayers.geometryLabel.show(\n prepareTitleForDisplay(this.sourcesWithLayers.geometry.shownFeatures),\n );\n this.config = config;\n }\n\n private updateLayerAndData(config: GeometriesModuleConfig) {\n const [geometryFillSpec, geometryOutlineSpec] = buildGeometryLayerSpecs(\n this.fillLayerID,\n this.outlineLayerID,\n config,\n );\n const newTitleLayerSpecs = buildGeometryTitleLayerSpec(this.titleLayerID, config);\n\n changeLayerProps(geometryFillSpec, this.geometryFillLayerSpecs, this.mapLibreMap);\n changeLayerProps(geometryOutlineSpec, this.geometryOutlineLayerSpecs, this.mapLibreMap);\n changeLayerProps(newTitleLayerSpecs, this.titleLayerSpecs, this.mapLibreMap);\n\n this.geometryFillLayerSpecs = geometryFillSpec;\n this.geometryOutlineLayerSpecs = geometryOutlineSpec;\n this.titleLayerSpecs = newTitleLayerSpecs;\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previousShownFeatures = this.sourcesWithLayers.geometry.shownFeatures;\n this.initSourcesWithLayers(this.config, true);\n this.config && this._applyConfig(this.config);\n this.show(previousShownFeatures);\n }\n\n /**\n * Displays the given polygon geometries on the map.\n *\n * @param geometries - Polygon features to display. Can be a single Feature,\n * array of Features, or a FeatureCollection.\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown geometries\n * - Applies current module styling configuration\n * - Waits for module to be ready before displaying\n * - Automatically handles both Polygon and MultiPolygon types\n *\n * **Feature Properties:**\n * - `title`: Used for labels if text config is set\n * - `color`: Override fill color per feature\n * - Custom properties accessible in styling expressions\n *\n * @example\n * Single polygon:\n * ```typescript\n * await geometries.show({\n * type: 'Feature',\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.88, 52.37], [4.89, 52.37], [4.89, 52.38], [4.88, 52.37]]]\n * },\n * properties: {\n * title: 'Amsterdam Center',\n * color: '#FF5733'\n * }\n * });\n * ```\n *\n * @example\n * Multiple polygons:\n * ```typescript\n * await geometries.show({\n * type: 'FeatureCollection',\n * features: [\n * { type: 'Feature', geometry: {...}, properties: {...} },\n * { type: 'Feature', geometry: {...}, properties: {...} }\n * ]\n * });\n * ```\n *\n * @example\n * From search API response:\n * ```typescript\n * import { search } from '@tomtom-international/maps-sdk-js/services';\n *\n * const result = await search.geometrySearch({\n * query: 'Amsterdam',\n * geometryList: [{ type: 'CIRCLE', position: [52.37, 4.89], radius: 5000 }]\n * });\n *\n * if (result.results[0].dataSources?.geometry) {\n * await geometries.show(result.results[0].dataSources.geometry);\n * }\n * ```\n */\n async show(geometries: PolygonFeatures) {\n await this.waitUntilModuleReady();\n const geometry = this.sourcesWithLayers.geometry;\n geometry.show(prepareGeometryForDisplay(geometries, this.config));\n this.sourcesWithLayers.geometryLabel.show(prepareTitleForDisplay(geometry.shownFeatures));\n }\n\n /**\n * Removes all geometries from the map.\n *\n * @remarks\n * - Clears both geometry layers and labels\n * - Does not reset styling configuration\n * - Module remains initialized and ready for new data\n *\n * @example\n * ```typescript\n * // Clear displayed geometries\n * await geometries.clear();\n *\n * // Show new geometries\n * await geometries.show(newGeometries);\n * ```\n */\n async clear() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.geometry.clear();\n }\n\n /**\n * Returns the currently shown geometries.\n *\n * @returns The geometries currently displayed on the map.\n *\n * @remarks\n * Returns the exact data that was passed to the `show()` method.\n *\n * @example\n * ```typescript\n * const shown = geometriesModule.getShown();\n * console.log(`Geometries: ${shown.geometry.features.length}`);\n * ```\n */\n getShown() {\n return {\n geometry: this.sourcesWithLayers.geometry.shownFeatures,\n geometryLabel: this.sourcesWithLayers.geometryLabel.shownFeatures,\n };\n }\n\n /**\n * Gets the events interface for handling user interactions with geometries.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a geometry\n * - `contextmenu`: User right-clicks on a geometry\n * - `hover`: Mouse enters a geometry\n * - `long-hover`: Mouse hovers over geometry for extended time\n *\n * **Event Features:**\n * - Receive the original feature data passed to `show()`\n * - Access feature properties and geometry\n * - Get click/hover coordinates\n *\n * @example\n * Basic event handling:\n * ```typescript\n * geometries.events.on('click', (feature, lngLat) => {\n * console.log('Clicked:', feature.properties);\n * console.log('Location:', lngLat);\n * });\n * ```\n *\n * @example\n * Multiple handlers:\n * ```typescript\n * // Highlight on hover\n * geometries.events.on('hover', (feature) => {\n * highlightGeometry(feature.id);\n * });\n *\n * // Show details on click\n * geometries.events.on('click', (feature) => {\n * showDetailPanel(feature.properties);\n * });\n *\n * // Context menu\n * geometries.events.on('contextmenu', (feature, lngLat) => {\n * showContextMenu(lngLat, feature);\n * });\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.geometry, this.config?.events);\n }\n}\n","import { AbstractMapModule, EventsModule, HILLSHADE_SOURCE_ID, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport type { HillshadeModuleConfig } from '.';\n\n/**\n * IDs of sources and layers for hillshade module.\n */\ntype HillshadeSourcesWithLayers = {\n hillshade: StyleSourceWithLayers;\n};\n\n/**\n * Map module for displaying terrain shading (hillshade).\n *\n * The HillshadeModule adds realistic terrain depth perception to the map by rendering\n * shadow and highlight effects based on elevation data. This enhances the 3D appearance\n * of mountainous and hilly terrain.\n *\n * @remarks\n * **Features:**\n * - Realistic terrain shading\n * - Uses vector tile elevation data\n * - Lightweight performance impact\n * - Seamlessly integrates with other map layers\n * - Toggle visibility on/off\n *\n * **Common Use Cases:**\n * - Outdoor recreation maps (hiking, skiing)\n * - Geographic/topographic applications\n * - Environmental visualization\n * - Landscape planning tools\n *\n * @example\n * ```typescript\n * // Create the module with hillshade visible\n * const hillshade = await HillshadeModule.getInstance(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * hillshade.setVisible(false);\n *\n * // Listen to events\n * hillshade.events.on('click', (feature, lngLat) => {\n * console.log('Clicked hillshade at:', lngLat);\n * });\n * ```\n *\n * @see [Hillshade Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/hillshade)\n *\n * @group Hillshade\n */\nexport class HillshadeModule extends AbstractMapModule<HillshadeSourcesWithLayers, HillshadeModuleConfig> {\n /**\n * Retrieves a HillshadeModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization and visibility.\n *\n * @returns A promise that resolves to the initialized HillshadeModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `ensureAddedToStyle`: Auto-add hillshade to style if missing\n *\n * **Style Requirement:**\n * - Hillshade must be included in the map style or added via `ensureAddedToStyle`\n * - Some styles may not support hillshade (e.g., satellite)\n *\n * @throws Error if hillshade source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map);\n * ```\n *\n * @example\n * Auto-add to style if missing:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map, {\n * visible: true\n * });\n * ```\n *\n * @example\n * Start hidden:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map, {\n * visible: false\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: HillshadeModuleConfig): Promise<HillshadeModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, HILLSHADE_SOURCE_ID, 'hillshade');\n return new HillshadeModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: HillshadeModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const hillshadeSource = this.mapLibreMap.getSource(HILLSHADE_SOURCE_ID);\n if (!hillshadeSource) {\n throw notInTheStyle(`init ${HillshadeModule.name} with source ID ${HILLSHADE_SOURCE_ID}`);\n }\n return { hillshade: new StyleSourceWithLayers(this.mapLibreMap, hillshadeSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: HillshadeModuleConfig | undefined) {\n this.setVisible(config?.visible ?? false);\n return config;\n }\n\n /**\n * Sets the visibility of the hillshade layer.\n *\n * @param visible - `true` to show hillshade, `false` to hide it.\n *\n * @remarks\n * Changes are applied immediately if the map is ready.\n *\n * @example\n * ```typescript\n * hillshade.setVisible(true); // Show terrain shading\n * hillshade.setVisible(false); // Hide terrain shading\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = {\n ...this.config,\n visible,\n };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.hillshade.setLayersVisible(visible);\n }\n }\n\n /**\n * Checks if the hillshade layer is currently visible.\n *\n * @returns `true` if visible, `false` if hidden.\n *\n * @example\n * ```typescript\n * if (hillshade.isVisible()) {\n * console.log('Terrain shading is active');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.hillshade.isAnyLayerVisible();\n }\n\n /**\n * Gets the events interface for handling user interactions with hillshade.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on the hillshade layer\n * - `contextmenu`: User right-clicks\n * - `hover`: Mouse enters hillshade area\n * - `long-hover`: Extended hover\n *\n * @example\n * ```typescript\n * hillshade.events.on('click', (feature, lngLat) => {\n * console.log('Clicked terrain at:', lngLat);\n * });\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.hillshade, this.config?.events);\n }\n}\n","import type { GlobalConfig } from '@tomtom-org/maps-sdk/core';\nimport type { MapOptions, StyleSpecification } from 'maplibre-gl';\nimport type { MapEventsConfig } from './mapEventsConfig';\n\n/**\n * Array of all available standard style identifiers.\n *\n * This constant provides the complete list of TomTom-hosted standard map styles\n * that can be used when initializing a map. It serves as the source of truth for\n * valid style IDs and is used to derive the {@link StandardStyleID} type.\n *\n * @remarks\n * Use this array when you need to:\n * - Iterate over all available styles (e.g., building a style picker UI)\n * - Validate if a style ID is a standard style\n * - Display available options to users\n *\n * The array is defined as `const` with `as const` assertion to ensure type safety\n * and prevent modifications at runtime.\n *\n * @example\n * ```typescript\n * import { standardStyleIDs } from '@tomtom-org/maps-sdk/map';\n *\n * // Build a dropdown menu with all standard styles\n * const styleSelector = document.getElementById('style-selector');\n * standardStyleIDs.forEach((styleId) => {\n * const option = new Option(styleId);\n * styleSelector.add(option);\n * });\n *\n * // Check if a style ID is valid\n * const isValidStyle = (id: string): boolean => {\n * return standardStyleIDs.includes(id as any);\n * };\n * ```\n *\n * @see {@link StandardStyleID} - The type derived from this array\n *\n * @group Map Style\n */\nexport const standardStyleIDs = [\n 'standardLight',\n 'standardDark',\n // TODO: driving styles not supported in Orbis for now\n 'drivingLight',\n 'drivingDark',\n 'monoLight',\n 'monoDark',\n 'satellite',\n] as const;\n\n/**\n * Identifier for a TomTom-hosted standard map style.\n *\n * Standard styles are officially maintained by TomTom and provide consistent,\n * professionally designed map appearances.\n *\n * @remarks\n * Available styles:\n * - `standardLight`: Default light theme with full detail\n * - `standardDark`: Dark theme for low-light environments\n * - `drivingLight`: Optimized for in-car navigation (light)\n * - `drivingDark`: Optimized for in-car navigation (dark)\n * - `monoLight`: Minimalist monochrome light theme\n * - `monoDark`: Minimalist monochrome dark theme\n * - `satellite`: Satellite imagery basemap\n *\n * @example\n * ```typescript\n * const styleId: StandardStyleID = 'standardLight';\n * ```\n *\n * @group Map Style\n */\nexport type StandardStyleID = (typeof standardStyleIDs)[number];\n\n/**\n * Configuration for a TomTom standard map style.\n *\n * Provides options to customize which modules are included and which style version to use.\n *\n * @example\n * ```typescript\n * // Standard style with all default modules\n * const style: StandardStyle = {\n * id: 'standardLight'\n * };\n *\n * // Exclude traffic modules\n * const styleNoTraffic: StandardStyle = {\n * id: 'standardLight',\n * include: ['hillshade'] // Only include hillshade, exclude traffic\n * };\n *\n * // Use specific style version\n * const versionedStyle: StandardStyle = {\n * id: 'standardDark',\n * version: '1.0.0'\n * };\n * ```\n *\n * @group Map Style\n */\nexport type StandardStyle = {\n /**\n * Standard style identifier.\n *\n * Determines the visual appearance of the map.\n */\n id?: StandardStyleID;\n /**\n * Modules to include when loading the style.\n * * Use this to selectively enable only needed modules for better performance.\n *\n * If not specified, all available modules are included by default.\n * If an empty array is provided, no optional modules will be included.\n *\n * @default All available modules\n *\n * @remarks\n * Available modules:\n * - `trafficIncidents`: Real-time traffic incidents (accidents, closures)\n * - `trafficFlow`: Real-time traffic flow visualization\n * - `hillshade`: Terrain elevation shading\n *\n * @example\n * ```typescript\n * // Include only traffic modules\n * include: ['trafficIncidents', 'trafficFlow']\n *\n * // Include only hillshade\n * include: ['hillshade']\n *\n * // Include no optional modules\n * include: []\n * ```\n */\n include?: StyleModule[];\n /**\n * Style version to load.\n *\n * Allows pinning to a specific style version for consistency.\n * If not specified, uses the latest SDK-supported version.\n *\n * @default Latest SDK-supported version\n *\n * @example\n * ```typescript\n * version: '1.0.0'\n * ```\n */\n version?: string;\n};\n\n/**\n * Configuration for a custom map style.\n *\n * Allows using your own map style either via URL or direct JSON specification.\n *\n * @remarks\n * Use custom styles for:\n * - Branded map appearances\n * - Specialized use cases (indoor maps, thematic maps)\n * - Integration with custom tile servers\n *\n * @example\n * ```typescript\n * // Load from URL\n * const urlStyle: CustomStyle = {\n * url: 'https://example.com/my-custom-style.json'\n * };\n *\n * // Direct JSON specification\n * const jsonStyle: CustomStyle = {\n * json: {\n * version: 8,\n * sources: { ... },\n * layers: [ ... ]\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport type CustomStyle = {\n /**\n * URL to a MapLibre/Mapbox style JSON.\n *\n * The URL should not include the API key - it will be automatically added.\n * Mutually exclusive with the `json` property.\n *\n * @example\n * ```typescript\n * url: 'https://api.tomtom.com/style/1/style/my-custom-style'\n * ```\n */\n url?: string;\n /**\n * Direct style specification as JSON.\n *\n * Provide the complete MapLibre Style Specification object.\n * Mutually exclusive with the `url` property.\n *\n * @see [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/)\n *\n * @example\n * ```typescript\n * json: {\n * version: 8,\n * sources: {\n * 'my-source': { type: 'vector', url: '...' }\n * },\n * layers: [\n * { id: 'background', type: 'background', paint: { 'background-color': '#f0f0f0' } }\n * ]\n * }\n * ```\n */\n json?: StyleSpecification;\n};\n\n/**\n * Array of all available style modules.\n *\n * @group Map Style\n */\nexport const styleModules = ['trafficIncidents', 'trafficFlow', 'hillshade'] as const;\n\n/**\n * Optional map modules that can be included with a style.\n *\n * @remarks\n * - `trafficIncidents`: Shows real-time traffic incidents on the map\n * - `trafficFlow`: Shows real-time traffic flow with color-coded speeds\n * - `hillshade`: Adds terrain elevation shading for topographic context\n *\n * @group Map Style\n */\nexport type StyleModule = (typeof styleModules)[number];\n\n/**\n * Map style specification for initialization.\n *\n * Defines which map style to load and how it should be configured.\n * Supports standard TomTom styles or custom styles.\n *\n * @remarks\n * Three input formats:\n * 1. **Simple ID**: Just pass a standard style ID string\n * 2. **Standard style object**: Use a TomTom style with custom configuration\n * 3. **Custom style object**: Load your own style from URL or JSON\n *\n * @example\n * ```typescript\n * // 1. Simple standard style\n * style: 'standardLight'\n *\n * // 2. Standard style with configuration\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * }\n *\n * // 3. Custom style from URL\n * style: {\n * type: 'custom',\n * url: 'https://example.com/style.json'\n * }\n *\n * // 4. Custom style from JSON\n * style: {\n * type: 'custom',\n * json: { version: 8, sources: {...}, layers: [...] }\n * }\n * ```\n *\n * @group Map Style\n */\nexport type StyleInput = StandardStyleID | (StandardStyle & { type: 'standard' }) | (CustomStyle & { type: 'custom' });\n\n/**\n * MapLibre-specific options for advanced map configuration.\n *\n * Extends MapLibre GL JS MapOptions, excluding style and attribution control\n * which are handled by the TomTom SDK.\n *\n * @remarks\n * Includes options for:\n * - Initial viewport (center, zoom, bearing, pitch)\n * - Interaction controls (zoom, rotation, drag)\n * - Rendering options (antialiasing, terrain)\n * - Localization and accessibility\n *\n * @see [MapLibre MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/)\n *\n * @group Map\n */\nexport type MapLibreOptions = Omit<MapOptions, 'style' | 'attributionControl'>;\n\n/**\n * Parameters for initializing a TomTom map instance.\n *\n * Combines global SDK configuration with map-specific settings like style, events, and MapLibre options.\n * All GlobalConfig properties (key, baseURL, etc.) are optional and will be merged from global configuration.\n * Only mapLibre.container is strictly required.\n *\n * @example\n * ```typescript\n * const mapParams: TomTomMapParams = {\n * key: 'your-api-key',\n * style: 'standardLight',\n * events: {\n * onClick: (event) => console.log('Map clicked', event)\n * },\n * mapLibre: {\n * container: 'map-container',\n * center: [4.9041, 52.3676],\n * zoom: 12\n * }\n * };\n * ```\n *\n * @group Map\n */\nexport type TomTomMapParams = Partial<GlobalConfig> & {\n /**\n * Map style to load.\n *\n * If not specified, defaults to 'standardLight'.\n *\n * @default 'standardLight'\n *\n * @example\n * ```typescript\n * // Use dark theme\n * style: 'standardDark'\n *\n * // Custom configuration\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow']\n * }\n * ```\n */\n style?: StyleInput;\n\n /**\n * Event handler configuration for map interactions.\n *\n * Define callbacks for various map events like clicks, hovers, and movements.\n *\n * @example\n * ```typescript\n * events: {\n * onClick: (event) => {\n * console.log('Clicked at', event.lngLat);\n * },\n * onMoveEnd: () => {\n * console.log('Map moved to', map.getCenter());\n * }\n * }\n * ```\n */\n events?: MapEventsConfig;\n\n /**\n * MapLibre-specific options for map configuration.\n *\n * Includes options for viewport settings, interaction controls, rendering options, and more.\n *\n * @example\n * ```typescript\n * mapLibre: {\n * container: 'map',\n * center: [4.9041, 52.3676],\n * zoom: 12,\n * pitch: 45,\n * bearing: -17.6,\n * antialias: true,\n * maxZoom: 18,\n * minZoom: 8\n * }\n * ```\n */\n mapLibre: MapLibreOptions;\n};\n\n/**\n * Complete TomTom map parameters after merging with global configuration.\n * This type represents the fully resolved configuration used internally by the SDK.\n *\n * @ignore\n */\nexport type InternalTomTomMapParams = GlobalConfig & TomTomMapParams;\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * Main route line foreground color.\n * @ignore\n */\nexport const ROUTE_LINE_FOREGROUND_COLOR = '#36A8F0';\n\n/**\n * Main route outline color.\n * @ignore\n */\nexport const ROUTE_LINE_OUTLINE_COLOR = '#105287';\n\n/**\n * Deselected route line foreground color.\n * @ignore\n */\nexport const DESELECTED_FOREGROUND_COLOR = '#ABAFB3';\n\n/**\n * Deselected route line outline color.\n * @ignore\n */\nexport const DESELECTED_OUTLINE_COLOR = '#3C4956';\n\n/**\n * @ignore\n */\nexport const DESELECTED_SECONDARY_COLOR = '#727C85';\n\n/**\n * Main route line width based on zoom level.\n * @ignore\n */\nexport const ROUTE_LINE_FOREGROUND_WIDTH: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 1,\n 3,\n 5,\n 4,\n 10,\n 7,\n 18,\n 10,\n];\n\n/**\n * Used for showing/hiding layer depending on layer being part of selected route or not.\n *\n * @remarks\n * Add this to layers that depend on whether they are part of the selected route or not.\n *\n * @example:\n * filter: SELECTED_ROUTE_FILTER\n\n * @group Routing\n */\nexport const SELECTED_ROUTE_FILTER: ExpressionSpecification = ['==', ['get', 'routeState'], 'selected'];\n\n/**\n * Used for showing/hiding layer depending on layer being part of deselected route or not.\n *\n * @remarks\n * Add this to layers that depend on whether they are part of the deselected route or not.\n *\n * @example:\n * filter: DESELECTED_ROUTE_FILTER\n\n * @group Routing\n */\nexport const DESELECTED_ROUTE_FILTER: ExpressionSpecification = ['==', ['get', 'routeState'], 'deselected'];\n\n/**\n * @ignore\n */\nexport const MAJOR_DELAY_COLOR = '#AD0000';\n/**\n * @ignore\n */\nexport const MODERATE_DELAY_COLOR = '#FB2D09';\n/**\n * @ignore\n */\nexport const MINOR_DELAY_LABEL_COLOR = '#f58240';\n/**\n * @ignore\n */\nexport const UNKNOWN_DELAY_COLOR = '#000000';\n","import type {\n DataDrivenPropertyValueSpecification,\n FormattedSpecification,\n SymbolLayerSpecification,\n} from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { pinLayerBaseSpec } from '../../shared/layers/symbolLayers';\nimport { ChargingStopsConfig, ChargingStopTextConfig } from '../types/routeModuleConfig';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\nconst chargingStopTextField = (\n config: ChargingStopTextConfig | undefined,\n): DataDrivenPropertyValueSpecification<FormattedSpecification> => {\n if (config?.visible === false) {\n return '';\n }\n\n return (\n config?.title ?? [\n 'format',\n ['get', 'title'],\n '\\n',\n ['get', 'chargingPower'],\n ' • ',\n ['get', 'chargingDuration'],\n ]\n );\n};\n\n/**\n * @ignore\n * @see toDisplayChargingStops\n */\nexport const chargingStopSymbol = (\n config: ChargingStopsConfig | undefined,\n): LayerSpecTemplate<SymbolLayerSpecification> => {\n return {\n ...pinLayerBaseSpec,\n filter: SELECTED_ROUTE_FILTER,\n minzoom: 4,\n layout: {\n ...pinLayerBaseSpec.layout,\n 'text-field': chargingStopTextField(config?.text),\n },\n paint: {\n ...pinLayerBaseSpec.paint,\n },\n };\n};\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\nconst commonProps = {\n filter: SELECTED_ROUTE_FILTER,\n minzoom: 16,\n};\n\nconst commonLineProps: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonProps,\n type: 'line',\n layout: { 'line-cap': 'round' },\n};\n\n/**\n * @ignore\n */\nexport const instructionOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonLineProps,\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 16, 14, 22, 20],\n 'line-color': 'grey',\n },\n};\n\n/**\n * @ignore\n */\nexport const instructionLine: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonLineProps,\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 16, 12, 22, 17],\n 'line-color': 'white',\n },\n};\n\n/**\n * @ignore\n */\nexport const INSTRUCTION_ARROW_IMAGE_ID = 'instruction-arrow';\n\n/**\n * @ignore\n */\nexport const instructionArrow: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...commonProps,\n type: 'symbol',\n layout: {\n 'icon-allow-overlap': true,\n 'icon-image': INSTRUCTION_ARROW_IMAGE_ID, // Will be updated with instance suffix in config\n 'icon-rotation-alignment': 'map',\n 'icon-rotate': ['get', 'lastPointBearingDegrees'],\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 16, 1, 22, 2],\n },\n};\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { ROUTE_LINE_FOREGROUND_WIDTH, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeFerriesLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n 'line-color': '#6dc4ed',\n },\n};\n\n/**\n * @ignore\n */\nexport const routeFerriesSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 6,\n // zoom where the map POI naturally appears:\n maxzoom: 16.5,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-image': 'poi-ferry_terminal',\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 6, 0.8, 16.5, 1],\n // helps smooth the transition from along-route to map-poi, which also has a label in it:\n 'icon-ignore-placement': true,\n },\n};\n","import type { ExpressionSpecification, LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport {\n DESELECTED_FOREGROUND_COLOR,\n DESELECTED_OUTLINE_COLOR,\n DESELECTED_ROUTE_FILTER,\n ROUTE_LINE_FOREGROUND_COLOR,\n ROUTE_LINE_FOREGROUND_WIDTH,\n ROUTE_LINE_OUTLINE_COLOR,\n SELECTED_ROUTE_FILTER,\n} from './shared';\n\n/**\n * @ignore\n */\nexport const routeLineBaseTemplate: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n 'line-sort-key': ['get', 'index'],\n },\n};\n\nconst outlineLineWidth: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 1, 5, 5, 6, 10, 10, 18, 14];\n\n/**\n * @ignore\n */\nexport const routeDeselectedOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: DESELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': DESELECTED_OUTLINE_COLOR,\n 'line-width': outlineLineWidth,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeDeselectedLine: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: DESELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': DESELECTED_FOREGROUND_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: SELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': ROUTE_LINE_OUTLINE_COLOR,\n 'line-width': outlineLineWidth,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeMainLine = (props?: { color?: string }): LayerSpecTemplate<LineLayerSpecification> => ({\n ...routeLineBaseTemplate,\n filter: SELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': props?.color ?? ROUTE_LINE_FOREGROUND_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n});\n\n/**\n * @ignore\n */\nexport const routeLineArrows: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n layout: {\n 'symbol-placement': 'line',\n 'icon-image': 'roads-arrow-white',\n // The current arrow icon seems to point backwards otherwise. Check with caution!\n 'icon-rotate': 180,\n },\n};\n\n/**\n * @ignore\n */\nexport const SELECTED_SUMMARY_POPUP_IMAGE_ID = 'selected-route-summary-popup';\n/**\n * @ignore\n */\nexport const DESELECTED_SUMMARY_POPUP_IMAGE_ID = 'deselected-route-summary-popup';\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeTollRoadsOutline: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n },\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 1, 9, 5, 11, 10, 15, 18, 20],\n 'line-color': '#BEBFFA',\n },\n};\n\n/**\n * @ignore\n */\nexport const routeTollRoadsSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 4,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-image': 'poi-toll_plaza',\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 4, 0.8, 16.5, 1],\n },\n};\n","import type { ExpressionSpecification, LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT } from '../../shared/layers/commonLayerProps';\nimport {\n MAJOR_DELAY_COLOR,\n MINOR_DELAY_LABEL_COLOR,\n MODERATE_DELAY_COLOR,\n SELECTED_ROUTE_FILTER,\n UNKNOWN_DELAY_COLOR,\n} from './shared';\n\nconst EXTRA_FOREGROUND_LINE_WIDTH: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 1,\n 2,\n 5,\n 3,\n 10,\n 4,\n 18,\n 6,\n];\n\n/**\n * @ignore\n */\nexport const routeIncidentsBGLine: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n layout: { 'line-cap': 'round' },\n paint: {\n 'line-width': EXTRA_FOREGROUND_LINE_WIDTH,\n 'line-color': [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'minor',\n '#FFC105',\n 'moderate',\n MODERATE_DELAY_COLOR,\n 'major',\n MAJOR_DELAY_COLOR,\n // other\n '#C7D2D8',\n ],\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsDashedLine: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n filter: ['in', ['get', 'magnitudeOfDelay'], ['literal', ['unknown', 'indefinite']]],\n layout: { 'line-join': 'round' },\n paint: {\n 'line-width': EXTRA_FOREGROUND_LINE_WIDTH,\n 'line-color': [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'unknown',\n 'rgba(190, 39, 27, 1)',\n // other (undefined):\n 'rgba(137, 150, 168, 1)',\n ],\n 'line-dasharray': [1.5, 1],\n },\n};\n\n/**\n * @ignore\n */\nexport const magnitudeOfDelayTextColor: ExpressionSpecification = [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'minor',\n MINOR_DELAY_LABEL_COLOR,\n 'moderate',\n MODERATE_DELAY_COLOR,\n 'major',\n MAJOR_DELAY_COLOR,\n 'indefinite',\n '#666666',\n // other\n UNKNOWN_DELAY_COLOR,\n];\n\nconst routeIncidentsSymbolBase: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 6,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-ignore-placement': true,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsJamSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...routeIncidentsSymbolBase,\n filter: ['all', ['has', 'jamIconID'], routeIncidentsSymbolBase.filter as ExpressionSpecification],\n layout: {\n ...routeIncidentsSymbolBase.layout,\n 'icon-image': ['get', 'jamIconID'],\n 'icon-anchor': 'bottom-left',\n 'text-anchor': 'bottom-left',\n // Jam symbols have delay labels in them:\n 'text-field': ['get', 'title'],\n 'text-font': [MAP_BOLD_FONT],\n 'text-offset': [3.9, -1.4],\n 'text-size': 13,\n },\n paint: {\n ...routeIncidentsSymbolBase.paint,\n 'text-color': magnitudeOfDelayTextColor,\n 'text-halo-color': '#FFFFFF',\n 'text-halo-width': 1,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsCauseSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...routeIncidentsSymbolBase,\n filter: ['all', ['has', 'causeIconID'], routeIncidentsSymbolBase.filter as ExpressionSpecification],\n layout: {\n ...routeIncidentsSymbolBase.layout,\n 'icon-image': ['get', 'causeIconID'],\n 'icon-anchor': 'bottom-right',\n // Cause symbols have no label in them.\n },\n paint: { ...routeIncidentsSymbolBase.paint },\n};\n","import type { LineLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { ROUTE_LINE_FOREGROUND_WIDTH, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeTunnelsLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n 'line-color': '#000000',\n 'line-opacity': 0.3,\n },\n};\n","import type { LineLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport {\n ROUTE_LINE_FOREGROUND_COLOR,\n ROUTE_LINE_FOREGROUND_WIDTH,\n ROUTE_LINE_OUTLINE_COLOR,\n SELECTED_ROUTE_FILTER,\n} from './shared';\n\n/**\n * @ignore\n */\nexport const routeVehicleRestrictedBackgroundLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-color': ROUTE_LINE_OUTLINE_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeVehicleRestrictedDottedLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n },\n paint: {\n 'line-color': ROUTE_LINE_FOREGROUND_COLOR,\n 'line-width': ['interpolate', ['linear'], ['zoom'], 1, 2, 5, 3, 10, 5, 18, 7],\n 'line-dasharray': [0, 1.5],\n },\n};\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT, MAP_MEDIUM_FONT } from '../../shared/layers/commonLayerProps';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport { DESELECTED_SUMMARY_POPUP_IMAGE_ID, SELECTED_SUMMARY_POPUP_IMAGE_ID } from './routeMainLineLayers';\nimport { magnitudeOfDelayTextColor } from './routeTrafficSectionLayers';\nimport { DESELECTED_SECONDARY_COLOR, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const TRAFFIC_CLEAR_IMAGE_ID = 'traffic-clear';\n/**\n * @ignore\n */\nexport const TRAFFIC_MAJOR_IMAGE_ID = 'traffic-major';\n/**\n * @ignore\n */\nexport const TRAFFIC_MODERATE_IMAGE_ID = 'traffic-moderate';\n/**\n * @ignore\n */\nexport const TRAFFIC_MINOR_IMAGE_ID = 'traffic-minor';\n\nconst hasFormattedTraffic: ExpressionSpecification = ['has', 'formattedTraffic'];\n\n/**\n * Builds the summary bubble symbol layer specification with instance-specific image IDs.\n * @param instanceIndex - Optional instance index for supporting multiple RoutingModule instances\n * @ignore\n */\nexport const buildSummaryBubbleSymbolPoint = (instanceIndex?: number): LayerSpecTemplate<SymbolLayerSpecification> => {\n const selectedImageID =\n instanceIndex !== undefined\n ? suffixNumber(SELECTED_SUMMARY_POPUP_IMAGE_ID, instanceIndex)\n : SELECTED_SUMMARY_POPUP_IMAGE_ID;\n const deselectedImageID =\n instanceIndex !== undefined\n ? suffixNumber(DESELECTED_SUMMARY_POPUP_IMAGE_ID, instanceIndex)\n : DESELECTED_SUMMARY_POPUP_IMAGE_ID;\n const trafficClearID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_CLEAR_IMAGE_ID, instanceIndex) : TRAFFIC_CLEAR_IMAGE_ID;\n const trafficMajorID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_MAJOR_IMAGE_ID, instanceIndex) : TRAFFIC_MAJOR_IMAGE_ID;\n const trafficModerateID =\n instanceIndex !== undefined\n ? suffixNumber(TRAFFIC_MODERATE_IMAGE_ID, instanceIndex)\n : TRAFFIC_MODERATE_IMAGE_ID;\n const trafficMinorID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_MINOR_IMAGE_ID, instanceIndex) : TRAFFIC_MINOR_IMAGE_ID;\n\n return {\n type: 'symbol',\n layout: {\n 'icon-image': ['case', SELECTED_ROUTE_FILTER, selectedImageID, deselectedImageID],\n 'symbol-placement': 'point',\n 'icon-rotation-alignment': 'viewport',\n 'text-rotation-alignment': 'viewport',\n 'symbol-sort-key': ['case', SELECTED_ROUTE_FILTER, 0, 1],\n 'icon-text-fit': 'both',\n 'icon-text-fit-padding': [10, 5, 5, 10],\n 'text-font': [MAP_MEDIUM_FONT],\n 'text-size': 13,\n 'icon-padding': 0,\n 'text-justify': 'left',\n 'text-line-height': 1.5,\n 'text-field': [\n 'format',\n ['get', 'formattedDuration'],\n {\n 'text-font': ['literal', [MAP_BOLD_FONT]],\n 'text-color': ['case', SELECTED_ROUTE_FILTER, 'black', DESELECTED_SECONDARY_COLOR],\n },\n ['concat', '\\t\\t', ['get', 'formattedDistance']],\n { 'text-color': DESELECTED_SECONDARY_COLOR },\n ['case', hasFormattedTraffic, '\\n', ''],\n {},\n [\n 'image',\n [\n 'case',\n hasFormattedTraffic,\n [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'major',\n trafficMajorID,\n 'moderate',\n trafficModerateID,\n 'minor',\n trafficMinorID,\n trafficClearID,\n ],\n '',\n ],\n ],\n {},\n ['case', hasFormattedTraffic, ['concat', ' ', ['get', 'formattedTraffic']], ''],\n {\n 'text-font': ['literal', [MAP_BOLD_FONT]],\n 'text-color': magnitudeOfDelayTextColor,\n },\n ],\n },\n paint: {\n 'icon-translate': [0, -35],\n 'text-translate': [0, -35],\n },\n };\n};\n\n/**\n * Default summary bubble symbol layer (without instance suffix)\n * @ignore\n */\nexport const summaryBubbleSymbolPoint: LayerSpecTemplate<SymbolLayerSpecification> = buildSummaryBubbleSymbolPoint();\n","import type { PlaceDisplayProps } from '../../places';\n\n/**\n * @group Routing\n */\nexport const START_INDEX = 'start';\n/**\n * @group Routing\n */\nexport const MIDDLE_INDEX = 'middle';\n/**\n * @group Routing\n */\nexport const FINISH_INDEX = 'finish';\n/**\n * @group Routing\n */\nexport type WaypointIndexType = typeof START_INDEX | typeof MIDDLE_INDEX | typeof FINISH_INDEX;\n\n/**\n * Display properties for a waypoint marker on the map.\n *\n * Extends location display properties with waypoint-specific information\n * including position in the route and stop numbering.\n *\n * @remarks\n * Waypoints are displayed differently based on their position:\n * - **Start**: Origin marker (often \"A\" or green pin)\n * - **Middle**: Numbered stop markers (1, 2, 3, etc.)\n * - **Finish**: Destination marker (often \"B\" or red pin)\n *\n * @example\n * ```typescript\n * // Start waypoint\n * const start: WaypointDisplayProps = {\n * id: 'waypoint-0',\n * iconID: 'waypoint-start',\n * index: 0,\n * indexType: 'start',\n * title: 'Amsterdam Central Station'\n * };\n *\n * // Intermediate stop\n * const stop: WaypointDisplayProps = {\n * id: 'waypoint-1',\n * iconID: 'waypoint-stop',\n * index: 1,\n * indexType: 'middle',\n * stopDisplayIndex: 1,\n * title: 'Schiphol Airport'\n * };\n *\n * // Destination\n * const finish: WaypointDisplayProps = {\n * id: 'waypoint-2',\n * iconID: 'waypoint-finish',\n * index: 2,\n * indexType: 'finish',\n * title: 'Rotterdam Central Station'\n * };\n * ```\n *\n * @group Routing\n */\nexport type WaypointDisplayProps = PlaceDisplayProps & {\n /**\n * The index of the waypoint in relation to the other waypoints.\n *\n * @remarks\n * Zero-based index of this waypoint in the complete waypoints array,\n * including start, all stops, and finish.\n *\n * @example\n * ```typescript\n * index: 0 // First waypoint (start)\n * index: 1 // Second waypoint (first stop or finish)\n * index: 2 // Third waypoint\n * ```\n */\n index: number;\n\n /**\n * The type associated to the index, describing how the waypoint sits in the list of waypoints.\n *\n * @remarks\n * Determines the waypoint's role and visual representation:\n * - `start`: Origin point\n * - `middle`: Intermediate stop\n * - `finish`: Final destination\n *\n * This affects icon selection and labeling behavior.\n */\n indexType: WaypointIndexType;\n\n /**\n * The stop index to be displayed.\n *\n * @remarks\n * Stops are the non-soft waypoints added between origin and destination,\n * numbered starting from 1. Only present for middle waypoints.\n *\n * **Display Behavior:**\n * - Start waypoint: undefined\n * - First stop: 1\n * - Second stop: 2\n * - Finish waypoint: undefined\n *\n * Used for displaying stop numbers (e.g., \"Stop 1\", \"Stop 2\") in the UI.\n *\n * @example\n * ```typescript\n * stopDisplayIndex: 1 // First intermediate stop\n * stopDisplayIndex: 2 // Second intermediate stop\n * stopDisplayIndex: undefined // Start or finish waypoint\n * ```\n */\n stopDisplayIndex?: number;\n};\n\n/**\n * @ignore\n */\nexport const INDEX_TYPE = 'indexType';\n\n/**\n * @ignore\n */\nexport const STOP_DISPLAY_INDEX = 'stopDisplayIndex';\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT } from '../../shared/layers/commonLayerProps';\nimport {\n ICON_ID,\n pinIconBaseLayout,\n pinIconBasePaint,\n pinTextBaseLayout,\n pinTextBasePaint,\n} from '../../shared/layers/symbolLayers';\nimport { INDEX_TYPE, MIDDLE_INDEX, STOP_DISPLAY_INDEX } from '../types/waypointDisplayProps';\n\n/**\n * Waypoint start image ID.\n * @group Routing\n */\nexport const WAYPOINT_START_IMAGE_ID = 'waypointStart';\n/**\n * Waypoint stop image ID.\n *\n * @remarks\n * Used for intermediate waypoints in a route.\n *\n * @group Routing.\n */\nexport const WAYPOINT_STOP_IMAGE_ID = 'waypointStop';\n/**\n * Soft waypoint image ID.\n *\n * @remarks\n * This is currently unsupported in Orbis maps.\n *\n * @group Routing\n */\nexport const WAYPOINT_SOFT_IMAGE_ID = 'waypointSoft';\n/**\n * Waypoint finish image ID.\n *\n * @group Routing\n */\nexport const WAYPOINT_FINISH_IMAGE_ID = 'waypointFinish';\n\nconst isSoftWaypoint: ExpressionSpecification = [\n 'all',\n ['==', ['get', INDEX_TYPE], MIDDLE_INDEX],\n ['!', ['has', STOP_DISPLAY_INDEX]],\n];\n\nconst pinIndexLabelPaint: SymbolLayerSpecification['paint'] = {\n 'text-color': '#ffffff',\n};\n\nconst pinIndexLabelLayout: SymbolLayerSpecification['layout'] = {\n // optional centered text to indicate stop numbers (1, 2 ...):\n 'text-field': ['get', STOP_DISPLAY_INDEX],\n 'text-font': [MAP_BOLD_FONT],\n 'text-size': ['interpolate', ['linear'], ['zoom'], 13, 14, 18, 16],\n 'text-offset': [0, -1.6],\n // pin vs circle:\n 'icon-anchor': [\n 'case',\n isSoftWaypoint,\n 'center',\n // else\n 'bottom',\n ],\n 'text-allow-overlap': true,\n};\n\n// TODO: reusable display of pins with indexes, not just waypoints\n/**\n * @ignore\n */\nexport const waypointSymbols: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n paint: {\n ...pinIconBasePaint,\n ...pinIndexLabelPaint,\n },\n layout: {\n ...pinIconBaseLayout,\n ...pinIndexLabelLayout,\n 'symbol-sort-key': [\n 'case',\n ['==', ['get', ICON_ID], WAYPOINT_SOFT_IMAGE_ID],\n 0,\n ['abs', ['-', ['get', 'index'], 1000]],\n ],\n },\n};\n\n/**\n * @ignore\n */\nexport const waypointLabels: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n paint: {\n ...pinTextBasePaint,\n 'text-color': 'black',\n 'text-halo-width': 1.5,\n 'text-halo-color': '#ffffff',\n },\n layout: {\n ...pinTextBaseLayout,\n 'text-anchor': 'top',\n 'text-offset': [0, 0.4],\n },\n};\n","import { mapStyleLayerIDs } from '../../shared';\nimport type { RouteLayersConfig, RoutingModuleConfig } from '../types/routeModuleConfig';\nimport { chargingStopSymbol } from './chargingStopLayers';\nimport { instructionArrow, instructionLine, instructionOutline } from './guidanceLayers';\nimport { routeFerriesLine, routeFerriesSymbol } from './routeFerrySectionLayers';\nimport {\n routeDeselectedLine,\n routeDeselectedOutline,\n routeLineArrows,\n routeMainLine,\n routeOutline,\n} from './routeMainLineLayers';\nimport { routeTollRoadsOutline, routeTollRoadsSymbol } from './routeTollRoadLayers';\nimport {\n routeIncidentsBGLine,\n routeIncidentsCauseSymbol,\n routeIncidentsDashedLine,\n routeIncidentsJamSymbol,\n} from './routeTrafficSectionLayers';\nimport { routeTunnelsLine } from './routeTunnelSectionLayers';\nimport { routeVehicleRestrictedBackgroundLine, routeVehicleRestrictedDottedLine } from './routeVehicleRestrictedLayers';\nimport { buildSummaryBubbleSymbolPoint, summaryBubbleSymbolPoint } from './summaryBubbleLayers';\nimport { waypointLabels, waypointSymbols } from './waypointLayers';\n\n/**\n * Helper function to add layer ID prefix to beforeID references, but only for internal routing layer IDs\n * @ignore\n */\nconst prefixBeforeID = (beforeID: string | undefined, layerIDPrefix: string | undefined): string | undefined => {\n if (!beforeID || !layerIDPrefix) {\n return beforeID;\n }\n // Don't prefix map style layer IDs (they start with capital letters or contain specific prefixes)\n if (beforeID.startsWith('route') || beforeID.startsWith('waypoint')) {\n return `${layerIDPrefix}-${beforeID}`;\n }\n return beforeID;\n};\n\n/**\n * Helper function to process additional layers and prefix their beforeID fields\n * @ignore\n */\nconst prefixBeforeIDs = (\n additional: Record<string, any> | undefined,\n layerIDPrefix: string | undefined,\n): Record<string, any> | undefined => {\n if (!additional || !layerIDPrefix) {\n return additional;\n }\n\n return Object.fromEntries(\n Object.entries(additional).map(([key, layer]) => [\n key,\n layer?.beforeID ? { ...layer, beforeID: prefixBeforeID(layer.beforeID, layerIDPrefix) } : layer,\n ]),\n );\n};\n\n/**\n * Helper function to add instance suffix to image IDs for supporting multiple RoutingModule instances\n * @ignore\n */\nconst suffixImageID = (imageID: string | undefined, instanceIndex: number | undefined): string | undefined => {\n if (!imageID || instanceIndex === undefined) {\n return imageID;\n }\n return `${imageID}-${instanceIndex}`;\n};\n\n/**\n * Generates the routing layers configuration for route visualization on the map.\n * @param config - Optional routing module configuration to customize layer properties.\n * @param layerIDPrefix - Optional prefix to add to layer IDs for supporting multiple instances.\n * @param instanceIndex - Optional instance index for image ID suffixes.\n * @ignore\n */\nexport const buildRoutingLayers = (\n config: RoutingModuleConfig = {},\n layerIDPrefix?: string,\n instanceIndex?: number,\n): Required<RouteLayersConfig> => {\n const configLayers = config.layers;\n const configSectionLayers = configLayers?.sections;\n const mainColor = config.theme?.mainColor;\n\n return {\n mainLines: {\n routeLineArrows: {\n ...routeLineArrows,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configLayers?.mainLines?.routeLineArrows,\n },\n routeLine: {\n ...routeMainLine({ color: mainColor }),\n beforeID: prefixBeforeID('routeIncidentBackgroundLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeLine,\n },\n routeOutline: {\n ...routeOutline,\n beforeID: prefixBeforeID('routeLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeOutline,\n },\n routeDeselectedLine: {\n ...routeDeselectedLine,\n beforeID: prefixBeforeID('routeOutline', layerIDPrefix),\n ...configLayers?.mainLines?.routeDeselectedLine,\n },\n routeDeselectedOutline: {\n ...routeDeselectedOutline,\n beforeID: prefixBeforeID('routeDeselectedLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeDeselectedOutline,\n },\n ...prefixBeforeIDs(configLayers?.mainLines?.additional, layerIDPrefix),\n },\n waypoints: {\n routeWaypointSymbol: {\n ...waypointSymbols,\n beforeID: prefixBeforeID('routeSummaryBubbleSymbol', layerIDPrefix),\n ...configLayers?.waypoints?.routeWaypointSymbol,\n },\n routeWaypointLabel: {\n ...waypointLabels,\n beforeID: prefixBeforeID('routeWaypointSymbol', layerIDPrefix),\n ...configLayers?.waypoints?.routeWaypointLabel,\n },\n ...prefixBeforeIDs(configLayers?.waypoints?.additional, layerIDPrefix),\n },\n chargingStops: {\n routeChargingStopSymbol: {\n ...chargingStopSymbol(config.chargingStops),\n beforeID: prefixBeforeID('routeWaypointSymbol', layerIDPrefix),\n ...configLayers?.chargingStops?.routeChargingStopSymbol,\n },\n ...prefixBeforeIDs(configLayers?.chargingStops?.additional, layerIDPrefix),\n },\n sections: {\n incident: {\n routeIncidentJamSymbol: {\n ...routeIncidentsJamSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentJamSymbol,\n },\n routeIncidentCauseSymbol: {\n ...routeIncidentsCauseSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentCauseSymbol,\n },\n routeIncidentBackgroundLine: {\n ...routeIncidentsBGLine,\n beforeID: prefixBeforeID('routeIncidentDashedLine', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentBackgroundLine,\n },\n routeIncidentDashedLine: {\n ...routeIncidentsDashedLine,\n beforeID: prefixBeforeID('routeTunnelLine', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentDashedLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.incident?.additional, layerIDPrefix),\n },\n ferry: {\n routeFerryLine: {\n ...routeFerriesLine,\n beforeID: prefixBeforeID('routeLineArrows', layerIDPrefix),\n ...configSectionLayers?.ferry?.routeFerryLine,\n },\n routeFerrySymbol: {\n ...routeFerriesSymbol,\n beforeID: prefixBeforeID('routeIncidentJamSymbol', layerIDPrefix),\n ...configSectionLayers?.ferry?.routeFerrySymbol,\n },\n ...prefixBeforeIDs(configSectionLayers?.ferry?.additional, layerIDPrefix),\n },\n tollRoad: {\n routeTollRoadOutline: {\n ...routeTollRoadsOutline,\n beforeID: prefixBeforeID('routeDeselectedOutline', layerIDPrefix),\n ...configSectionLayers?.tollRoad?.routeTollRoadOutline,\n },\n routeTollRoadSymbol: {\n ...routeTollRoadsSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.tollRoad?.routeTollRoadSymbol,\n },\n ...prefixBeforeIDs(configSectionLayers?.tollRoad?.additional, layerIDPrefix),\n },\n tunnel: {\n routeTunnelLine: {\n ...routeTunnelsLine,\n beforeID: prefixBeforeID('routeLineArrows', layerIDPrefix),\n ...configSectionLayers?.tunnel?.routeTunnelLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.tunnel?.additional, layerIDPrefix),\n },\n vehicleRestricted: {\n routeVehicleRestrictedBackgroundLine: {\n ...routeVehicleRestrictedBackgroundLine,\n beforeID: prefixBeforeID('routeVehicleRestrictedForegroundLine', layerIDPrefix),\n ...configSectionLayers?.vehicleRestricted?.routeVehicleRestrictedBackgroundLine,\n },\n routeVehicleRestrictedForegroundLine: {\n ...routeVehicleRestrictedDottedLine,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configSectionLayers?.vehicleRestricted?.routeVehicleRestrictedForegroundLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.vehicleRestricted?.additional, layerIDPrefix),\n },\n },\n instructionLines: {\n routeInstructionLine: {\n ...instructionLine,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configLayers?.instructionLines?.routeInstructionLine,\n },\n routeInstructionOutline: {\n ...instructionOutline,\n beforeID: prefixBeforeID('routeInstructionLine', layerIDPrefix),\n ...configLayers?.instructionLines?.routeInstructionOutline,\n },\n ...prefixBeforeIDs(configLayers?.instructionLines?.additional, layerIDPrefix),\n },\n instructionArrows: {\n routeInstructionArrowSymbol: {\n ...instructionArrow,\n beforeID: prefixBeforeID('routeInstructionLine', layerIDPrefix),\n ...(instanceIndex !== undefined && {\n layout: {\n ...instructionArrow.layout,\n 'icon-image': suffixImageID(instructionArrow.layout?.['icon-image'] as string, instanceIndex),\n },\n }),\n ...configLayers?.instructionArrows?.routeInstructionArrowSymbol,\n },\n ...prefixBeforeIDs(configLayers?.instructionArrows?.additional, layerIDPrefix),\n },\n summaryBubbles: {\n routeSummaryBubbleSymbol: {\n ...(instanceIndex !== undefined\n ? buildSummaryBubbleSymbolPoint(instanceIndex)\n : summaryBubbleSymbolPoint),\n ...configLayers?.summaryBubbles?.routeSummaryBubbleSymbol,\n },\n ...prefixBeforeIDs(configLayers?.summaryBubbles?.additional, layerIDPrefix),\n },\n };\n};\n\n/**\n * Default routing layers configuration. Calls routingLayers with no parameters.\n *\n * @remarks\n * This configuration defines the complete visual styling for all route-related map layers,\n * including main route lines, waypoints, special road sections (ferries, tunnels, toll roads, etc.),\n * turn-by-turn guidance instructions, and route summary information.\n *\n * **Usage:**\n * - Automatically applied when initializing {@link RoutingModule} without custom layer configuration\n * - Can be used as a reference or starting point for creating custom layer configurations\n * - Individual properties can be selectively overridden while keeping defaults for others\n *\n * @see {@link buildRoutingLayers} for details.\n *\n * @see {@link RouteLayersConfig} for the configuration type definition\n * @see {@link RoutingModule.get} for initialization options\n * @see {@link RoutingModule.applyConfig} for runtime configuration updates\n *\n * @group Routing\n */\nexport const defaultRoutingLayers: Required<RouteLayersConfig> = buildRoutingLayers();\n","import type { StyleImageMetadata } from 'maplibre-gl';\nimport { SVGIconStyleOptions } from '../../shared';\nimport { isDOMImageSupported, svgToImg } from '../../shared/imageUtils';\nimport { parseSvg, pinSvg } from '../../shared/resources';\nimport circleSvgRaw from './circle.svg?raw';\nimport finishSvgRaw from './finish.svg?raw';\nimport instructionArrowSvgRaw from './instruction-line-arrow.svg?raw';\nimport startSvgRaw from './start.svg?raw';\nimport summaryMapBubbleSvgRaw from './summary-map-bubble.svg?raw';\nimport trafficSvgRaw from './traffic.svg?raw';\n\nlet instructionArrowIconImg: HTMLImageElement;\n\n// defensive check for SSR\nif (isDOMImageSupported()) {\n instructionArrowIconImg = svgToImg(parseSvg(instructionArrowSvgRaw));\n}\n\n/**\n * @ignore\n */\nexport { instructionArrowIconImg };\n\n/**\n * @ignore\n */\nexport const summaryMapBubbleImg = (color: string): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg: SVGElement = parseSvg(summaryMapBubbleSvgRaw);\n svg.querySelector('#bubble')?.setAttribute('fill', color);\n svg.querySelector('#pin')?.setAttribute('fill', color);\n return svgToImg(svg);\n};\n\n/**\n * Options to effectively stretch the summary bubble image to fit its text.\n * * They are tightly coupled with the SVG original dimensions.\n * @ignore\n */\nexport const summaryBubbleImageOptions: Partial<StyleImageMetadata> = {\n pixelRatio: 2,\n stretchX: [\n [20, 45],\n [100, 130],\n ],\n stretchY: [[20, 35]],\n content: [10, 10, 130, 45],\n};\n\n/**\n * @ignore\n * @param color\n */\nexport const trafficImg = (color: string): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg: SVGElement = parseSvg(trafficSvgRaw);\n const main = svg.querySelector('#main') as Element;\n main.setAttribute('transform', 'scale(2)');\n main.setAttribute('fill', color);\n return svgToImg(svg);\n};\n\n/**\n * @ignore\n */\nexport const waypointIcon = (\n foregroundSvg: SVGElement | undefined,\n svgOptions: SVGIconStyleOptions | undefined,\n): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg = pinSvg(svgOptions);\n if (foregroundSvg) {\n svg.appendChild(foregroundSvg);\n }\n return svgToImg(svg);\n};\n\n/**\n * @ignore\n */\nexport const waypointStartIcon = (svgOptions: SVGIconStyleOptions | undefined): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return waypointIcon(parseSvg(startSvgRaw), svgOptions);\n};\n\n/**\n * @ignore\n */\nexport const waypointFinishIcon = (svgOptions: SVGIconStyleOptions | undefined): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return waypointIcon(parseSvg(finishSvgRaw), svgOptions);\n};\n\n/**\n * @ignore\n */\nexport const softWaypointIcon = (): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return svgToImg(parseSvg(circleSvgRaw));\n};\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"50\\\" height=\\\"50\\\">\\n <path d=\\\"M1.562 40.569L25 1.562l23.438 39.007L25 27.566 1.562 40.569z\\\" fill=\\\"#fff\\\"/>\\n <path d=\\\"M25.369.107c.782.429.506.167.917.682l23.438 39.007c.79 1.316-.671 2.829-2.014 2.085L25 29.281l-22.71 12.6c-1.343.744-2.804-.769-2.014-2.085L23.714.789c.461-.694.9-.682 1.656-.682zM25 1.562L1.562 40.569 25 27.566l23.438 13.003L25 1.562z\\\"\\n fill=\\\"gray\\\"/>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"160\\\" height=\\\"65\\\">\\n <g filter=\\\"url(#A)\\\">\\n <rect id=\\\"pin\\\" x=\\\"81.899\\\" y=\\\"47\\\" width=\\\"10\\\" height=\\\"10\\\" rx=\\\"2\\\" transform=\\\"rotate(45 81.899 47)\\\"/>\\n <rect id=\\\"bubble\\\" x=\\\"10\\\" y=\\\"10\\\" width=\\\"140\\\" height=\\\"45\\\" rx=\\\"12\\\"/>\\n </g>\\n <defs>\\n <filter id=\\\"A\\\">\\n <feDropShadow dx=\\\"0\\\" dy=\\\"2\\\" stdDeviation=\\\"4\\\" flood-opacity=\\\".3\\\"/>\\n </filter>\\n </defs>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"32\\\" height=\\\"32\\\">\\n <g id=\\\"main\\\">\\n <path fill-rule=\\\"evenodd\\\"\\n d=\\\"M11.389 4H9.025a.86.86 0 0 0-.517.173l-.001-.001a.5.5 0 0 1-.464.059l-.028-.011a.5.5 0 0 1-.129-.83A1.86 1.86 0 0 1 9.025 3h2.364a2 2 0 0 1 1.789 1.106l1.02 2.04c.471.176.802.63.802 1.156v.803c0 .543 0 .815-.071 1.038a1.5 1.5 0 0 1-.914.953c-.219.08-.491.092-1.034.114l-1.481.062v-.166-.803a2.23 2.23 0 0 0-1.055-1.897l-.74-1.479 2.326.033h.955l-.703-1.406A1 1 0 0 0 11.389 4zM12 8.4a1 1 0 1 1 2 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z\\\"/>\\n <path d=\\\"M11.833 11.115c0-.109.089-.198.198-.198h1.979c.109 0 .198.089.198.198 0 .328-.266.594-.594.594h-1.187c-.328 0-.594-.266-.594-.594z\\\"/>\\n <path fill-rule=\\\"evenodd\\\"\\n d=\\\"M1.802 8.146C1.331 8.321 1 8.776 1 9.302v.803c0 .543 0 .815.071 1.038a1.5 1.5 0 0 0 .914.953c.219.08.491.092 1.034.114l2.731.114 2.731-.114c.543-.023.814-.034 1.034-.114a1.5 1.5 0 0 0 .914-.953c.071-.223.071-.494.071-1.038v-.803c0-.526-.331-.98-.802-1.156l-1.02-2.04A2 2 0 0 0 6.889 5H4.611a2 2 0 0 0-1.789 1.106l-1.02 2.04zM6.889 6H4.611a1 1 0 0 0-.894.553l-.703 1.406h.955 3.563.955l-.703-1.406A1 1 0 0 0 6.889 6zM2 10.4a1 1 0 1 1 2 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm6.5-1a1 1 0 0 0-1 1 .5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5 1 1 0 0 0-1-1z\\\"/>\\n <path d=\\\"M1.792 13.115c0-.109.089-.198.198-.198h1.979c.109 0 .198.089.198.198 0 .328-.266.594-.594.594H2.385c-.328 0-.594-.266-.594-.594zm5.739-.198c-.109 0-.198.089-.198.198 0 .328.266.594.594.594h1.188c.328 0 .594-.266.594-.594 0-.109-.089-.198-.198-.198h-1.98z\\\"/>\\n </g>\\n</svg>\\n\"","import { TomTomConfig } from '@tomtom-org/maps-sdk/core';\nimport type { ToBeAddedLayerSpecTemplate, ToBeAddedLayerSpecWithoutSource } from '../../shared';\nimport { buildRoutingLayers } from '../layers/routingLayers';\nimport type { RouteLayersConfig, RoutingModuleConfig } from '../types/routeModuleConfig';\nimport type { RoutingLayersSpecs } from '../types/routingSourcesAndLayers';\n\n/**\n * @ignore\n */\nconst mapLayerSpecs = (\n layerSpecs: Record<string, Partial<ToBeAddedLayerSpecTemplate>> = {},\n layerIDPrefix?: string,\n): ToBeAddedLayerSpecWithoutSource[] =>\n // The key of the entry is the layer ID:\n Object.entries(layerSpecs).map(\n ([id, spec]) =>\n ({\n ...spec,\n id: layerIDPrefix ? `${layerIDPrefix}-${id}` : id,\n }) as ToBeAddedLayerSpecWithoutSource,\n );\n\n/**\n * @ignore\n */\nexport const createLayersSpecs = (\n layerConfigs: RouteLayersConfig = {},\n layerIDPrefix?: string,\n): RoutingLayersSpecs => ({\n mainLines: mapLayerSpecs(layerConfigs.mainLines, layerIDPrefix),\n waypoints: mapLayerSpecs(layerConfigs.waypoints, layerIDPrefix),\n chargingStops: mapLayerSpecs(layerConfigs?.chargingStops, layerIDPrefix),\n ferries: mapLayerSpecs(layerConfigs.sections?.ferry, layerIDPrefix),\n incidents: mapLayerSpecs(layerConfigs.sections?.incident, layerIDPrefix),\n tollRoads: mapLayerSpecs(layerConfigs.sections?.tollRoad, layerIDPrefix),\n tunnels: mapLayerSpecs(layerConfigs.sections?.tunnel, layerIDPrefix),\n vehicleRestricted: mapLayerSpecs(layerConfigs.sections?.vehicleRestricted, layerIDPrefix),\n instructionLines: mapLayerSpecs(layerConfigs.instructionLines, layerIDPrefix),\n instructionArrows: mapLayerSpecs(layerConfigs.instructionArrows, layerIDPrefix),\n summaryBubbles: mapLayerSpecs(layerConfigs.summaryBubbles, layerIDPrefix),\n});\n\n/**\n * @ignore\n */\nexport const routeModuleConfigWithDefaults = (\n config: RoutingModuleConfig | undefined,\n layerIDPrefix: string,\n instanceIndex: number,\n): RoutingModuleConfig => {\n const globalDisplayUnits = TomTomConfig.instance.get().displayUnits;\n const displayUnits = config?.displayUnits;\n return {\n // First apply the provided configuration not to lose any properties:\n ...config,\n ...(displayUnits ? {} : { displayUnits: globalDisplayUnits }),\n layers: buildRoutingLayers(config, layerIDPrefix, instanceIndex),\n };\n};\n","import {\n ChargingStop,\n ChargingStopProps,\n formatDuration,\n generateId,\n type Place,\n type Routes,\n} from '@tomtom-org/maps-sdk/core';\nimport { FeatureCollection, Point } from 'geojson';\nimport { PlaceDisplayProps } from '../../places';\nimport type { DisplayRouteProps, RouteStateProps } from '../types/displayRoutes';\nimport { RoutingModuleConfig } from '../types/routeModuleConfig';\n\nconst getIconID = (chargingStop: ChargingStop, config: RoutingModuleConfig | undefined): string => {\n const iconConfig = config?.chargingStops?.icon;\n if (iconConfig?.mapping) {\n const mapping = iconConfig.mapping;\n switch (mapping.basedOn) {\n case 'chargingSpeed':\n if (chargingStop.properties.chargingConnectionInfo?.chargingSpeed) {\n return mapping.value[chargingStop.properties.chargingConnectionInfo?.chargingSpeed];\n }\n break;\n case 'custom':\n return mapping.fn(chargingStop);\n }\n }\n\n // default: (genesis-like) categorySet ID for \"EV Charging Station\" based on search\n return '7309';\n};\n\nconst formatTitle = (chargingStop: ChargingStop): string => {\n const properties = chargingStop.properties;\n return properties.chargingParkName ?? (properties.chargingParkOperatorName as string);\n};\n\ntype DisplayChargingStopProps = PlaceDisplayProps &\n ChargingStopProps &\n RouteStateProps & {\n chargingDuration: string;\n chargingPower: string;\n };\n\ntype DisplayChargingStops = FeatureCollection<Point, DisplayChargingStopProps>;\n\n/**\n * Generates display-ready charging stations for the given planning context ones.\n * @param routes The routes return for ldEV.\n * @param config The charging stops display configuration.\n * @see chargingStopLayers\n * @ignore\n */\nexport const toDisplayChargingStops = (\n routes: Routes<DisplayRouteProps>,\n config: RoutingModuleConfig | undefined,\n): DisplayChargingStops => {\n const displayChargingStops: Place<DisplayChargingStopProps>[] = [];\n\n if (config?.chargingStops?.visible !== false) {\n for (const route of routes.features) {\n for (const leg of route.properties.sections.leg) {\n const chargingStop = leg.summary.chargingInformationAtEndOfLeg;\n\n if (chargingStop) {\n const properties = chargingStop.properties;\n displayChargingStops.push({\n ...chargingStop,\n properties: {\n ...chargingStop.properties,\n id: chargingStop.properties.chargingParkId ?? generateId(),\n iconID: getIconID(chargingStop, config),\n title: formatTitle(chargingStop),\n chargingPower: `${properties.chargingConnectionInfo?.chargingPowerInkW} kW`,\n chargingDuration: formatDuration(\n properties.chargingTimeInSeconds,\n config?.displayUnits?.time,\n ) as string,\n routeState: route.properties.routeState,\n },\n });\n }\n }\n }\n }\n return { type: 'FeatureCollection', features: displayChargingStops };\n};\n","import { formatDuration, type TrafficSectionProps } from '@tomtom-org/maps-sdk/core';\nimport type { DisplayTrafficSectionProps } from '../types/routeSections';\n\nconst hasJam = (sectionProps: TrafficSectionProps): boolean => sectionProps.categories.includes('jam');\n\nconst buildTitle = (sectionProps: TrafficSectionProps): string | undefined => {\n if (hasJam(sectionProps)) {\n return formatDuration(sectionProps.delayInSeconds);\n }\n return undefined;\n};\n\nconst toTrafficJamIconSuffix = (title: string | undefined): 'collapsed' | 'small' | 'medium' | 'large' => {\n if (!title?.length) {\n return 'collapsed';\n }\n if (title.length < 6) {\n // 1 digit minutes\n return 'small';\n }\n if (title.length < 8) {\n // 2 digit minutes\n return 'medium';\n }\n // hours + minutes\n return 'large';\n};\n\nconst toJamIconID = (sectionProps: TrafficSectionProps, title: string | undefined): string | null => {\n if (!hasJam(sectionProps)) {\n return null;\n }\n const magnitude = sectionProps.magnitudeOfDelay ?? 'unknown';\n return `traffic-jam-${magnitude}-${toTrafficJamIconSuffix(title)}`;\n};\n\nconst toCauseIconID = (sectionProps: TrafficSectionProps): string | null => {\n const firstNonJamCategory = sectionProps.categories.find((category) => category !== 'jam');\n switch (firstNonJamCategory) {\n case 'accident':\n return 'traffic-incidents-accident';\n case 'roadworks':\n return 'traffic-incidents-roadworks';\n case 'road-closed':\n return 'traffic-incidents-road_closed';\n case 'danger':\n case 'animals-on-road':\n return 'traffic-incidents-danger';\n case 'broken-down-vehicle':\n return 'traffic-incidents-broken_down_vehicle';\n case 'lane-closed':\n case 'narrow-lanes':\n return 'traffic-incidents-lane_closed';\n case 'wind':\n return 'traffic-incidents-wind';\n case 'fog':\n return 'traffic-incidents-fog';\n case 'rain':\n return 'traffic-incidents-rain';\n case 'frost':\n return 'traffic-incidents-frost';\n case 'flooding':\n return 'traffic-incidents-flooding';\n default:\n return null;\n }\n};\n\n/**\n * @ignore\n */\nexport const toDisplayTrafficSectionProps = (\n sectionProps: TrafficSectionProps,\n): Omit<DisplayTrafficSectionProps, 'routeState' | 'routeIndex'> => {\n const title = buildTitle(sectionProps);\n const jamIconID = toJamIconID(sectionProps, title);\n const causeIconID = toCauseIconID(sectionProps);\n return {\n ...sectionProps,\n ...(jamIconID && { jamIconID }),\n ...(causeIconID && { causeIconID }),\n ...(title && { title }),\n };\n};\n","import type { CommonPlaceProps, Waypoint, WaypointLike, Waypoints } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPosition } from '@tomtom-org/maps-sdk/core';\nimport type { Point, Position } from 'geojson';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport {\n WAYPOINT_FINISH_IMAGE_ID,\n WAYPOINT_SOFT_IMAGE_ID,\n WAYPOINT_START_IMAGE_ID,\n WAYPOINT_STOP_IMAGE_ID,\n} from '../layers/waypointLayers';\nimport type { PlanningWaypoint } from '../types/planningWaypoint';\nimport type { WaypointsConfig } from '../types/routeModuleConfig';\nimport type { WaypointDisplayProps, WaypointIndexType } from '../types/waypointDisplayProps';\nimport { FINISH_INDEX, MIDDLE_INDEX, START_INDEX } from '../types/waypointDisplayProps';\n\nconst indexTypeFor = (index: number, arrayLength: number): WaypointIndexType =>\n index === 0 ? START_INDEX : index < arrayLength - 1 ? MIDDLE_INDEX : FINISH_INDEX;\n\n/**\n * Builds the title of the given waypoint.\n * @param waypoint The waypoint for which to build the title.\n */\nexport const buildWaypointTitle = (waypoint: Waypoint): string | undefined => {\n const placeProperties = waypoint?.properties as CommonPlaceProps;\n return placeProperties?.poi?.name ?? placeProperties?.address?.freeformAddress ?? undefined;\n};\n\nexport const getImageIDForWaypoint = (\n waypoint: Waypoint,\n indexType: WaypointIndexType,\n instanceIndex?: number,\n): string => {\n if (waypoint.properties.radiusMeters) {\n return instanceIndex !== undefined\n ? suffixNumber(WAYPOINT_SOFT_IMAGE_ID, instanceIndex)\n : WAYPOINT_SOFT_IMAGE_ID;\n }\n let baseImageID: string;\n switch (indexType) {\n case 'start':\n baseImageID = WAYPOINT_START_IMAGE_ID;\n break;\n case 'finish':\n baseImageID = WAYPOINT_FINISH_IMAGE_ID;\n break;\n default:\n baseImageID = WAYPOINT_STOP_IMAGE_ID;\n break;\n }\n return instanceIndex !== undefined ? suffixNumber(baseImageID, instanceIndex) : baseImageID;\n};\n\nconst toWaypointFromPosition = (position: Position): Waypoint => ({\n type: 'Feature',\n geometry: {\n type: 'Point',\n coordinates: position,\n },\n properties: {},\n});\n\nconst toWaypointFromPoint = (point: Point): Waypoint => ({\n type: 'Feature',\n geometry: point,\n properties: {},\n ...(point.bbox && { bbox: point.bbox }),\n});\n\nconst asWaypoint = (waypointInput: WaypointLike): Waypoint => {\n if (Array.isArray(waypointInput)) {\n return toWaypointFromPosition(waypointInput);\n }\n if (waypointInput.type === 'Point') {\n return toWaypointFromPoint(waypointInput);\n }\n return waypointInput as Waypoint;\n};\n\n/**\n * Determines whether the given waypoint is a regular start/stop/destination with guidance attached,\n * as opposed to a circle (soft) waypoint.\n * @param waypoint The waypoint to verify.\n */\nexport const isHardWaypoint = (waypoint: Waypoint): boolean => !waypoint.properties.radiusMeters;\n\n/**\n * Generates display-ready waypoints for the given planning context ones.\n * @param waypoints The planning context waypoints.\n * @param options\n * @param instanceIndex Optional instance index for supporting multiple RoutingModule instances\n */\nexport const toDisplayWaypoints = (\n waypoints: PlanningWaypoint[],\n options: WaypointsConfig | undefined,\n instanceIndex?: number,\n): Waypoints<WaypointDisplayProps> => {\n // Since waypoints are of mixed types (hard and soft), we need to calculate the hard-only indexes\n // in case we have stops with numbered icons:\n let hardWaypointIndex = -1;\n return {\n type: 'FeatureCollection',\n features: waypoints\n .map((waypointInput, index) => {\n if (!waypointInput) {\n // (we consider placeholder waypoints to be \"hard\", since they typically occupy a position in a planner panel)\n hardWaypointIndex++;\n // (will be filtered out below - we don't pre-filter it to keep the original input index)\n return null as unknown as Waypoint<WaypointDisplayProps>;\n }\n const waypoint: Waypoint = asWaypoint(waypointInput);\n const indexType = indexTypeFor(index, waypoints.length);\n const hardWaypoint = isHardWaypoint(waypoint);\n if (hardWaypoint) {\n hardWaypointIndex++;\n }\n const title = buildWaypointTitle(waypoint);\n const id = (waypoint.id as string) ?? generateId();\n return {\n ...waypoint,\n ...(options?.entryPoints === 'main-when-available' && {\n geometry: {\n type: 'Point',\n // We replace the waypoint coordinates with their main entry point ones:\n coordinates: getPosition(waypoint, { useEntryPoint: 'main-when-available' }),\n } as Point,\n }),\n id,\n properties: {\n ...waypoint.properties,\n id,\n index,\n indexType,\n ...(title && { title }),\n iconID: getImageIDForWaypoint(waypoint, indexType, instanceIndex),\n ...(hardWaypoint && indexType === MIDDLE_INDEX && { stopDisplayIndex: hardWaypointIndex }),\n },\n };\n })\n .filter((feature) => feature),\n };\n};\n","import { generateId, Route, Routes, SectionProps, SectionType } from '@tomtom-org/maps-sdk/core';\nimport type { DisplayRouteProps } from '../types/displayRoutes';\nimport type { DisplaySectionProps, RouteSection, RouteSections } from '../types/routeSections';\n\nconst buildRouteSectionsFromRoute = <\n S extends SectionProps = SectionProps,\n D extends DisplaySectionProps = DisplaySectionProps,\n>(\n route: Route<DisplayRouteProps>,\n sectionType: SectionType,\n displaySectionPropsBuilder?: (\n sectionProps: S,\n routeProps?: DisplayRouteProps,\n ) => Omit<D, 'routeState' | 'routeIndex'>,\n): RouteSection<D>[] =>\n (route.properties.sections[sectionType] as S[])?.map((sectionProps) => {\n const id = sectionProps.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: {\n type: 'LineString',\n coordinates: route.geometry.coordinates.slice(\n sectionProps.startPointIndex,\n sectionProps.endPointIndex + 1,\n ),\n },\n properties: {\n ...(displaySectionPropsBuilder\n ? displaySectionPropsBuilder(sectionProps, route.properties)\n : sectionProps),\n routeState: route.properties.routeState,\n routeIndex: route.properties.index,\n id, // we need id in properties due to promoteId feature\n } as D,\n };\n }) || [];\n\n/**\n * Builds display-ready LineString features to render the sections of a given type, from the given route.\n * @param routes The routes from which to extract the section props.\n * @param sectionType The type of the sections to extract.\n * @param displaySectionPropsBuilder An optional function which will convert each section props into an extended display-ready version.\n * @ignore\n */\nexport const toDisplayRouteSections = <\n S extends SectionProps = SectionProps,\n D extends DisplaySectionProps = DisplaySectionProps,\n>(\n routes: Routes<DisplayRouteProps>,\n sectionType: SectionType,\n displaySectionPropsBuilder?: (\n sectionProps: S,\n routeProps?: DisplayRouteProps,\n ) => Omit<D, 'routeState' | 'routeIndex'>,\n): RouteSections<D> => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap((route) =>\n buildRouteSectionsFromRoute<S, D>(route, sectionType, displaySectionPropsBuilder),\n ),\n});\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FeatureCollection, Geometry } from 'geojson';\nimport type { GeoJSONSourceWithLayers } from '../../shared';\nimport type { DisplayRouteProps, DisplayRouteRelatedProps } from '../types/displayRoutes';\n\n/**\n * Rebuilds route-related display features such as sections or instructions, with the updated route selection based on the given routes.\n * @ignore\n */\nexport const rebuildFeaturesWithRouteSelection = <T extends FeatureCollection<Geometry, DisplayRouteRelatedProps>>(\n routes: Routes<DisplayRouteProps>,\n featureCollection: T,\n): T => ({\n ...featureCollection,\n features: featureCollection.features.map((features) => ({\n ...features,\n properties: {\n ...features.properties,\n routeState: routes.features[features.properties.routeIndex || 0].properties.routeState,\n },\n })),\n});\n\n/**\n * @ignore\n */\nexport const showFeaturesWithRouteSelection = <T extends FeatureCollection<Geometry, DisplayRouteRelatedProps>>(\n routesWithSelection: Routes<DisplayRouteProps>,\n sourceWithLayers: GeoJSONSourceWithLayers<T>,\n): void =>\n sourceWithLayers.show(rebuildFeaturesWithRouteSelection(routesWithSelection, sourceWithLayers.shownFeatures));\n","import {\n DelayMagnitude,\n DisplayUnits,\n formatDistance,\n formatDuration,\n generateId,\n Route,\n Routes,\n TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { DisplayRouteProps, DisplayRouteSummaries } from '../types/displayRoutes';\n\n/**\n * Builds map display-ready routes, applying default style props.\n * @ignore\n */\nexport const toDisplayRoutes = (routes: Route | Routes, selectedIndex = 0): Routes<DisplayRouteProps> => {\n const routesCollection: Routes = 'features' in routes ? routes : { type: 'FeatureCollection', features: [routes] };\n return {\n ...routesCollection,\n features: routesCollection.features.map((route, index) => {\n const id = route.id ?? generateId();\n return {\n ...route,\n id,\n properties: {\n ...route.properties,\n id, // we need id in properties due to promoteId feature\n routeState: index === selectedIndex ? 'selected' : 'deselected',\n },\n };\n }),\n };\n};\n\nconst hasMagnitude = (sections: TrafficSectionProps[], magnitude: DelayMagnitude): boolean =>\n sections.some((section) => section.magnitudeOfDelay === magnitude);\n\nconst summaryDelayMagnitude = (route: Route): DelayMagnitude | undefined => {\n const trafficSections = route.properties.sections.traffic;\n if (!trafficSections?.length) {\n return undefined;\n }\n if (hasMagnitude(trafficSections, 'major')) {\n return 'major';\n }\n if (hasMagnitude(trafficSections, 'moderate')) {\n return 'moderate';\n }\n if (hasMagnitude(trafficSections, 'minor')) {\n return 'minor';\n }\n return undefined;\n};\n/**\n * Builds map display-ready route summaries based on display routes.\n * @ignore\n */\nexport const toDisplayRouteSummaries = (\n routes: Routes<DisplayRouteProps>,\n displayUnits?: DisplayUnits,\n): DisplayRouteSummaries => ({\n type: 'FeatureCollection',\n features: routes.features.map((route) => {\n const summary = route.properties.summary;\n const routeCoordinates = route.geometry.coordinates;\n const formattedTraffic = formatDuration(summary.trafficDelayInSeconds, displayUnits?.time);\n const magnitudeOfDelay = summaryDelayMagnitude(route);\n const id = route.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: {\n type: 'Point',\n coordinates: routeCoordinates[Math.round(routeCoordinates.length / 2)],\n },\n properties: {\n id, // we need id in properties due to promoteId feature\n routeIndex: route.properties.index,\n routeState: route.properties.routeState,\n formattedDistance: formatDistance(summary.lengthInMeters, displayUnits?.distance),\n ...(magnitudeOfDelay && { magnitudeOfDelay }),\n ...(formattedTraffic && { formattedTraffic }),\n formattedDuration: formatDuration(summary.travelTimeInSeconds, displayUnits?.time),\n },\n };\n }),\n});\n","import type { Route, Routes, Waypoint, Waypoints } from '@tomtom-org/maps-sdk/core';\nimport { isEqual } from 'lodash-es';\nimport type { StyleImageMetadata } from 'maplibre-gl';\nimport {\n AbstractMapModule,\n EventsModule,\n GeoJSONSourceWithLayers,\n mapStyleLayerIDs,\n SVGIconStyleOptions,\n} from '../shared';\nimport { suffixNumber } from '../shared/layers/utils';\nimport { addLayers, addOrUpdateImage, updateLayersAndSource, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { INSTRUCTION_ARROW_IMAGE_ID } from './layers/guidanceLayers';\nimport { DESELECTED_SUMMARY_POPUP_IMAGE_ID, SELECTED_SUMMARY_POPUP_IMAGE_ID } from './layers/routeMainLineLayers';\nimport { MAJOR_DELAY_COLOR, MINOR_DELAY_LABEL_COLOR, MODERATE_DELAY_COLOR, UNKNOWN_DELAY_COLOR } from './layers/shared';\nimport {\n TRAFFIC_CLEAR_IMAGE_ID,\n TRAFFIC_MAJOR_IMAGE_ID,\n TRAFFIC_MINOR_IMAGE_ID,\n TRAFFIC_MODERATE_IMAGE_ID,\n} from './layers/summaryBubbleLayers';\nimport {\n WAYPOINT_FINISH_IMAGE_ID,\n WAYPOINT_SOFT_IMAGE_ID,\n WAYPOINT_START_IMAGE_ID,\n WAYPOINT_STOP_IMAGE_ID,\n} from './layers/waypointLayers';\nimport {\n instructionArrowIconImg,\n softWaypointIcon,\n summaryBubbleImageOptions,\n summaryMapBubbleImg,\n trafficImg,\n waypointFinishIcon,\n waypointIcon,\n waypointStartIcon,\n} from './resources';\nimport type { DisplayRouteProps, DisplayRouteSummary } from './types/displayRoutes';\nimport type { DisplayInstruction } from './types/guidance';\nimport type { PlanningWaypoint } from './types/planningWaypoint';\nimport type { RoutingModuleConfig } from './types/routeModuleConfig';\nimport type { DisplayTrafficSectionProps, RouteSection, RouteSections } from './types/routeSections';\nimport type { RoutingLayersSpecs, RoutingSourcesWithLayers } from './types/routingSourcesAndLayers';\nimport type { ShowRoutesOptions } from './types/showOptions';\nimport type { WaypointDisplayProps } from './types/waypointDisplayProps';\nimport { createLayersSpecs, routeModuleConfigWithDefaults } from './util/config';\nimport { toDisplayChargingStops } from './util/displayChargingStops';\nimport { toDisplayTrafficSectionProps } from './util/displayTrafficSectionProps';\nimport { toDisplayWaypoints } from './util/displayWaypoints';\nimport { toDisplayInstructionArrows, toDisplayInstructions } from './util/guidance';\nimport { toDisplayRouteSections } from './util/routeSections';\nimport { showFeaturesWithRouteSelection } from './util/routeSelection';\nimport { toDisplayRouteSummaries, toDisplayRoutes } from './util/routes';\n\n/**\n * Map module for displaying and managing route visualizations.\n *\n * The RoutingModule provides comprehensive functionality for displaying routes on the map,\n * including route lines, alternative routes, turn-by-turn guidance, and interactive waypoints.\n * It integrates seamlessly with the TomTom Routing API.\n *\n * @remarks\n * **Features:**\n * - Display route lines with customizable styling\n * - Show alternative routes with different styling\n * - Interactive waypoint markers (drag, add, remove)\n * - Turn-by-turn guidance instructions\n * - Route section highlighting\n * - Distance and duration information\n * - Support for multiple routes simultaneously\n *\n * **Common Use Cases:**\n * - Turn-by-turn navigation displays\n * - Route planning and comparison\n * - Multi-stop route optimization\n * - Interactive route editing\n * - Fleet management route visualization\n *\n * @example\n * ```typescript\n * // Create the module\n * const routingModule = await RoutingModule.getInstance(map, {\n * displayUnits: {\n * distance: { type: 'metric' }\n * }\n * });\n *\n * // Calculate and display a route\n * const result = await calculateRoute({\n * key: 'your-api-key',\n * locations: [\n * [4.9041, 52.3676], // Amsterdam\n * [4.4777, 51.9244] // Rotterdam\n * ],\n * routeOptions: {\n * travelMode: 'car',\n * routeType: 'fastest'\n * }\n * });\n *\n * await routingModule.showRoutes(result);\n * ```\n *\n * @see [Routing Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/routing)\n *\n * @group Routing\n */\nexport class RoutingModule extends AbstractMapModule<RoutingSourcesWithLayers, RoutingModuleConfig> {\n private static lastInstanceIndex = -1;\n private layersSpecs!: RoutingLayersSpecs;\n private layerIDPrefix!: string;\n /**\n * The index of this instance, to generate unique source and layer IDs.\n * * Starts with 0 and each instance increments it by one.\n * @private\n */\n private instanceIndex!: number;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n *\n * @remarks\n * **Configuration Options:**\n * - `displayUnits`: Distance units (metric/imperial)\n * - `waypointsSource`: Waypoint entry point options\n * - `layers`: Complete layer styling configuration\n *\n * **Default Styling:**\n * If no custom layers are provided, uses {@link defaultRoutingLayers}.\n *\n * @example\n * Default initialization:\n * ```typescript\n * const routingModule = await RoutingModule.get(map);\n * ```\n *\n * @example\n * With custom configuration:\n * ```typescript\n * const routingModule = await RoutingModule.get(map, {\n * displayUnits: 'imperial',\n * waypointsSource: {\n * entryPoints: 'main-when-available'\n * }\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: RoutingModuleConfig): Promise<RoutingModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new RoutingModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: RoutingModuleConfig) {\n super('geojson', map, config);\n }\n\n private createSourcesWithLayers(layersSpecs: RoutingLayersSpecs): RoutingSourcesWithLayers {\n const sourcePrefix = suffixNumber('routes', this.instanceIndex);\n return {\n mainLines: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-mainLines`,\n layersSpecs.mainLines,\n false,\n ),\n waypoints: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-waypoints`,\n layersSpecs.waypoints,\n false,\n ),\n incidents: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-incidents`,\n layersSpecs.incidents,\n false,\n ),\n ferries: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-ferries`,\n layersSpecs.ferries,\n false,\n ),\n chargingStops: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-chargingStops`,\n layersSpecs.chargingStops,\n false,\n ),\n tollRoads: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-tollRoads`,\n layersSpecs.tollRoads,\n false,\n ),\n tunnels: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-tunnels`,\n layersSpecs.tunnels,\n false,\n ),\n vehicleRestricted: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-vehicleRestricted`,\n layersSpecs.vehicleRestricted,\n false,\n ),\n instructionLines: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-instructionLines`,\n layersSpecs.instructionLines,\n false,\n ),\n instructionArrows: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-instructionArrows`,\n layersSpecs.instructionArrows,\n false,\n ),\n summaryBubbles: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-summaryBubbles`,\n layersSpecs.summaryBubbles,\n false,\n ),\n };\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: RoutingModuleConfig, restore?: boolean): RoutingSourcesWithLayers {\n // Only increment the instance index for new instances, not for restore operations\n if (!restore) {\n RoutingModule.lastInstanceIndex++;\n this.instanceIndex = RoutingModule.lastInstanceIndex;\n this.layerIDPrefix = suffixNumber('routes', this.instanceIndex);\n }\n\n this.layersSpecs = createLayersSpecs(\n routeModuleConfigWithDefaults(config, this.layerIDPrefix, this.instanceIndex).layers,\n this.layerIDPrefix,\n );\n const routingSourcesWithLayers: RoutingSourcesWithLayers = this.createSourcesWithLayers(this.layersSpecs);\n addLayers(\n Object.values(routingSourcesWithLayers).flatMap((source) => source._layerSpecs),\n this.mapLibreMap,\n );\n\n const svgIconOptions: SVGIconStyleOptions = {\n // first comes the main theme fill color, if any:\n fillColor: config?.theme?.mainColor,\n // then come any icon style configs for the waypoint icons, if any:\n ...config?.waypoints?.icon?.style,\n };\n const options: Partial<StyleImageMetadata> = { pixelRatio: 2 };\n\n // Generate instance-specific image IDs to support multiple RoutingModule instances\n const waypointStartImageId = suffixNumber(WAYPOINT_START_IMAGE_ID, this.instanceIndex);\n const waypointStopImageId = suffixNumber(WAYPOINT_STOP_IMAGE_ID, this.instanceIndex);\n const waypointSoftImageId = suffixNumber(WAYPOINT_SOFT_IMAGE_ID, this.instanceIndex);\n const waypointFinishImageId = suffixNumber(WAYPOINT_FINISH_IMAGE_ID, this.instanceIndex);\n const instructionArrowImageId = suffixNumber(INSTRUCTION_ARROW_IMAGE_ID, this.instanceIndex);\n const selectedSummaryPopupImageId = suffixNumber(SELECTED_SUMMARY_POPUP_IMAGE_ID, this.instanceIndex);\n const deselectedSummaryPopupImageId = suffixNumber(DESELECTED_SUMMARY_POPUP_IMAGE_ID, this.instanceIndex);\n const trafficClearImageId = suffixNumber(TRAFFIC_CLEAR_IMAGE_ID, this.instanceIndex);\n const trafficMajorImageId = suffixNumber(TRAFFIC_MAJOR_IMAGE_ID, this.instanceIndex);\n const trafficModerateImageId = suffixNumber(TRAFFIC_MODERATE_IMAGE_ID, this.instanceIndex);\n const trafficMinorImageId = suffixNumber(TRAFFIC_MINOR_IMAGE_ID, this.instanceIndex);\n\n // loading of extra assets if not present in the map style:\n this.addImageIfNotExisting(waypointStartImageId, waypointStartIcon(svgIconOptions), options);\n this.addImageIfNotExisting(waypointStopImageId, waypointIcon(undefined, svgIconOptions), options);\n this.addImageIfNotExisting(waypointSoftImageId, softWaypointIcon(), options);\n this.addImageIfNotExisting(waypointFinishImageId, waypointFinishIcon(svgIconOptions), options);\n this.addImageIfNotExisting(instructionArrowImageId, instructionArrowIconImg, options);\n this.addImageIfNotExisting(\n selectedSummaryPopupImageId,\n summaryMapBubbleImg('white'),\n summaryBubbleImageOptions,\n );\n this.addImageIfNotExisting(\n deselectedSummaryPopupImageId,\n summaryMapBubbleImg('#EEEEEE'),\n summaryBubbleImageOptions,\n );\n this.addImageIfNotExisting(trafficClearImageId, trafficImg(UNKNOWN_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficMajorImageId, trafficImg(MAJOR_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficModerateImageId, trafficImg(MODERATE_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficMinorImageId, trafficImg(MINOR_DELAY_LABEL_COLOR), options);\n\n // If we have custom icons, ensure they're added to the map style:\n for (const customChargingStopIcon of config?.chargingStops?.icon?.customIcons ?? []) {\n this.addImageIfNotExisting(customChargingStopIcon.id, customChargingStopIcon.image as string, {\n pixelRatio: customChargingStopIcon.pixelRatio ?? 2,\n });\n }\n\n return routingSourcesWithLayers;\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config?: RoutingModuleConfig) {\n const mergedConfig = routeModuleConfigWithDefaults(config, this.layerIDPrefix, this.instanceIndex);\n\n // If there was already some config set, we must update the changes:\n if (this.config) {\n // replace existing configuration with new one\n const newLayersSpecs = createLayersSpecs(mergedConfig.layers, this.layerIDPrefix);\n\n // here we assume that keys for layer specs and sources are the same, please keep it that way to simplify the logic\n Object.keys(newLayersSpecs).forEach((layersSpecID) => {\n const id = layersSpecID as keyof RoutingSourcesWithLayers;\n updateLayersAndSource(\n newLayersSpecs[id],\n this.layersSpecs[id],\n this.sourcesWithLayers[id],\n this.mapLibreMap,\n );\n });\n // we need to add layers correctly\n const listOfSources = Object.values(this.sourcesWithLayers) as GeoJSONSourceWithLayers[];\n addLayers(\n listOfSources.flatMap((source) => source._layerSpecs),\n this.mapLibreMap,\n );\n // set the correct visibility if there are new layers\n listOfSources.forEach((source) => source.setLayersVisible(!!source.shownFeatures.features.length));\n this.layersSpecs = newLayersSpecs;\n }\n\n // Summary bubbles have dedicated sources and contain distance-units dependent text ...\n // ... so we need to re-show them if that config part changed:\n if (\n !isEqual(this.config?.displayUnits, mergedConfig.displayUnits) &&\n this.sourcesWithLayers.summaryBubbles.shownFeatures.features.length\n ) {\n this.sourcesWithLayers.summaryBubbles.show(\n toDisplayRouteSummaries(this.sourcesWithLayers.mainLines.shownFeatures, mergedConfig.displayUnits),\n );\n }\n\n return mergedConfig;\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previouslyShown = Object.entries(this.sourcesWithLayers)\n .map((entry) => ({\n [entry[0]]: entry[1].shownFeatures,\n }))\n .reduce((acc, item) => ({ ...acc, ...item }), {}) as Record<keyof RoutingSourcesWithLayers, any>;\n\n this.initSourcesWithLayers(this.config, true);\n this._applyConfig(this.config);\n\n for (const key of Object.keys(previouslyShown) as (keyof RoutingSourcesWithLayers)[]) {\n this.sourcesWithLayers[key].show(previouslyShown[key]);\n }\n }\n\n private addImageIfNotExisting(\n imageId: string,\n image: string | HTMLImageElement,\n options: Partial<StyleImageMetadata> | undefined,\n ) {\n addOrUpdateImage('if-not-in-sprite', imageId, image, this.mapLibreMap, options);\n }\n\n /**\n * Displays the given routes on the map.\n *\n * @param routes - Route data from Routing API or custom routes.\n * @param options - Optional configuration for route selection and display.\n * @param options.selectedIndex - Index of the route to display as selected (default: 0).\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown routes\n * - Shows all route-related features: lines, sections, summaries, guidance\n * - First route is selected by default (appears more prominent)\n * - Waypoints are NOT shown automatically (use {@link showWaypoints})\n *\n * **Route Features:**\n * - Main route lines (selected and deselected styles)\n * - Traffic sections with delays\n * - Ferry, tunnel, and toll sections\n * - EV charging stations (for EV routes)\n * - Turn-by-turn instruction lines and arrows\n * - Summary bubbles with distance/time/traffic info\n *\n * @example\n * Show single route:\n * ```typescript\n * await routing.showRoutes(response.routes);\n * ```\n *\n * @example\n * Show multiple routes with specific selection:\n * ```typescript\n * await routing.showRoutes(response.routes, { selectedIndex: 1 });\n * ```\n *\n * @example\n * Complete routing workflow:\n * ```typescript\n * import { routing as routingAPI } from '@tomtom-international/maps-sdk-js/services';\n *\n * // Calculate route\n * const response = await routingAPI.calculateRoute({\n * locations: [[4.9, 52.4], [4.5, 51.9]],\n * traffic: true,\n * travelMode: 'car'\n * });\n *\n * // Display on map\n * const routing = await RoutingModule.get(map);\n * await routing.showRoutes(response.routes);\n * await routing.showWaypoints(response.routes[0].legs[0].points);\n * ```\n */\n async showRoutes(routes: Route | Routes, options?: ShowRoutesOptions) {\n const displayRoutes = toDisplayRoutes(routes, options?.selectedIndex);\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.mainLines.show(displayRoutes);\n this.sourcesWithLayers.vehicleRestricted.show(toDisplayRouteSections(displayRoutes, 'vehicleRestricted'));\n this.sourcesWithLayers.incidents.show(\n toDisplayRouteSections(displayRoutes, 'traffic', toDisplayTrafficSectionProps),\n );\n this.sourcesWithLayers.chargingStops.show(toDisplayChargingStops(displayRoutes, this.config));\n this.sourcesWithLayers.ferries.show(toDisplayRouteSections(displayRoutes, 'ferry'));\n this.sourcesWithLayers.tunnels.show(toDisplayRouteSections(displayRoutes, 'tunnel'));\n this.sourcesWithLayers.tollRoads.show(toDisplayRouteSections(displayRoutes, 'toll'));\n this.sourcesWithLayers.instructionLines.show(toDisplayInstructions(displayRoutes));\n this.sourcesWithLayers.instructionArrows.show(toDisplayInstructionArrows(displayRoutes));\n this.sourcesWithLayers.summaryBubbles.show(toDisplayRouteSummaries(displayRoutes, this.config?.displayUnits));\n }\n\n /**\n * Clears any previously shown routes from the map.\n *\n * @remarks\n * - Clears all route-related layers (lines, sections, guidance, summaries)\n * - Does NOT clear waypoints (use {@link clearWaypoints})\n * - Module remains initialized and ready for new routes\n *\n * @example\n * ```typescript\n * await routing.clearRoutes();\n * ```\n */\n async clearRoutes() {\n await this.waitUntilModuleReady();\n for (const key of Object.keys(this.sourcesWithLayers) as (keyof RoutingSourcesWithLayers)[]) {\n if (key !== 'waypoints') {\n this.sourcesWithLayers[key as keyof RoutingSourcesWithLayers].clear();\n }\n }\n }\n\n /**\n * Changes which route appears as selected.\n *\n * @param index - Zero-based index of the route to select.\n *\n * @remarks\n * **Visual Changes:**\n * - Selected route appears more prominent (thicker, brighter)\n * - Previously selected route becomes deselected style\n * - Updates all route-related features (sections, guidance)\n *\n * **Requirements:**\n * - Route must already be displayed via {@link showRoutes}\n * - Index must be within range of displayed routes\n *\n * @example\n * ```typescript\n * // Show multiple routes\n * await routingModule.showRoutes(routes);\n *\n * // User clicks alternative route\n * await routingModule.selectRoute(1);\n *\n * // Switch back to first route\n * await routingModule.selectRoute(0);\n * ```\n */\n async selectRoute(index: number) {\n const updatedRoutes = toDisplayRoutes(this.sourcesWithLayers.mainLines.shownFeatures, index);\n\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.mainLines.show(updatedRoutes);\n // TODO: simply update route style instead of regenerating EV stations again\n this.sourcesWithLayers.chargingStops.show(toDisplayChargingStops(updatedRoutes, this.config));\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.vehicleRestricted);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.incidents);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.ferries);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.tollRoads);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.tunnels);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.instructionLines);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.instructionArrows);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.summaryBubbles);\n }\n\n /**\n * Shows the given waypoints on the map.\n * @param waypoints The waypoint-like inputs to show.\n */\n async showWaypoints(waypoints: PlanningWaypoint[] | Waypoints) {\n const displayWaypoints = Array.isArray(waypoints)\n ? toDisplayWaypoints(waypoints, this.config?.waypoints, this.instanceIndex)\n : // FeatureCollection expected:\n toDisplayWaypoints(waypoints.features as PlanningWaypoint[], this.config?.waypoints, this.instanceIndex);\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.waypoints.show(displayWaypoints);\n }\n\n /**\n * Clears any previously shown waypoints from the map.\n * * If nothing was shown before, nothing happens.\n */\n async clearWaypoints() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.waypoints.clear();\n }\n\n /**\n * Returns the currently shown routes and waypoints.\n *\n * @returns An object containing all currently displayed routing data.\n *\n * @remarks\n * Returns the exact data that was passed to the `showRoutes()` and `showWaypoints()` methods.\n *\n * **Returned Data:**\n * - `mainLines`: Main route lines (selected and alternative)\n * - `waypoints`: Route waypoints (start, stops, finish)\n * - `incidents`: Traffic incidents on routes\n * - `ferries`: Ferry sections\n * - `chargingStops`: EV charging stations\n * - `tollRoads`: Toll road sections\n * - `tunnels`: Tunnel sections\n * - `vehicleRestricted`: Vehicle-restricted sections\n * - `instructionLines`: Turn-by-turn instruction lines\n * - `instructionArrows`: Instruction arrow markers\n * - `summaryBubbles`: Route summary popups\n *\n * @example\n * ```typescript\n * const shown = routingModule.getShown();\n * console.log(`Showing ${shown.mainLines.features.length} routes`);\n * console.log(`Showing ${shown.waypoints.features.length} waypoints`);\n * console.log(`Showing ${shown.chargingStops.features.length} charging stops`);\n * ```\n *\n * @example\n * Check if any routes are displayed:\n * ```typescript\n * const shown = routingModule.getShown();\n * if (shown.mainLines.features.length > 0) {\n * console.log('Routes are displayed');\n * } else {\n * console.log('No routes displayed');\n * }\n * ```\n */\n getShown() {\n return {\n mainLines: this.sourcesWithLayers.mainLines.shownFeatures,\n waypoints: this.sourcesWithLayers.waypoints.shownFeatures,\n incidents: this.sourcesWithLayers.incidents.shownFeatures,\n ferries: this.sourcesWithLayers.ferries.shownFeatures,\n chargingStops: this.sourcesWithLayers.chargingStops.shownFeatures,\n tollRoads: this.sourcesWithLayers.tollRoads.shownFeatures,\n tunnels: this.sourcesWithLayers.tunnels.shownFeatures,\n vehicleRestricted: this.sourcesWithLayers.vehicleRestricted.shownFeatures,\n instructionLines: this.sourcesWithLayers.instructionLines.shownFeatures,\n instructionArrows: this.sourcesWithLayers.instructionArrows.shownFeatures,\n summaryBubbles: this.sourcesWithLayers.summaryBubbles.shownFeatures,\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return {\n mainLines: new EventsModule<Route<DisplayRouteProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.mainLines,\n this.config?.events,\n ),\n waypoints: new EventsModule<Waypoint<WaypointDisplayProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.waypoints,\n this.config?.events,\n ),\n chargingStops: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.chargingStops,\n this.config?.events,\n ),\n summaryBubbles: new EventsModule<DisplayRouteSummary>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.summaryBubbles,\n this.config?.events,\n ),\n incidents: new EventsModule<RouteSections<DisplayTrafficSectionProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.incidents,\n this.config?.events,\n ),\n vehicleRestricted: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.vehicleRestricted,\n this.config?.events,\n ),\n ferries: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.ferries,\n this.config?.events,\n ),\n tollRoads: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.tollRoads,\n this.config?.events,\n ),\n tunnels: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.tunnels,\n this.config?.events,\n ),\n instructionLines: new EventsModule<DisplayInstruction>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.instructionLines,\n this.config?.events,\n ),\n };\n }\n\n /**\n * Returns the map style layer under which route lines are rendered.\n * * Useful if you want to render extra layers just above the route ones but not on top of everything else.\n * * It might differ depending on the loaded style/version.\n */\n getLayerToRenderLinesUnder(): string {\n return mapStyleLayerIDs.lowestLabel;\n }\n}\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path d=\\\"M63.734 86.383c-1.404.026-2.78-.393-3.93-1.197s-2.01-1.949-2.46-3.271l-3.15-9.339c-1.093-3.403-3.748-6.089-7.154-7.238L37.5 62.21c-2.742-.902-4.567-3.477-4.5-6.345-.058-2.874 1.761-5.457 4.5-6.388l36-11.886c2.461-.88 5.213-.27 7.062 1.567s2.466 4.571 1.578 7.015L70.17 81.918c-.938 2.719-3.54 4.526-6.436 4.469h0z\\\"\\n fill=\\\"#f4f5f6\\\"/>\\n</svg>\\n\"","export default \"<svg width=\\\"32\\\" height=\\\"32\\\" viewBox=\\\"0 0 32 32\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n <circle id=\\\"circle\\\" cx=\\\"16\\\" cy=\\\"16\\\" r=\\\"12\\\" fill=\\\"white\\\" stroke=\\\"#105287\\\" stroke-width=\\\"4\\\"/>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path d=\\\"M33 83.404a2.99 2.99 0 0 1-3-2.978V44.681a2.99 2.99 0 1 1 6 0v35.745a2.99 2.99 0 0 1-3 2.978zm48-38.723H39v17.872h42V44.681zm3.878 37.851a3.02 3.02 0 0 0 4.244 0c.561-.559.878-1.317.878-2.106V44.681a2.99 2.99 0 1 0-6 0v35.745c0 .789.317 1.547.878 2.106z\\\"\\n fill=\\\"#f4f5f6\\\"/>\\n</svg>\\n\"","import { generateId, Routes } from '@tomtom-org/maps-sdk/core';\nimport { bearing } from '@turf/turf';\nimport type { DisplayRouteProps } from '../types/displayRoutes';\nimport type {\n DisplayInstruction,\n DisplayInstructionArrow,\n DisplayInstructionArrows,\n DisplayInstructions,\n} from '../types/guidance';\n\n/**\n * @ignore\n */\nexport const toDisplayInstructions = (routes: Routes<DisplayRouteProps>): DisplayInstructions => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap(\n (route, routeIndex) =>\n route.properties.guidance?.instructions\n ?.filter((instruction) => instruction.routePath?.length)\n .map(\n (instruction): DisplayInstruction => ({\n type: 'Feature',\n geometry: {\n type: 'LineString',\n coordinates: instruction.routePath.map((pathPoint) => pathPoint.point),\n },\n properties: {\n ...instruction,\n id: generateId(),\n routeIndex,\n routeState: route.properties.routeState,\n },\n }),\n ) || [],\n ),\n});\n\n/**\n * @ignore\n */\nexport const toDisplayInstructionArrows = (routes: Routes<DisplayRouteProps>): DisplayInstructionArrows => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap(\n (route, routeIndex) =>\n route.properties.guidance?.instructions\n ?.filter((instruction) => instruction.routePath?.length && instruction.routePath.length > 1)\n .map((instruction): DisplayInstructionArrow => {\n const instructionLastSegment = [\n instruction.routePath[instruction.routePath.length - 2]?.point,\n instruction.routePath[instruction.routePath.length - 1]?.point,\n ];\n\n return {\n type: 'Feature',\n geometry: { type: 'Point', coordinates: instructionLastSegment[1] },\n properties: {\n ...instruction,\n id: generateId(),\n routeIndex,\n routeState: route.properties.routeState,\n lastPointBearingDegrees: bearing(instructionLastSegment[0], instructionLastSegment[1]),\n },\n };\n }) || [],\n ),\n});\n","import type { StyleSpecification } from 'maplibre-gl';\nimport type { InternalTomTomMapParams, StandardStyle, StandardStyleID, StyleInput, StyleModule } from './types/mapInit';\nimport { styleModules } from './types/mapInit';\n\nexport const DEFAULT_STANDARD_STYLE_ID: StandardStyleID = 'standardLight';\nconst URL_PREFIX = '${baseURL}/maps/orbis/assets/styles/${version}/style.json?&apiVersion=1&key=${apiKey}';\n\nconst standardStyleModulesValues: Record<StandardStyleID, Record<StyleModule, string>> = {\n standardLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_light',\n },\n standardDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_dark',\n },\n drivingLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_light',\n },\n drivingDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_dark',\n },\n monoLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_mono-light',\n },\n monoDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_mono-dark',\n },\n satellite: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_satellite',\n },\n};\n\nconst baseMapStyleUrlTemplate = (suffix: string): string => `${URL_PREFIX}&map=${suffix}`;\n\nconst baseMapStyleUrlTemplates: Record<StandardStyleID, string> = {\n standardLight: baseMapStyleUrlTemplate('basic_street-light'),\n standardDark: baseMapStyleUrlTemplate('basic_street-dark'),\n drivingLight: baseMapStyleUrlTemplate('basic_street-light-driving'),\n drivingDark: baseMapStyleUrlTemplate('basic_street-dark-driving'),\n monoLight: baseMapStyleUrlTemplate('basic_mono-light'),\n monoDark: baseMapStyleUrlTemplate('basic_mono-dark'),\n satellite: baseMapStyleUrlTemplate('basic_street-satellite'),\n};\n\nconst buildStandardStyleUrl = (standardStyle: StandardStyle, baseUrl: string, apiKey: string): string => {\n const standardStyleID = standardStyle.id ?? DEFAULT_STANDARD_STYLE_ID;\n\n const styleURL = new URL(\n baseMapStyleUrlTemplates[standardStyleID]\n .replace('${baseURL}', baseUrl)\n .replace('${version}', standardStyle.version ?? '0.6.0-0')\n .replace('${apiKey}', apiKey),\n );\n\n for (const module of standardStyle.include ?? styleModules) {\n styleURL.searchParams.append(module, standardStyleModulesValues[standardStyleID][module]);\n }\n\n return styleURL.toString();\n};\n\nconst withApiKey = (givenUrl: string, apiKey: string): string => {\n const url = new URL(givenUrl);\n if (!url.searchParams.has('key')) {\n url.searchParams.set('key', apiKey);\n } else {\n console.warn(\n 'The style URL is coming with an API key parameter which takes priority. ' +\n 'If you want to use the SDK configured API key, remove the key param from the style URL',\n );\n }\n return url.toString();\n};\n\n/**\n * @ignore\n * @param mapParams The SDK parameters to convert to input renderer style.\n * @return The map style to load into the renderer.\n */\nexport const buildStyleInput = (mapParams: InternalTomTomMapParams): StyleSpecification | string => {\n const style = mapParams.style;\n const baseUrl = mapParams.commonBaseURL;\n const apiKey = mapParams.apiKey;\n\n if (typeof style === 'string') {\n return buildStandardStyleUrl({ id: style }, baseUrl, apiKey);\n } else if (style?.type === 'standard') {\n return buildStandardStyleUrl(style, baseUrl, apiKey);\n } else if (style?.type === 'custom' && style?.url) {\n return withApiKey(style.url, apiKey);\n } else if (style?.type === 'custom' && style?.json) {\n return style.json;\n }\n\n // no style defined, use default\n return buildStandardStyleUrl({ id: DEFAULT_STANDARD_STYLE_ID }, baseUrl, apiKey);\n};\n\n/**\n * Includes the previous standard style parts into the given standard style if the new one didn't define any.\n * * Both new and previous styles must be of \"standard\" type.\n * @ignore\n */\nexport const withPreviousStyleParts = (style: StyleInput, previousStyle?: StyleInput): StyleInput => {\n if (\n previousStyle &&\n typeof previousStyle === 'object' &&\n previousStyle.type === 'standard' &&\n previousStyle.include\n ) {\n if (typeof style === 'string' || (style.type === 'standard' && !style.include)) {\n return {\n type: 'standard',\n id: typeof style === 'string' ? style : style.id,\n include: (previousStyle as StandardStyle).include,\n };\n }\n }\n return style;\n};\n","import { type BBox, type Language, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { isEqual } from 'lodash-es';\nimport { getRTLTextPluginStatus, Map, setRTLTextPlugin, setWorkerCount } from 'maplibre-gl';\nimport { version as maplibreVersion } from 'maplibre-gl/package.json';\nimport type { InternalTomTomMapParams, MapLibreOptions, StyleInput, TomTomMapParams } from './init';\nimport { buildMapOptions } from './init/buildMapOptions';\nimport { buildStyleInput, DEFAULT_STANDARD_STYLE_ID, withPreviousStyleParts } from './init/styleInputBuilder';\nimport {\n EventsProxy,\n filterLayersBySources,\n HILLSHADE_SOURCE_ID,\n LightDark,\n TRAFFIC_FLOW_SOURCE_ID,\n TRAFFIC_INCIDENTS_SOURCE_ID,\n} from './shared';\nimport { isLayerLocalizable } from './shared/localization';\nimport { addPinCategoriesSpriteToStyle, getStyleLightDarkTheme } from './shared/mapUtils';\n\n/**\n * Handler interface for responding to map style changes.\n *\n * @remarks\n * This interface defines callbacks that are invoked when the map style changes via {@link TomTomMap.setStyle}.\n * Use this to perform cleanup or reinitialization of custom map features when styles are switched.\n *\n * **Lifecycle:**\n * 1. `onStyleAboutToChange` - Called before the new style is applied\n * 2. Style change occurs\n * 3. `onStyleChanged` - Called after the new style has been fully loaded\n *\n * **Common Use Cases:**\n * - Saving and restoring custom layers or sources\n * - Reinitializing map modules after style changes\n * - Updating UI components based on the new style\n * - Cleaning up resources tied to the previous style\n *\n * @example\n * ```typescript\n * const styleHandler: StyleChangeHandler = {\n * onStyleAboutToChange: () => {\n * console.log('Style changing - saving state...');\n * // Save custom layer data\n * },\n * onStyleChanged: () => {\n * console.log('Style changed - restoring state...');\n * // Restore custom layers\n * }\n * };\n *\n * map.addStyleChangeHandler(styleHandler);\n * ```\n *\n * @see {@link TomTomMap.addStyleChangeHandler}\n * @see {@link TomTomMap.setStyle}\n *\n * @group Map Style\n */\nexport type StyleChangeHandler = {\n /**\n * Callback invoked immediately before a style change begins.\n *\n * @remarks\n * Use this to perform cleanup or save state before the current style is removed.\n * This method can be synchronous or asynchronous.\n *\n * @returns void or a Promise that resolves when preparation is complete\n */\n onStyleAboutToChange?: () => void | Promise<void>;\n /**\n * Callback invoked after a new style has been fully loaded.\n *\n * @remarks\n * Use this to restore state, reinitialize layers, or perform other setup\n * that depends on the new style being ready. This method can be synchronous or asynchronous.\n *\n * @returns void or a Promise that resolves when reinitialization is complete\n */\n onStyleChanged?: () => void | Promise<void>;\n};\n\n/**\n * Main TomTom Map class for displaying interactive maps in web applications.\n *\n * This is the entry point for rendering TomTom maps. It wraps MapLibre GL JS and provides\n * a simplified, enhanced API for common mapping tasks.\n *\n * @remarks\n * **Key Features:**\n * - Built on MapLibre GL JS for high-performance rendering\n * - Seamless style switching without map reload\n * - Integrated event handling system\n * - Multi-language support with dynamic switching\n * - Compatible with TomTom map modules (traffic, POIs, routing, etc.)\n *\n * **Architecture:**\n * - Exposes the underlying MapLibre Map instance via {@link mapLibreMap}\n * - Manages map lifecycle and style transitions\n * - Coordinates with map modules for data visualization\n *\n * @example\n * Basic map initialization:\n * ```typescript\n * import { TomTomMap } from '@tomtom-international/maps-sdk-js/map';\n *\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: 'standardLight',\n * mapLibre: {\n * container: 'map',\n * center: [4.9041, 52.3676],\n * zoom: 10\n * }\n * });\n * ```\n *\n * @example\n * With modules and configuration:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: {\n * type: 'standard',\n * id: 'standardDark',\n * include: ['trafficFlow', 'trafficIncidents']\n * },\n * language: 'en-US',\n * events: {\n * precisionMode: 'point-then-box',\n * cursorOnHover: 'pointer'\n * },\n * mapLibre: {\n * container: 'map',\n * center: [-74.006, 40.7128],\n * zoom: 12\n * }\n * });\n *\n * // Access MapLibre functionality directly\n * map.mapLibreMap.on('load', () => {\n * console.log('Map loaded');\n * });\n * ```\n *\n * @group Map\n */\nexport class TomTomMap {\n /**\n * Indicates whether the map style has been fully loaded and is ready for interaction.\n *\n * @remarks\n * - `true` when the style is loaded and modules can be safely initialized\n * - `false` during map construction or style changes\n * - Check this before performing style-dependent operations\n *\n * @example\n * ```typescript\n * if (map.mapReady) {\n * // Safe to initialize modules\n * const trafficFlowModule = await TrafficFlowModule.get(map);\n * }\n * ```\n */\n mapReady = false;\n\n /**\n * The underlying MapLibre GL JS Map instance.\n *\n * @remarks\n * **When to Use:**\n * - Access advanced MapLibre functionality not exposed by TomTomMap\n * - Add custom layers, sources, or controls\n * - Listen to MapLibre-specific events\n * - Integrate third-party MapLibre plugins\n *\n * **Important:**\n * - Available immediately after TomTomMap construction\n * - Direct modifications may affect SDK module behavior\n * - Coordinate with SDK modules to avoid conflicts\n *\n * @example\n * Add custom layer:\n * ```typescript\n * map.mapLibreMap.addLayer({\n * id: 'custom-layer',\n * type: 'circle',\n * source: 'my-data',\n * paint: {\n * 'circle-radius': 6,\n * 'circle-color': '#ff0000'\n * }\n * });\n * ```\n *\n * @example\n * Listen to events:\n * ```typescript\n * map.mapLibreMap.on('moveend', () => {\n * console.log('Camera position:', map.mapLibreMap.getCenter());\n * });\n * ```\n *\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/map/ | MapLibre Map Documentation}\n */\n readonly mapLibreMap: Map;\n /**\n * @ignore\n */\n readonly _eventsProxy: EventsProxy;\n /**\n * @ignore\n */\n _params: InternalTomTomMapParams;\n\n styleLightDarkTheme: LightDark;\n\n private readonly styleChangeHandlers: StyleChangeHandler[] = [];\n\n /**\n * Constructs a new TomTom Map instance and attaches it to a DOM element.\n *\n * @param mapParams - Combined TomTom and MapLibre parameters for map initialization.\n * Includes API key, style, events, and MapLibre options like container, center, zoom, etc.\n * See {@link TomTomMapParams} for all available parameters.\n *\n * @remarks\n * **Initialization Process:**\n * 1. Merges `mapParams` with global configuration\n * 2. Creates underlying MapLibre map instance\n * 3. Loads specified style asynchronously\n * 4. Sets `mapReady` to `true` when complete\n *\n * **Configuration Priority:**\n * - Parameters passed here override global configuration\n * - Allows per-map customization while sharing common settings\n *\n * @example\n * Minimal initialization:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * mapLibre: {\n * container: 'map',\n * center: [0, 0],\n * zoom: 2\n * }\n * });\n * ```\n *\n * @example\n * Full configuration:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * },\n * language: 'en-US',\n * events: {\n * precisionMode: 'point-then-box',\n * paddingBoxPx: 10\n * },\n * mapLibre: {\n * container: 'map',\n * center: [-122.4194, 37.7749],\n * zoom: 13,\n * pitch: 45,\n * bearing: -17.6,\n * antialias: true,\n * maxZoom: 18,\n * minZoom: 8\n * }\n * });\n * ```\n *\n * @throws Will log errors if RTL text plugin fails to load (non-blocking)\n *\n * @see {@link MapLibreOptions}\n * @see {@link TomTomMapParams}\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/map/ | MapLibre Map Parameters}\n * @see [Map Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/quickstart)\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n * @see [User Interaction Events Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/user-events)\n */\n constructor(mapParams: TomTomMapParams) {\n this._params = mergeFromGlobal(mapParams);\n if (this._params.style === undefined) {\n this._params = { ...this._params, style: DEFAULT_STANDARD_STYLE_ID };\n }\n this.styleLightDarkTheme = getStyleLightDarkTheme(this._params.style);\n this.ensureMapLibreCSSLoaded();\n\n // Set worker count before creating the Map instance.\n // MapLibre defaults to 1 worker (3 on Safari). 4 workers gives smooth\n // tile-by-tile transitions during style changes without starving the main thread.\n setWorkerCount(4);\n\n this.mapLibreMap = new Map(buildMapOptions(this._params));\n this.mapLibreMap.once('styledata', () => {\n this.handleStyleData(false);\n });\n this._eventsProxy = new EventsProxy(this.mapLibreMap, this._params?.events);\n\n this.loadRTLTextPlugin();\n }\n\n private loadRTLTextPlugin(): void {\n // deferred (just in case), lazy loading of the RTL plugin:\n setTimeout(() => {\n if (!['deferred', 'loaded'].includes(getRTLTextPluginStatus())) {\n setRTLTextPlugin(\n 'https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.3.0/dist/mapbox-gl-rtl-text.js',\n true,\n ).catch((error) => console.error('Something went wrong when setting RTL plugin', error));\n }\n });\n }\n\n /**\n * Dynamically loads the MapLibre CSS stylesheet from CDN.\n */\n private ensureMapLibreCSSLoaded(): void {\n if (typeof document === 'undefined') {\n return;\n }\n // Check if the CSS is already loaded to avoid duplicates:\n const existingLink = Array.from(document.querySelectorAll('link[rel=\"stylesheet\"], style')).some((element) =>\n element.textContent?.includes('.maplibregl-map'),\n );\n if (existingLink) {\n return;\n }\n\n // Create and inject a link tag to load CSS from unpkg CDN\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = `https://unpkg.com/maplibre-gl@${maplibreVersion}/dist/maplibre-gl.css`;\n document.head.appendChild(link);\n }\n\n /**\n * Changes the map style dynamically without reloading the entire map.\n *\n * @param style - The new style to apply. Can be a string ID or a detailed style configuration.\n * @param options - Configuration options for the style change behavior.\n * @param options.keepState - Whether to preserve SDK-rendered items and configurations when changing styles.\n * When `true` (default), maintains traffic layers, routes, markers, and other SDK features.\n * When `false`, performs a clean style switch without preserving previous state.\n *\n * @remarks\n * **Behavior:**\n * - Temporarily sets {@link mapReady} to `false` during the transition\n * - Triggers all registered {@link StyleChangeHandler} callbacks\n * - Resets {@link mapReady} to `true` when the new style is fully loaded\n *\n * **State Preservation (keepState: true):**\n * - Merges style parts from the previous style with the new one\n * - Maintains SDK module layers (traffic, routes, POIs, etc.)\n * - Preserves language settings\n *\n * **Clean Switch (keepState: false):**\n * - Applies the new style without merging previous configuration\n * - Removes all SDK module layers\n * - Useful for complete style resets\n *\n * @example\n * Simple style change:\n * ```typescript\n * // Switch to dark mode\n * map.setStyle('standardDark');\n * ```\n *\n * @example\n * Style change with detailed configuration:\n * ```typescript\n * map.setStyle({\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * });\n * ```\n *\n * @example\n * Clean style switch without state preservation:\n * ```typescript\n * // Complete reset - removes all SDK layers and modules\n * map.setStyle('standardDark', { keepState: false });\n * ```\n *\n * @example\n * With style change handlers:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * console.log('Preparing for style change...');\n * },\n * onStyleChanged: () => {\n * console.log('New style applied!');\n * }\n * });\n *\n * map.setStyle('standardDark');\n * ```\n *\n * @see {@link TomTomMapParams.style} - For setting style during initialization\n * @see {@link StyleChangeHandler} - For handling style change events\n * @see {@link getStyle} - For retrieving the current style\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n */\n setStyle = (style: StyleInput, options: { keepState?: boolean } = { keepState: true }): void => {\n this.mapReady = false;\n\n // Notify all modules that the style is about to change (they mark themselves as not ready).\n for (const handler of this.styleChangeHandlers) {\n try {\n handler.onStyleAboutToChange?.();\n } catch (e) {\n console.error(e);\n }\n }\n const effectiveStyle = options.keepState ? withPreviousStyleParts(style, this._params.style) : style;\n this._params = { ...this._params, style: effectiveStyle };\n this.styleLightDarkTheme = getStyleLightDarkTheme(effectiveStyle);\n this.mapLibreMap.once('styledata', () => {\n // We only handle the style data change if the applied style is still the same as the one we set,\n // to prevent race conditions when handling stale styles applied quickly in succession.\n // (If the current style parameters are different, there's likely a new style being set, which will trigger the handler soon after)\n if (!this.mapReady && isEqual(effectiveStyle, this._params.style)) {\n this.handleStyleData(options.keepState || true);\n }\n });\n this.mapLibreMap.setStyle(buildStyleInput(this._params), { validate: false });\n };\n\n /**\n * Retrieves the current style configuration of the map.\n *\n * @returns The current {@link StyleInput} configuration, or `undefined` if no style is set.\n *\n * @remarks\n * Returns the style configuration as it was set, not the fully resolved MapLibre style object.\n * Use this to inspect or store the current style configuration for later restoration.\n *\n * **Return Value:**\n * - String ID (e.g., `'standardLight'`) for simple style configurations\n * - Style object with `type`, `id`, and optional `include` properties for detailed configurations\n * - `undefined` if no style has been explicitly set\n *\n * @example\n * ```typescript\n * const currentStyle = map.getStyle();\n * console.log('Current style:', currentStyle);\n *\n * // Save style for later\n * const savedStyle = map.getStyle();\n *\n * // Later, restore it\n * if (savedStyle) {\n * map.setStyle(savedStyle);\n * }\n * ```\n *\n * @example\n * Conditional logic based on current style:\n * ```typescript\n * const style = map.getStyle();\n * if (typeof style === 'string' && style.includes('Dark')) {\n * console.log('Dark mode is active');\n * }\n * ```\n *\n * @see {@link setStyle} - For changing the map style\n * @see {@link StyleInput} - For available style configuration options\n */\n getStyle = (): StyleInput | undefined => {\n return this._params.style;\n };\n\n private _setLanguage(language: Language) {\n this._params = { ...this._params, language };\n const mapLanguage = language?.includes('-') ? language.split('-')[0] : language;\n this.mapLibreMap.getStyle().layers.forEach((layer) => {\n if (layer.type === 'symbol' && isLayerLocalizable(layer)) {\n const textFieldValue = mapLanguage\n ? ['coalesce', ['get', `name_${mapLanguage}`], ['get', 'name']]\n : ['get', 'name'];\n this.mapLibreMap.setLayoutProperty(layer.id, 'text-field', textFieldValue, { validate: false });\n }\n });\n }\n\n /**\n * Changes the language of the map.\n * * You can use this method to change the language at runtime.\n * * To set the language upon initialization, you can better do it via {@link core!TomTomConfig global config}\n * or {@link TomTomMapParams}.\n * @param language The language to be used in map translations.\n *\n * @remarks\n * **Behavior:**\n * - Updates all localizable map labels to the specified language\n * - Falls back to the default label name if the requested language is unavailable\n * - Can be called before or after the map is fully loaded\n * - If called before map is ready, will apply once the style loads\n *\n * **Language Format:**\n * - Simple language codes: `'en'`, `'fr'`, `'de'`, `'ja'`, `'zh'`\n * - Locale-specific codes: `'en-US'`, `'en-GB'`, `'zh-CN'`, `'pt-BR'`\n * - When using locale codes (with `-`), only the language portion is used for labels\n *\n * **Persistence:**\n * - Language setting persists across style changes (when `keepState: true`)\n * - Set during initialization via {@link TomTomMapParams.language} for immediate application\n *\n * @example\n * Change language at runtime:\n * ```typescript\n * // Switch to French\n * map.setLanguage('fr');\n * ```\n *\n * @example\n * Use locale-specific codes:\n * ```typescript\n * // Use Simplified Chinese\n * map.setLanguage('zh-CN');\n *\n * // Use Brazilian Portuguese\n * map.setLanguage('pt-BR');\n * ```\n *\n * @example\n * Language switcher UI:\n * ```typescript\n * const languageSelector = document.getElementById('lang-select');\n * languageSelector.addEventListener('change', (e) => {\n * map.setLanguage(e.target.value);\n * });\n * ```\n *\n * @see {@link TomTomMapParams.language} - For setting language during initialization\n * @see {@link https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes | ISO 639-1 Language Codes}\n */\n setLanguage(language: Language) {\n if (this.mapReady) {\n this._setLanguage(language);\n } else {\n this.mapLibreMap.once('styledata', () => this.setLanguage(language));\n }\n }\n\n /**\n * Retrieves the current visible map area as a GeoJSON bounding box.\n *\n * @returns A {@link https://tools.ietf.org/html/rfc7946#section-5 | GeoJSON BBox} array\n * in the format `[west, south, east, north]` representing the map's current viewport bounds.\n *\n * @remarks\n * **Return Format:**\n * - Array of four numbers: `[minLongitude, minLatitude, maxLongitude, maxLatitude]`\n * - Coordinates are in WGS84 decimal degrees\n * - West/East values range from -180 to 180\n * - South/North values range from -90 to 90\n *\n * @example\n * Get current bounds:\n * ```typescript\n * const bbox = map.getBBox();\n * console.log('Bounds:', bbox);\n * // Output: [-122.5, 37.7, -122.3, 37.8]\n * // [west, south, east, north]\n * ```\n *\n * @example\n * Use bounds for spatial query:\n * ```typescript\n * const bbox = map.getBBox();\n * const results = await searchAPI.searchInBoundingBox({\n * bbox: bbox,\n * query: 'restaurants'\n * });\n * ```\n *\n * @example\n * Save and restore map view:\n * ```typescript\n * // Save current view\n * const savedBounds = map.getBBox();\n * const savedZoom = map.mapLibreMap.getZoom();\n *\n * // Later, restore the view\n * const [west, south, east, north] = savedBounds;\n * map.mapLibreMap.fitBounds([[west, south], [east, north]]);\n * ```\n *\n * @see {@link https://tools.ietf.org/html/rfc7946#section-5 | GeoJSON BBox Specification}\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/geography/#lnglatbounds | MapLibre LngLatBounds}\n */\n getBBox(): BBox {\n return this.mapLibreMap.getBounds().toArray().flat() as BBox;\n }\n\n private handleStyleData(keepState: boolean) {\n // We ensure to make traffic and hillshade hidden by default (even if right after the modules bring it back to visible state)\n // This way we ensure such layers are invisible even of their related SDK modules are not used.\n for (const layer of filterLayersBySources(this.mapLibreMap, [\n TRAFFIC_INCIDENTS_SOURCE_ID,\n TRAFFIC_FLOW_SOURCE_ID,\n HILLSHADE_SOURCE_ID,\n ])) {\n this.mapLibreMap.setLayoutProperty(layer.id, 'visibility', 'none', { validate: false });\n }\n\n // For most use cases we'll need to have pins available (places, routing...) so we add them by default:\n // (subsequent loads for the same sprite should be cached)\n addPinCategoriesSpriteToStyle(this._params, this.mapLibreMap);\n // We restore the language if it was set before:\n this._params.language && this._setLanguage(this._params.language);\n\n this.mapReady = true;\n if (keepState) {\n for (const handler of this.styleChangeHandlers) {\n try {\n handler.onStyleChanged?.();\n } catch (e) {\n console.error(e);\n }\n }\n }\n }\n\n /**\n * Registers a handler to be notified when the map style changes.\n *\n * @param handler - A {@link StyleChangeHandler} object with callbacks for style change events.\n *\n * @remarks\n * **When to Use:**\n * - You have custom layers or sources that need to be recreated after style changes\n * - Your application needs to respond to style switches (e.g., light/dark mode transitions)\n * - You need to save and restore state during style changes\n * - Map modules need to reinitialize when styles change\n *\n * **Handler Lifecycle:**\n * 1. `onStyleAboutToChange()` - Called before the style change begins\n * 2. Style change occurs\n * 3. `onStyleChanged()` - Called after the new style has been fully loaded\n *\n * **Multiple Handlers:**\n * - Multiple handlers can be registered and will all be called in registration order\n * - Each handler's errors are caught independently and logged to the console\n * - One failing handler won't prevent others from executing\n *\n * **Important Notes:**\n * - Handlers are only triggered by {@link setStyle} calls, not initial map construction\n * - Only called when `keepState: true` in {@link setStyle} options\n * - Handlers persist for the lifetime of the TomTomMap instance\n *\n * @example\n * Basic usage:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * console.log('Style is changing...');\n * },\n * onStyleChanged: () => {\n * console.log('Style changed successfully!');\n * }\n * });\n *\n * // Later trigger the handlers\n * map.setStyle('standardDark');\n * ```\n *\n * @example\n * Preserve custom layers across style changes:\n * ```typescript\n * let customLayerData = null;\n *\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * // Save custom layer data before style changes\n * if (map.mapLibreMap.getLayer('my-custom-layer')) {\n * customLayerData = map.mapLibreMap.getSource('my-data')._data;\n * map.mapLibreMap.removeLayer('my-custom-layer');\n * map.mapLibreMap.removeSource('my-data');\n * }\n * },\n * onStyleChanged: () => {\n * // Restore custom layer after new style is loaded\n * if (customLayerData) {\n * map.mapLibreMap.addSource('my-data', {\n * type: 'geojson',\n * data: customLayerData\n * });\n * map.mapLibreMap.addLayer({\n * id: 'my-custom-layer',\n * type: 'circle',\n * source: 'my-data',\n * paint: { 'circle-radius': 6, 'circle-color': '#007cbf' }\n * });\n * }\n * }\n * });\n * ```\n *\n * @example\n * Async handler for external API calls:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: async () => {\n * await saveStateToAPI(map.getStyle());\n * },\n * onStyleChanged: async () => {\n * await loadStateFromAPI();\n * }\n * });\n * ```\n *\n * @example\n * Update UI based on style:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * document.body.classList.add('style-changing');\n * },\n * onStyleChanged: () => {\n * document.body.classList.remove('style-changing');\n * const style = map.getStyle();\n * if (typeof style === 'string' && style.includes('Dark')) {\n * document.body.classList.add('dark-mode');\n * } else {\n * document.body.classList.remove('dark-mode');\n * }\n * }\n * });\n * ```\n *\n * @see {@link StyleChangeHandler} - Handler interface definition\n * @see {@link setStyle} - Method that triggers the handlers\n */\n addStyleChangeHandler(handler: StyleChangeHandler): void {\n this.styleChangeHandlers.push(handler);\n }\n}\n","import type { MapOptions } from 'maplibre-gl';\nimport { injectTomTomHeaders } from '../shared/mapUtils';\nimport { buildStyleInput } from './styleInputBuilder';\nimport type { InternalTomTomMapParams } from './types/mapInit';\n\n/**\n * @ignore\n * @param tomtomMapParams\n */\nexport const buildMapOptions = (tomtomMapParams: InternalTomTomMapParams): MapOptions => {\n return {\n // defaults (can be overwritten by given options)\n validateStyle: false,\n maxTileCacheZoomLevels: 22,\n cancelPendingTileRequestsWhileZooming: false,\n // given options:\n ...tomtomMapParams.mapLibre,\n // SDK overrides (won't have any effect via given options):\n style: buildStyleInput(tomtomMapParams),\n attributionControl: { compact: false },\n transformRequest: injectTomTomHeaders(tomtomMapParams),\n };\n};\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\n\n/**\n * @ignore\n */\nexport const isLayerLocalizable = (layer: SymbolLayerSpecification): boolean => {\n const textField = (layer.layout?.['text-field'] ?? '') as string | ExpressionSpecification;\n return textField\n ? // tries to detect layers which have a \"text-field\" that can be localized\n // ex. \"text-field\": \"{name}\" or \"text-field\": [\"get\", \"name\"]\n textField === '{name}' ||\n (textField.length === 2 && textField[1] === 'name') ||\n // tries to detect layers which have \"text-field\" that was localized already\n // ex. \"text-field\": [\"coalesce\", [\"get\", \"name_en\"], [\"get\", \"name\"]]\n (textField.length === 3 &&\n Array.isArray(textField[1]) &&\n typeof textField[1][1] === 'string' &&\n textField[1][1].includes('name_') &&\n Array.isArray(textField[2]) &&\n textField[2].includes('name'))\n : false;\n};\n","import type { DelayMagnitude } from '@tomtom-org/maps-sdk/core';\nimport type { FilterShowMode, MapModuleCommonConfig, ValuesFilter } from '../../shared';\n\n/**\n * Available traffic incident category identifiers.\n *\n * @remarks\n * These categories classify different types of traffic incidents that can be displayed on the map.\n *\n * @group Traffic Incidents\n */\nexport const incidentCategories = [\n 'unknown',\n 'accident',\n 'fog',\n 'dangerous_conditions',\n 'rain',\n 'ice',\n 'jam',\n 'lane_closed',\n 'road_closed',\n 'road_works',\n 'wind',\n 'flooding',\n 'broken_down_vehicle',\n] as const;\n\n/**\n * Traffic incident category type.\n *\n * @remarks\n * Represents the type of traffic incident affecting road conditions.\n * Used for filtering and categorizing incidents displayed on the map.\n *\n * Available categories:\n * - `unknown` - Unclassified incident\n * - `accident` - Vehicle collision or crash\n * - `fog` - Low visibility due to fog\n * - `dangerous_conditions` - Hazardous road conditions\n * - `rain` - Heavy rain affecting traffic\n * - `ice` - Icy road conditions\n * - `jam` - Traffic congestion or standstill\n * - `lane_closed` - One or more lanes unavailable\n * - `road_closed` - Complete road closure\n * - `road_works` - Construction or maintenance\n * - `wind` - Strong winds affecting traffic\n * - `flooding` - Water on roadway\n * - `broken_down_vehicle` - Disabled vehicle blocking traffic\n *\n * @group Traffic Incidents\n */\nexport type IncidentCategory = (typeof incidentCategories)[number];\n\n/**\n * @ignore\n */\nexport const incidentCategoriesMapping: Record<IncidentCategory, number> = {\n unknown: 0,\n accident: 1,\n fog: 2,\n dangerous_conditions: 3,\n rain: 4,\n ice: 5,\n jam: 6,\n lane_closed: 7,\n road_closed: 8,\n road_works: 9,\n wind: 10,\n flooding: 11,\n broken_down_vehicle: 14,\n} as const;\n\n/**\n * Available road hierarchy category identifiers.\n *\n * @remarks\n * These categories represent different levels in the road network hierarchy,\n * from major highways to local streets.\n *\n * @group Traffic\n */\nexport const roadCategories = ['motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'street'] as const;\n\n/**\n * Road hierarchy category type.\n *\n * @remarks\n * Classifies roads by their importance and capacity in the transportation network.\n * Used for filtering traffic data display based on road significance.\n *\n * Road hierarchy (from highest to lowest):\n * - `motorway` - High-capacity highways with restricted access\n * - `trunk` - Major inter-city roads\n * - `primary` - Primary through routes\n * - `secondary` - Secondary through routes\n * - `tertiary` - Connecting roads (see {@link TertiaryRoadCategory})\n * - `street` - Local streets (see {@link StreetRoadCategory})\n *\n * @group Traffic\n */\nexport type RoadCategory = (typeof roadCategories)[number];\n\n/**\n * Available tertiary road sub-category identifiers.\n *\n * @remarks\n * Provides finer granularity for classifying tertiary roads.\n *\n * @group Traffic\n */\nexport const tertiaryRoadCategories = ['connecting', 'major_local'] as const;\n\n/**\n * Tertiary road sub-category type.\n *\n * @remarks\n * Further classifies tertiary roads into specific sub-types for more granular filtering.\n *\n * Sub-categories:\n * - `connecting` - Roads connecting different areas\n * - `major_local` - Major roads within local areas\n *\n * @group Traffic\n */\nexport type TertiaryRoadCategory = (typeof tertiaryRoadCategories)[number];\n\n/**\n * Available street road sub-category identifiers.\n *\n * @remarks\n * Provides finer granularity for classifying local streets.\n *\n * @group Traffic\n */\nexport const streetRoadCategories = ['local', 'minor_local'] as const;\n\n/**\n * Street road sub-category type.\n *\n * @remarks\n * Further classifies local streets into specific sub-types for more granular filtering.\n *\n * Sub-categories:\n * - `local` - Standard local streets\n * - `minor_local` - Minor residential streets\n *\n * @group Traffic\n */\nexport type StreetRoadCategory = (typeof streetRoadCategories)[number];\n\n/**\n * Configuration for filtering traffic incidents by delay duration.\n *\n * @remarks\n * Allows filtering incidents based on whether they cause delays and the severity of those delays.\n * Useful for focusing on incidents with the most significant traffic impact.\n *\n * @group Traffic\n */\nexport type DelayFilter = {\n /**\n * Requires incidents to have an associated delay.\n *\n * @remarks\n * When `true`, incidents without delay information will be hidden from the map.\n * When `false` or omitted, incidents are shown regardless of delay data availability.\n *\n * @defaultValue `false`\n */\n mustHaveDelay?: boolean;\n\n /**\n * Minimum delay threshold in minutes.\n *\n * @remarks\n * Only incidents causing delays of at least this duration will be shown.\n *\n * **Behavior:**\n * - If `mustHaveDelay` is `false` or not set, this filter only applies to incidents that have delay data\n * - Incidents without delay data are still shown (unless `mustHaveDelay` is `true`)\n *\n * @example\n * ```ts\n * // Show only incidents with delays of 5 minutes or more\n * delays: { minDelayMinutes: 5 }\n * ```\n */\n minDelayMinutes?: number;\n};\n\n/**\n * Common filter configuration shared between traffic incidents and flow visualization.\n *\n * @remarks\n * Provides road category filtering capabilities used by both incident and flow modules.\n *\n * @group Traffic\n */\nexport type TrafficCommonFilter = {\n /**\n * Filters traffic data by road hierarchy categories.\n *\n * @remarks\n * Controls which road types display traffic information.\n * Use the `mode` field to specify whether to show or hide the selected categories.\n *\n * @example\n * ```ts\n * // Show only motorways and trunk roads\n * roadCategories: { show: 'only', values: ['motorway', 'trunk'] }\n * ```\n */\n roadCategories?: ValuesFilter<RoadCategory>;\n\n /**\n * Filters traffic data by road sub-categories.\n *\n * @remarks\n * Provides finer-grained control for tertiary roads and streets.\n * Applies to sub-categories of {@link TertiaryRoadCategory} and {@link StreetRoadCategory}.\n *\n * @example\n * ```ts\n * // Hide minor local streets\n * roadSubCategories: { show: 'all_except', values: ['minor_local'] }\n * ```\n */\n roadSubCategories?: ValuesFilter<TertiaryRoadCategory | StreetRoadCategory>;\n};\n\n/**\n * Filter configuration for traffic incidents visualization.\n *\n * @remarks\n * Extends common traffic filters with incident-specific filtering options\n * including category, severity, and delay-based filtering.\n *\n * @group Traffic Incidents\n */\nexport type TrafficIncidentsFilter = TrafficCommonFilter & {\n /**\n * Filters incidents by category type.\n *\n * @remarks\n * Controls which types of incidents are displayed on the map.\n *\n * @example\n * ```ts\n * // Show only accidents and road closures\n * incidentCategories: { show: 'only', values: ['accident', 'road_closed'] }\n * ```\n */\n incidentCategories?: ValuesFilter<IncidentCategory>;\n\n /**\n * Filters incidents by delay severity magnitude.\n *\n * @remarks\n * Controls display based on the severity of traffic delays caused by incidents.\n *\n * Available magnitude values:\n * - `'unknown'` - Unknown delay severity\n * - `'minor'` - Minor delays\n * - `'moderate'` - Moderate delays\n * - `'major'` - Major delays\n * - `'indefinite'` - Indefinite delays (e.g., road closures)\n *\n * @example\n * ```ts\n * // Show only major incidents\n * magnitudes: { show: 'only', values: ['major'] }\n *\n * // Show moderate and major incidents\n * magnitudes: { show: 'only', values: ['moderate', 'major'] }\n *\n * // Hide minor incidents\n * magnitudes: { show: 'all_except', values: ['minor'] }\n * ```\n */\n magnitudes?: ValuesFilter<DelayMagnitude>;\n\n /**\n * Filters incidents by delay duration.\n *\n * @remarks\n * Allows filtering based on whether incidents have delays and minimum delay thresholds.\n */\n delays?: DelayFilter;\n};\n\n/**\n * Collection of traffic incident filters with OR logic.\n *\n * @remarks\n * Combines multiple incident filter configurations where an incident is shown\n * if it matches **any** of the provided filter criteria (logical OR).\n *\n * This allows for complex filtering scenarios where incidents from different\n * categories or with different characteristics can all be displayed.\n *\n * @example\n * ```ts\n * // Show major incidents OR road closures\n * filters: {\n * any: [\n * { magnitudes: { show: 'only', values: ['major'] } },\n * { incidentCategories: { show: 'only', values: ['road_closed'] } }\n * ]\n * }\n * ```\n *\n * @group Traffic Incidents\n */\nexport type TrafficIncidentsFilters = {\n /**\n * Array of incident filters combined with OR logic.\n *\n * @remarks\n * An incident is displayed if it satisfies at least one of the filter configurations.\n */\n any: TrafficIncidentsFilter[];\n};\n\n/**\n * Filter configuration for traffic flow visualization.\n *\n * @remarks\n * Extends common traffic filters with flow-specific options,\n * particularly for highlighting road closures in the flow layer.\n *\n * @group Traffic Flow\n */\nexport type TrafficFlowFilter = TrafficCommonFilter & {\n /**\n * Controls road closure display in the traffic flow layer.\n *\n * @remarks\n * Determines whether to exclusively show road closures or exclude them from display.\n *\n * - `'show'` - Display only road closures\n * - `'hide'` - Display everything except road closures\n *\n * @example\n * ```ts\n * // Highlight only road closures\n * showRoadClosures: 'show'\n * ```\n */\n showRoadClosures?: FilterShowMode;\n};\n\n/**\n * Collection of traffic flow filters with OR logic.\n *\n * @remarks\n * Combines multiple flow filter configurations where traffic flow data is shown\n * if it matches **any** of the provided filter criteria (logical OR).\n *\n * @example\n * ```ts\n * // Show flow on motorways OR show road closures on any road\n * filters: {\n * any: [\n * { roadCategories: { show: 'only', values: ['motorway'] } },\n * { showRoadClosures: 'only' }\n * ]\n * }\n * ```\n *\n * @group Traffic Flow\n */\nexport type TrafficFlowFilters = {\n /**\n * Array of flow filters combined with OR logic.\n *\n * @remarks\n * Traffic flow data is displayed if it satisfies at least one of the filter configurations.\n */\n any: TrafficFlowFilter[];\n};\n\n/**\n * Common configuration for traffic incident visualization components.\n *\n * @remarks\n * Provides shared styling and filtering options used by both incident lines and icons.\n * Extends base style module configuration with traffic incident-specific filters.\n *\n * @group Traffic Incidents\n */\nexport type IncidentsCommonConfig = {\n /**\n * Controls the visibility of the traffic incident layers.\n *\n * @default false\n */\n visible?: boolean;\n\n /**\n * Filter configuration for traffic incidents.\n *\n * @remarks\n * Controls which incidents are displayed based on category, severity, delay, and road type.\n */\n filters?: TrafficIncidentsFilters;\n};\n\n/**\n * Configuration for traffic incidents module.\n *\n * @remarks\n * Provides complete configuration for displaying traffic incidents on the map,\n * including separate styling for incident lines and icons.\n *\n * @group Traffic Incidents\n */\nexport type IncidentsConfig = MapModuleCommonConfig &\n IncidentsCommonConfig & {\n /**\n * Configuration specific to incident icon display.\n *\n * @remarks\n * Allows separate styling and filtering for incident marker icons,\n * independent of the incident line styling.\n */\n icons?: IncidentsCommonConfig;\n };\n\n/**\n * Configuration for traffic flow visualization module.\n *\n * @remarks\n * Controls the display of real-time traffic flow data on road segments,\n * including styling and filtering options.\n *\n * @group Traffic Flow\n */\nexport type FlowConfig = MapModuleCommonConfig & {\n /**\n * Controls the visibility of the traffic flow layers.\n *\n * @default false\n */\n visible?: boolean;\n\n /**\n * Filter configuration for traffic flow data.\n *\n * @remarks\n * Controls which road segments display traffic flow information\n * based on road category and closure status.\n */\n filters?: TrafficFlowFilters;\n};\n","import { indexedMagnitudes } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type {\n ExpressionFilterSpecification,\n FilterSpecification,\n LayerSpecification,\n LegacyFilterSpecification,\n Map,\n} from 'maplibre-gl';\nimport type { MultiSyntaxFilter, ValuesFilter } from '../../shared';\nimport { buildValuesFilter, getMergedAllFilter, getMergedAnyFilter } from '../../shared/mapLibreFilterUtils';\nimport type {\n DelayFilter,\n TrafficCommonFilter,\n TrafficFlowFilter,\n TrafficFlowFilters,\n TrafficIncidentsFilter,\n TrafficIncidentsFilters,\n} from '../types/trafficModuleConfig';\nimport { incidentCategoriesMapping } from '../types/trafficModuleConfig';\n\nconst toMultiSyntaxAllFilter = (\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n): MultiSyntaxFilter | null => {\n if (!newSyntaxExpressions.length) {\n return null;\n }\n if (newSyntaxExpressions.length === 1) {\n return {\n expression: newSyntaxExpressions[0] as ExpressionFilterSpecification,\n legacy: legacySyntaxExpressions[0] as LegacyFilterSpecification,\n };\n }\n return {\n expression: ['all', ...newSyntaxExpressions] as ExpressionFilterSpecification,\n legacy: ['all', ...legacySyntaxExpressions] as LegacyFilterSpecification,\n };\n};\n\nconst delayFilterToMapLibre = (delayFilter: DelayFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions = [];\n const legacySyntaxExpressions = [];\n if (delayFilter.mustHaveDelay && delayFilter.minDelayMinutes) {\n // there must be a delay and with the min specified value:\n const delaySeconds = delayFilter.minDelayMinutes * 60;\n newSyntaxExpressions.push(['>=', ['get', 'delay'], delaySeconds]);\n legacySyntaxExpressions.push(['>=', 'delay', delaySeconds]);\n } else if (delayFilter.mustHaveDelay) {\n // just expects a delay of any kind\n newSyntaxExpressions.push(['>', ['get', 'delay'], 0]);\n legacySyntaxExpressions.push(['>', 'delay', 0]);\n } else if (delayFilter.minDelayMinutes) {\n // Min delay expected, but also allows for non-existing delays:\n const delaySeconds = delayFilter.minDelayMinutes * 60;\n newSyntaxExpressions.push([\n 'any',\n ['!', ['has', 'delay']],\n ['==', ['get', 'delay'], 0],\n ['>=', ['get', 'delay'], delaySeconds],\n ]);\n legacySyntaxExpressions.push(['any', ['!has', 'delay'], ['==', 'delay', 0], ['>=', 'delay', delaySeconds]]);\n }\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\nconst addFilter = (\n filter: MultiSyntaxFilter | undefined | null,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n) => {\n if (filter) {\n newSyntaxExpressions.push(filter.expression);\n legacySyntaxExpressions.push(filter.legacy);\n }\n};\n\nconst addValuesFilter = (\n valuesFilter: ValuesFilter<string> | undefined,\n propName: string,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n) => {\n if (valuesFilter) {\n addFilter(buildValuesFilter(propName, valuesFilter), newSyntaxExpressions, legacySyntaxExpressions);\n }\n};\n\nconst addCommonFilterExpressions = (\n sdkFilter: TrafficCommonFilter,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n): void => {\n addValuesFilter(sdkFilter.roadCategories, 'road_category', newSyntaxExpressions, legacySyntaxExpressions);\n addValuesFilter(sdkFilter.roadSubCategories, 'road_subcategory', newSyntaxExpressions, legacySyntaxExpressions);\n};\n\nconst buildMapLibreIncidentsFilter = (sdkFilter: TrafficIncidentsFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions: unknown[] = [];\n const legacySyntaxExpressions: unknown[] = [];\n\n addCommonFilterExpressions(sdkFilter, newSyntaxExpressions, legacySyntaxExpressions);\n\n if (sdkFilter.incidentCategories) {\n const incidentCategoryFilter = buildValuesFilter(\n 'icon_category_0',\n sdkFilter.incidentCategories,\n (value) => incidentCategoriesMapping[value],\n );\n addFilter(incidentCategoryFilter, newSyntaxExpressions, legacySyntaxExpressions);\n }\n if (sdkFilter.magnitudes) {\n const magnitudesFilter = buildValuesFilter('magnitude_of_delay', sdkFilter.magnitudes, (magnitude) =>\n indexedMagnitudes.indexOf(magnitude),\n );\n addFilter(magnitudesFilter, newSyntaxExpressions, legacySyntaxExpressions);\n }\n if (sdkFilter.delays) {\n addFilter(delayFilterToMapLibre(sdkFilter.delays), newSyntaxExpressions, legacySyntaxExpressions);\n }\n\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\n/**\n * @ignore\n */\nexport const buildMapLibreIncidentFilters = (incidentFilters: TrafficIncidentsFilters): MultiSyntaxFilter | null => {\n if (!incidentFilters?.any?.length) {\n return null;\n }\n const mapLibreFilters = incidentFilters.any\n .map(buildMapLibreIncidentsFilter)\n .filter((mapLibreFilter) => !isNil(mapLibreFilter));\n return getMergedAnyFilter(mapLibreFilters);\n};\n\nconst buildMapLibreFlowFilter = (sdkFilter: TrafficFlowFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions: unknown[] = [];\n const legacySyntaxExpressions: unknown[] = [];\n\n addCommonFilterExpressions(sdkFilter, newSyntaxExpressions, legacySyntaxExpressions);\n if (sdkFilter.showRoadClosures) {\n const operator = sdkFilter.showRoadClosures === 'only' ? '==' : '!=';\n newSyntaxExpressions.push([operator, ['get', 'road_closure'], true]);\n legacySyntaxExpressions.push([operator, 'road_closure', true]);\n }\n\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\n/**\n * @ignore\n */\nexport const buildMapLibreFlowFilters = (flowFilters: TrafficFlowFilters): MultiSyntaxFilter | null => {\n if (!flowFilters?.any?.length) {\n return null;\n }\n const mapLibreFilters = flowFilters.any\n .map(buildMapLibreFlowFilter)\n .filter((mapLibreFilter) => !isNil(mapLibreFilter));\n return getMergedAnyFilter(mapLibreFilters);\n};\n\n/**\n * @ignore\n * @param filter\n * @param layers\n * @param mapLibreMap\n * @param originalFilters\n */\nexport const applyFilter = (\n filter: MultiSyntaxFilter | undefined,\n layers: LayerSpecification[],\n mapLibreMap: Map,\n originalFilters: Record<string, FilterSpecification | undefined>,\n) => {\n for (const layer of layers) {\n mapLibreMap.setFilter(\n layer.id,\n filter ? getMergedAllFilter(filter, originalFilters[layer.id]) : originalFilters[layer.id],\n );\n }\n};\n","import { isNil, omitBy } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport type { LayerSpecWithSource } from '../shared';\nimport {\n AbstractMapModule,\n EventsModule,\n filterLayersBySources,\n StyleSourceWithLayers,\n TRAFFIC_FLOW_SOURCE_ID,\n} from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { applyFilter, buildMapLibreFlowFilters } from './filters/trafficFilters';\nimport type { FlowConfig, TrafficFlowFilters } from './types/trafficModuleConfig';\n\n/**\n * IDs of sources and layers for traffic flow module.\n */\ntype TrafficFlowSourcesWithLayers = {\n trafficFlow: StyleSourceWithLayers;\n};\n\n/**\n * Traffic Flow Module for displaying and configuring real-time traffic flow information on the map.\n *\n * This module controls the vector tile traffic flow layers that visualize current\n * traffic speed conditions using color-coded road segments.\n *\n * @remarks\n * **Features:**\n * - Toggle traffic flow visibility on/off\n * - Filter by road categories and types\n * - Color-coded speed visualization (green = free flow, red = congestion)\n * - Real-time traffic data from vector tiles\n * - Filter road closures\n *\n * **Visual Representation:**\n * - Green: Free-flowing traffic\n * - Yellow/Orange: Slow traffic\n * - Red: Heavy congestion\n * - Dark gray: Road closures\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { TrafficFlowModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module (auto-add to style if needed)\n * const trafficFlow = await TrafficFlowModule.get(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * trafficFlow.setVisible(false);\n * trafficFlow.setVisible(true);\n * ```\n *\n * @example\n * Filter by road type:\n * ```typescript\n * // Show only highway traffic\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk']\n * }\n * }]\n * });\n *\n * // Hide local streets\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'all_except',\n * values: ['street']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Show only road closures:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * showRoadClosures: 'only'\n * }]\n * });\n * ```\n *\n * @see [Traffic Flow Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic-flow)\n * @see [Traffic Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic)\n *\n * @group Traffic Flow\n */\nexport class TrafficFlowModule extends AbstractMapModule<TrafficFlowSourcesWithLayers, FlowConfig> {\n private originalFilters!: Record<string, FilterSpecification | undefined>;\n\n /**\n * Retrieves a TrafficFlowModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization, visibility, and filters.\n *\n * @returns A promise that resolves to the initialized TrafficFlowModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `ensureAddedToStyle`: Auto-add traffic flow to style if missing\n * - `filters`: Road category and type filters\n *\n * **Style Requirement:**\n * Traffic flow must be included in the map style or added via `ensureAddedToStyle`.\n *\n * @throws Error if traffic flow source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const trafficFlowModule = await TrafficFlowModule.get(map);\n * ```\n *\n * @example\n * Auto-add to style:\n * ```typescript\n * const trafficFlowModule = await TrafficFlowModule.get(map, {\n * visible: true,\n * filters: {\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk', 'primary']\n * }\n * }]\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: FlowConfig): Promise<TrafficFlowModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, TRAFFIC_FLOW_SOURCE_ID, 'trafficFlow');\n return new TrafficFlowModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: FlowConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const flowSource = this.mapLibreMap.getSource(TRAFFIC_FLOW_SOURCE_ID);\n if (!flowSource) {\n throw notInTheStyle(`init ${TrafficFlowModule.name} with source ID ${TRAFFIC_FLOW_SOURCE_ID}`);\n }\n this.originalFilters = {};\n for (const layer of this.getLayers()) {\n this.originalFilters[layer.id] = layer.filter;\n }\n return { trafficFlow: new StyleSourceWithLayers(this.mapLibreMap, flowSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: FlowConfig | undefined) {\n this.setVisible(config?.visible ?? false);\n this._filter(config?.filters, false);\n return config;\n }\n\n private getLayers(): LayerSpecWithSource[] {\n return filterLayersBySources(this.tomtomMap.mapLibreMap, [TRAFFIC_FLOW_SOURCE_ID]);\n }\n\n /**\n * Applies filters to traffic flow display.\n *\n * @param filters - Filter configuration for road types, categories, and closures.\n * Pass `undefined` to reset to defaults (show all).\n *\n * @remarks\n * **Filter Options:**\n * - `roadCategories`: Filter by road importance (motorway, trunk, primary, etc.)\n * - `roadSubCategories`: Filter by specific street types\n * - `showRoadClosures`: Show only closures or exclude them\n *\n * **Available Road Categories:**\n * - `motorway`: Major highways\n * - `trunk`: Major roads\n * - `primary`: Primary roads\n * - `secondary`: Secondary roads\n * - `tertiary`: Tertiary roads\n * - `street`: Local streets\n *\n * **Filter Logic:**\n * Uses \"any\" (OR) logic - traffic matching any filter is shown.\n *\n * @example\n * Show only major roads:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk', 'primary']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Hide street-level traffic:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'all_except',\n * values: ['street']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Multiple filter criteria:\n * ```typescript\n * trafficFlow.filter({\n * any: [\n * {\n * roadCategories: { show: 'only', values: ['motorway'] }\n * },\n * {\n * showRoadClosures: 'only'\n * }\n * ]\n * });\n * ```\n *\n * @example\n * Reset filters:\n * ```typescript\n * trafficFlow.filter(undefined);\n * ```\n */\n filter(filters?: TrafficFlowFilters) {\n this._filter(filters);\n }\n\n private _filter(filters: TrafficFlowFilters | undefined, updateConfig = true) {\n if (this.tomtomMap.mapReady) {\n if (filters?.any?.length) {\n const filterExpression = buildMapLibreFlowFilters(filters);\n if (filterExpression) {\n applyFilter(filterExpression, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n } else if (this.config?.filters?.any?.length) {\n applyFilter(undefined, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n }\n\n if (updateConfig) {\n this.config = omitBy({ ...this.config, filters }, isNil);\n }\n }\n\n /**\n * Sets the visibility of traffic flow layers.\n *\n * @param visible - `true` to show traffic flow, `false` to hide it.\n *\n * @example\n * ```typescript\n * trafficFlow.setVisible(true); // Show traffic\n * trafficFlow.setVisible(false); // Hide traffic\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = { ...this.config, visible };\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficFlow.setLayersVisible(visible);\n }\n }\n\n /**\n * Returns if any layer for traffic flow is visible or not.\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.trafficFlow.isAnyLayerVisible();\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible traffic flow features.\n *\n * @remarks\n * Returns all traffic flow features currently rendered in the visible map area.\n * These represent road segments with real-time traffic speed information.\n *\n * **Feature Properties:**\n * Properties depend on the traffic flow vector tile schema and may include:\n * - Current speed\n * - Free flow speed\n * - Road category\n * - Congestion level\n * - Road closure status\n *\n * @example\n * ```typescript\n * const shown = trafficFlow.getShown();\n * console.log(`Visible flow segments: ${shown.trafficFlow.length}`);\n * shown.trafficFlow.forEach(segment => {\n * console.log('Road segment:', segment.properties);\n * });\n * ```\n *\n * @example\n * Analyze congestion in viewport:\n * ```typescript\n * const shown = trafficFlow.getShown();\n * const congested = shown.trafficFlow.filter(\n * segment => segment.properties.speed < segment.properties.freeFlowSpeed * 0.5\n * );\n * console.log(`Congested segments: ${congested.length}`);\n * ```\n */\n getShown() {\n return {\n trafficFlow: this.mapLibreMap.queryRenderedFeatures({\n layers: this.sourcesWithLayers.trafficFlow.sourceAndLayerIDs.layerIDs,\n validate: false,\n }),\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.trafficFlow, this.config?.events);\n }\n}\n","import { isEmpty, isNil, omitBy } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport type { LayerSpecWithSource } from '../shared';\nimport {\n AbstractMapModule,\n EventsModule,\n filterLayersBySources,\n StyleSourceWithLayers,\n TRAFFIC_INCIDENTS_SOURCE_ID,\n} from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { applyFilter, buildMapLibreIncidentFilters } from './filters/trafficFilters';\nimport type { IncidentsConfig, TrafficIncidentsFilters } from './types/trafficModuleConfig';\n\n/**\n * IDs of sources and layers for traffic incidents module.\n */\ntype TrafficIncidentsSourcesWithLayers = {\n trafficIncidents: StyleSourceWithLayers;\n};\n\n/**\n * Traffic Incidents Module for displaying and configuring real-time traffic incidents on the map.\n *\n * This module controls the vector tile traffic incidents layers that show traffic\n * events like accidents, road closures, construction, and hazards.\n *\n * @remarks\n * **Features:**\n * - Toggle incidents visibility on/off\n * - Separate control for incident icons\n * - Filter by incident type (accident, construction, etc.)\n * - Filter by severity/delay magnitude\n * - Filter by road categories\n * - Icon and line/polygon visualization\n *\n * **Incident Types:**\n * - Accidents\n * - Road closures\n * - Construction/road works\n * - Weather conditions (fog, ice, rain, etc.)\n * - Lane closures\n * - Traffic jams\n * - Broken down vehicles\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { TrafficIncidentsModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module (auto-add to style if needed)\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * trafficIncidentsModule.setVisible(false);\n * trafficIncidentsModule.setVisible(true);\n *\n * // Control icons separately\n * trafficIncidentsModule.setIconsVisible(false);\n * ```\n *\n * @example\n * Filter by incident type:\n * ```typescript\n * // Show only accidents and road closures\n * trafficIncidentsModule.filter({\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road_closed']\n * }\n * }]\n * });\n *\n * // Hide construction\n * trafficIncidentsModule.filter({\n * any: [{\n * incidentCategories: {\n * show: 'all_except',\n * values: ['road_works']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter by severity:\n * ```typescript\n * // Show only major delays\n * incidents.filter({\n * any: [{\n * magnitudes: {\n * show: 'only',\n * values: ['major']\n * }\n * }]\n * });\n *\n * // Show incidents with at least 10 minutes delay\n * incidents.filter({\n * any: [{\n * delays: {\n * mustHaveDelay: true,\n * minDelayMinutes: 10\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter icons separately from incident areas:\n * ```typescript\n * // Show all incidents but only major icons\n * incidents.filter(\n * {\n * any: [{}] // Show all incidents\n * },\n * {\n * any: [{\n * magnitudes: { show: 'only', values: ['major'] }\n * }]\n * }\n * );\n * ```\n *\n * @see [Traffic Incidents Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic-incidents)\n * @see [Traffic Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic)\n *\n * @group Traffic Incidents\n */\nexport class TrafficIncidentsModule extends AbstractMapModule<TrafficIncidentsSourcesWithLayers, IncidentsConfig> {\n private originalFilters!: Record<string, FilterSpecification | undefined>;\n\n /**\n * Retrieves a TrafficIncidentsModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization, visibility, and filters.\n *\n * @returns A promise that resolves to the initialized TrafficIncidentsModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state for all incidents\n * - `icons.visible`: Initial visibility for incident icons\n * - `ensureAddedToStyle`: Auto-add traffic incidents to style if missing\n * - `filters`: Incident type, severity, and delay filters\n * - `icons.filters`: Separate filters for icons\n *\n * @throws Error if traffic incidents source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map);\n * ```\n *\n * @example\n * With configuration:\n * ```typescript\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map, {\n * visible: true,\n * icons: { visible: true },\n * filters: {\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road_closed', 'jam']\n * }\n * }]\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: IncidentsConfig): Promise<TrafficIncidentsModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, TRAFFIC_INCIDENTS_SOURCE_ID, 'trafficIncidents');\n return new TrafficIncidentsModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: IncidentsConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const incidentsSource = this.mapLibreMap.getSource(TRAFFIC_INCIDENTS_SOURCE_ID);\n if (!incidentsSource) {\n throw notInTheStyle(`init ${TrafficIncidentsModule.name} with source ID ${TRAFFIC_INCIDENTS_SOURCE_ID}`);\n }\n this.originalFilters = {};\n for (const layer of this.getLayers()) {\n this.originalFilters[layer.id] = layer.filter;\n }\n return { trafficIncidents: new StyleSourceWithLayers(this.mapLibreMap, incidentsSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: IncidentsConfig | undefined) {\n // We do not update config in setVisible since it could override icons visibility setting:\n this._setVisible(config?.visible ?? false, { updateConfig: false });\n if (!isNil(config?.icons?.visible)) {\n this.setIconsVisible(config.icons.visible);\n }\n this._filter(config?.filters, config?.icons?.filters, false);\n return config;\n }\n\n /**\n * Applies filters to traffic incidents display.\n *\n * @param incidentFilters - Filter for incident areas/lines. Pass `undefined` to reset.\n * @param iconFilters - Optional separate filter for incident icons. Pass `undefined` to reset.\n *\n * @remarks\n * **Filter Options:**\n * - `incidentCategories`: Filter by incident type\n * - `magnitudes`: Filter by delay severity (minor/moderate/major/unknown)\n * - `delays`: Filter by delay duration\n * - `roadCategories`: Filter by road importance\n * - `roadSubCategories`: Filter by specific road types\n *\n * **Available Incident Categories:**\n * - `accident`, `road_closed`, `lane_closed`\n * - `road_works` (construction)\n * - `jam` (traffic jam)\n * - `fog`, `rain`, `ice`, `wind`, `flooding`\n * - `dangerous_conditions`\n * - `broken_down_vehicle`\n * - `unknown`\n *\n * **Delay Magnitudes:**\n * - `minor`: Small delays\n * - `moderate`: Moderate delays\n * - `major`: Significant delays\n * - `unknown`: Unknown or no delay info\n *\n * @example\n * Filter by type:\n * ```typescript\n * incidents.filter({\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road_closed']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter by severity and delay:\n * ```typescript\n * incidents.filter({\n * any: [{\n * magnitudes: { show: 'only', values: ['major', 'moderate'] },\n * delays: {\n * mustHaveDelay: true,\n * minDelayMinutes: 5\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Different filters for icons and areas:\n * ```typescript\n * // Show all incidents on roads\n * const incidentFilter = {\n * any: [{\n * roadCategories: { show: 'only', values: ['motorway', 'trunk'] }\n * }]\n * };\n *\n * // But only show icons for major incidents\n * const iconFilter = {\n * any: [{\n * magnitudes: { show: 'only', values: ['major'] }\n * }]\n * };\n *\n * incidents.filter(incidentFilter, iconFilter);\n * ```\n */\n filter(incidentFilters?: TrafficIncidentsFilters, iconFilters?: TrafficIncidentsFilters) {\n this._filter(incidentFilters, iconFilters);\n }\n\n private _filter(\n incidentFilters: TrafficIncidentsFilters | undefined,\n iconFilters: TrafficIncidentsFilters | undefined,\n updateConfig = true,\n ) {\n if (this.tomtomMap.mapReady) {\n if (incidentFilters?.any?.length) {\n const incidentFilterExpression = buildMapLibreIncidentFilters(incidentFilters);\n if (incidentFilterExpression) {\n const layers = iconFilters ? this.getNonSymbolLayers() : this.getLayers();\n applyFilter(incidentFilterExpression, layers, this.mapLibreMap, this.originalFilters);\n }\n } else if (this.config?.filters?.any?.length) {\n applyFilter(undefined, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n if (iconFilters?.any?.length) {\n const iconFilterExpression = buildMapLibreIncidentFilters(iconFilters);\n if (iconFilterExpression) {\n applyFilter(iconFilterExpression, this.getSymbolLayers(), this.mapLibreMap, this.originalFilters);\n }\n }\n }\n\n // else: default incidents visibility has been set already if necessary\n if (updateConfig) {\n this.config = omitBy(\n {\n ...this.config,\n filters: incidentFilters,\n icons: { ...this.config?.icons, filters: iconFilters },\n },\n isNil,\n );\n }\n }\n\n private getLayers(): LayerSpecWithSource[] {\n return filterLayersBySources(this.tomtomMap.mapLibreMap, [TRAFFIC_INCIDENTS_SOURCE_ID]);\n }\n\n private getSymbolLayers(): LayerSpecWithSource[] {\n return this.getLayers().filter((layer) => layer.type === 'symbol');\n }\n\n private getNonSymbolLayers(): LayerSpecWithSource[] {\n return this.getLayers().filter((layer) => layer.type != 'symbol');\n }\n\n /**\n * Sets the visibility of incident icon layers.\n *\n * @param visible - `true` to show icons, `false` to hide them.\n *\n * @remarks\n * This controls only the icon/symbol layers, not the incident area polygons or lines.\n *\n * @example\n * ```typescript\n * // Hide icons but keep incident areas visible\n * incidents.setIconsVisible(false);\n *\n * // Show icons\n * incidents.setIconsVisible(true);\n * ```\n */\n setIconsVisible(visible: boolean): void {\n // We adjust the config for this change (but it might be overwritten if it's part of an \"applyConfig\" call)\n this.config = { ...this.config, icons: { ...this.config?.icons, visible } };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficIncidents.setLayersVisible(\n visible,\n (layerSpec) => layerSpec.type === 'symbol',\n );\n }\n }\n\n /**\n * Sets the visibility of all traffic incident layers.\n *\n * @param visible - `true` to show incidents, `false` to hide them.\n *\n * @remarks\n * This controls all incident layers including icons, lines, and polygons.\n *\n * @example\n * ```typescript\n * incidents.setVisible(false); // Hide all incidents\n * incidents.setVisible(true); // Show all incidents\n * ```\n */\n setVisible(visible: boolean): void {\n this._setVisible(visible);\n }\n\n private _setVisible(visible: boolean, options?: { updateConfig: boolean }): void {\n const updateConfig = options?.updateConfig ?? true;\n if (updateConfig) {\n // setting all traffic visible also nullifies the icons visible setting\n delete this.config?.icons?.visible;\n // we remove empty values from config to avoid confusion (in case icons part is just empty after deleting visible)\n this.config = { ...omitBy({ ...this.config }, isEmpty), visible };\n }\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficIncidents.setLayersVisible(visible);\n }\n }\n\n /**\n * Checks if any traffic incident layers are currently visible.\n *\n * @returns `true` if any incident layer is visible, `false` if all are hidden.\n *\n * @example\n * ```typescript\n * if (incidents.isVisible()) {\n * console.log('Incidents are displayed');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.trafficIncidents.isAnyLayerVisible();\n }\n\n /**\n * Checks if any incident icon layers are currently visible.\n *\n * @returns `true` if any icon layer is visible, `false` if all icons are hidden.\n *\n * @example\n * ```typescript\n * if (incidents.anyIconLayersVisible()) {\n * console.log('Incident icons are shown');\n * }\n * ```\n */\n anyIconLayersVisible(): boolean {\n return !!this.sourcesWithLayers.trafficIncidents?.isAnyLayerVisible((layerSpec) => layerSpec.type === 'symbol');\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible traffic incident features.\n *\n * @remarks\n * Returns all incident features currently rendered in the visible map area,\n * including both incident areas/lines and icon markers.\n *\n * **Feature Properties:**\n * Properties depend on the traffic incidents vector tile schema and may include:\n * - Incident type/category\n * - Severity/magnitude\n * - Delay information\n * - Road affected\n * - Description\n *\n * @example\n * ```typescript\n * const shown = incidents.getShown();\n * console.log(`Visible incidents: ${shown.trafficIncidents.length}`);\n * shown.trafficIncidents.forEach(incident => {\n * console.log('Incident:', incident.properties);\n * });\n * ```\n *\n * @example\n * Filter and analyze visible incidents:\n * ```typescript\n * const shown = incidents.getShown();\n * const accidents = shown.trafficIncidents.filter(\n * incident => incident.properties.category === 'accident'\n * );\n * console.log(`Accidents in view: ${accidents.length}`);\n * ```\n */\n getShown() {\n return {\n trafficIncidents: this.mapLibreMap.queryRenderedFeatures({\n layers: this.sourcesWithLayers.trafficIncidents.sourceAndLayerIDs.layerIDs,\n validate: false,\n }),\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.trafficIncidents,\n this.config?.events,\n );\n }\n}\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { Map } from 'maplibre-gl';\nimport type { TomTomMap } from '../TomTomMap';\nimport type {\n CalculateFittingBBoxOptions,\n CalculateMapBoundsOptions,\n CalculateMapCenterOptions,\n} from './types/paddedBounds';\n\n// (Originally mostly AI-generated)\n\ntype BoundsPX = {\n left: number;\n top: number;\n right: number;\n bottom: number;\n};\n\nconst getRelativeElementBounds = (elementRect: DOMRect, containerRect: DOMRect): BoundsPX => ({\n left: elementRect.left - containerRect.left,\n top: elementRect.top - containerRect.top,\n right: elementRect.right - containerRect.left,\n bottom: elementRect.bottom - containerRect.top,\n});\n\nconst isElementOutsideContainer = (bounds: BoundsPX, containerWidth: number, containerHeight: number): boolean =>\n bounds.right <= 0 || bounds.left >= containerWidth || bounds.bottom <= 0 || bounds.top >= containerHeight;\n\n// Adjusts the current visible bounds to account for a horizontal bar element (that spans the whole width)\nconst adjustForHorizontalBar = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerHeight: number,\n padding: number,\n): void => {\n const distanceFromTop = bounds.top;\n const distanceFromBottom = containerHeight - bounds.bottom;\n if (distanceFromTop <= distanceFromBottom) {\n currentVisible.top = Math.max(currentVisible.top, bounds.bottom + padding);\n } else {\n currentVisible.bottom = Math.min(currentVisible.bottom, bounds.top - padding);\n }\n};\n\n// Adjusts the current visible bounds to account for a vertical bar element (that spans the whole height)\nconst adjustForVerticalBar = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerWidth: number,\n padding: number,\n): void => {\n const distanceFromLeft = bounds.left;\n const distanceFromRight = containerWidth - bounds.right;\n if (distanceFromLeft <= distanceFromRight) {\n currentVisible.left = Math.max(currentVisible.left, bounds.right + padding);\n } else {\n currentVisible.right = Math.min(currentVisible.right, bounds.left - padding);\n }\n};\n\n// Adjusts the current visible bounds to account for a floating element (that does not span full width or height)\nconst adjustForFloatingElement = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerWidth: number,\n containerHeight: number,\n padding: number,\n): void => {\n const distanceFromLeft = bounds.left;\n const distanceFromRight = containerWidth - bounds.right;\n const distanceFromTop = bounds.top;\n const distanceFromBottom = containerHeight - bounds.bottom;\n const minDistance = Math.min(distanceFromLeft, distanceFromRight, distanceFromTop, distanceFromBottom);\n\n if (minDistance === distanceFromLeft) {\n currentVisible.left = Math.max(currentVisible.left, bounds.right + padding);\n } else if (minDistance === distanceFromRight) {\n currentVisible.right = Math.min(currentVisible.right, bounds.left - padding);\n } else if (minDistance === distanceFromTop) {\n currentVisible.top = Math.max(currentVisible.top, bounds.bottom + padding);\n } else {\n currentVisible.bottom = Math.min(currentVisible.bottom, bounds.top - padding);\n }\n};\n\n/**\n * Clamps the element bounds to the container boundaries.\n * This handles cases where UI elements extend beyond the visible container\n * (e.g., a sidebar that goes past the screen edge).\n */\nconst clampBoundsToContainer = (bounds: BoundsPX, containerWidth: number, containerHeight: number): BoundsPX => ({\n left: Math.max(0, bounds.left),\n top: Math.max(0, bounds.top),\n right: Math.min(containerWidth, bounds.right),\n bottom: Math.min(containerHeight, bounds.bottom),\n});\n\nconst adjustVisibleBoundsForElement = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n mapWidthPX: number,\n mapHeightPX: number,\n padding: number,\n): void => {\n // Clamp bounds to container to handle elements that extend beyond the screen\n const clampedBounds = clampBoundsToContainer(bounds, mapWidthPX, mapHeightPX);\n\n // Determine if the element touches or extends beyond the full width or height of the container\n // An element \"spans full width\" if it touches both left and right edges (or extends beyond)\n const spansFullWidth = bounds.left <= 0 && bounds.right >= mapWidthPX;\n const spansFullHeight = bounds.top <= 0 && bounds.bottom >= mapHeightPX;\n\n // For elements spanning full width (horizontal bars), only consider vertical adjustment\n if (spansFullWidth && !spansFullHeight) {\n adjustForHorizontalBar(clampedBounds, currentVisible, mapHeightPX, padding);\n return;\n }\n\n // For elements spanning full height (vertical bars), only consider horizontal adjustment\n if (spansFullHeight && !spansFullWidth) {\n adjustForVerticalBar(clampedBounds, currentVisible, mapWidthPX, padding);\n return;\n }\n\n // Check if element touches/extends beyond an edge (makes it act like a bar on that side)\n const touchesLeft = bounds.left <= 0;\n const touchesRight = bounds.right >= mapWidthPX;\n const touchesTop = bounds.top <= 0;\n const touchesBottom = bounds.bottom >= mapHeightPX;\n\n // If element touches opposite edges in one dimension but not the other,\n // treat it as a bar in that dimension\n if ((touchesLeft || touchesRight) && !touchesTop && !touchesBottom) {\n // Element extends horizontally but not vertically - treat as horizontal bar\n adjustForHorizontalBar(clampedBounds, currentVisible, mapHeightPX, padding);\n return;\n }\n if ((touchesTop || touchesBottom) && !touchesLeft && !touchesRight) {\n // Element extends vertically but not horizontally - treat as vertical bar\n adjustForVerticalBar(clampedBounds, currentVisible, mapWidthPX, padding);\n return;\n }\n\n // For corner elements (touching edges in both dimensions) or floating elements\n // determine the best adjustment based on which edge the element is closest to\n adjustForFloatingElement(clampedBounds, currentVisible, mapWidthPX, mapHeightPX, padding);\n};\n\n/**\n * Resolves a surrounding element reference to an HTMLElement.\n * If the reference is a string, it's treated as a DOM selector.\n * @param ref - Either an HTMLElement or a DOM selector string\n * @returns The resolved HTMLElement, or null if not found\n */\nconst toElement = (ref: HTMLElement | string): HTMLElement | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref);\n }\n return ref;\n};\n\n/**\n * Resolves a map reference to a MapLibre Map instance.\n * If the reference is a TomTomMap, it extracts the underlying mapLibreMap.\n * @param map - Either a TomTomMap or a MapLibre Map instance\n * @returns The MapLibre Map instance\n */\nconst getMapLibreMap = (map: TomTomMap | Map): Map => {\n if ('mapLibreMap' in map) {\n return map.mapLibreMap;\n }\n return map;\n};\n\ntype VisibleAreaResult = {\n visibleAreaBounds: BoundsPX;\n mapWidthPX: number;\n mapHeightPX: number;\n} | null;\n\n/**\n * Calculates the visible area bounds in pixel coordinates after accounting for\n * surrounding UI elements and padding.\n */\nconst calculateVisibleAreaBounds = (\n map: TomTomMap | Map,\n surroundingElements: (HTMLElement | string)[],\n paddingPX: number,\n): VisibleAreaResult => {\n const containerRect = getMapLibreMap(map).getContainer().getBoundingClientRect();\n const { width: mapWidthPX, height: mapHeightPX } = containerRect;\n\n // Initialize visible bounds with padding applied to all edges (will be mutated)\n const visibleAreaBounds: BoundsPX = {\n left: paddingPX,\n top: paddingPX,\n right: mapWidthPX - paddingPX,\n bottom: mapHeightPX - paddingPX,\n };\n\n for (const elementRef of surroundingElements) {\n const element = toElement(elementRef);\n if (!element) {\n continue;\n }\n const elementRect = element.getBoundingClientRect();\n const elementBounds = getRelativeElementBounds(elementRect, containerRect);\n\n if (isElementOutsideContainer(elementBounds, mapWidthPX, mapHeightPX)) {\n continue;\n }\n\n adjustVisibleBoundsForElement(elementBounds, visibleAreaBounds, mapWidthPX, mapHeightPX, paddingPX);\n }\n\n if (visibleAreaBounds.left >= visibleAreaBounds.right || visibleAreaBounds.top >= visibleAreaBounds.bottom) {\n return null;\n }\n\n return { visibleAreaBounds, mapWidthPX, mapHeightPX };\n};\n\n/**\n * Calculates the bounding box in lng-lat coordinates of the visible map area\n * that does not overlap with the given UI HTML elements.\n *\n * @remarks\n * This is useful for determining the area of the map that is not obscured by UI components.\n *\n * @param options - The options for calculating map bounds\n * @returns The padded bounding box as [west, south, east, north], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculatePaddedBBox = (options: CalculateMapBoundsOptions): BBox | null => {\n const { map, surroundingElements, paddingPX = 0 } = options;\n\n const result = calculateVisibleAreaBounds(map, surroundingElements, paddingPX);\n if (!result) {\n return null;\n }\n\n const { visibleAreaBounds } = result;\n const mapLibreMap = getMapLibreMap(map);\n const sw = mapLibreMap.unproject([visibleAreaBounds.left, visibleAreaBounds.bottom]);\n const ne = mapLibreMap.unproject([visibleAreaBounds.right, visibleAreaBounds.top]);\n\n return [sw.lng, sw.lat, ne.lng, ne.lat];\n};\n\n/**\n * Calculates the center point in lng-lat coordinates of the visible map area\n * that does not overlap with the given UI HTML elements.\n *\n * @remarks\n * * This is useful to offset the map center in a way that it looks harmonious with surrounding UI components.\n * * It's equivalent to the center of calculatePaddedBBox.\n *\n * @param options - The options for calculating map center\n * @returns The center as [lng, lat], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculatePaddedCenter = (options: CalculateMapCenterOptions): Position | null => {\n const { map, surroundingElements } = options;\n const bbox = calculatePaddedBBox({ map, surroundingElements });\n if (!bbox) {\n return null;\n }\n const [west, south, east, north] = bbox;\n return [(west + east) / 2, (south + north) / 2];\n};\n\n/**\n * Calculates an expanded bounding box that, when the map is zoomed to it, ensures that the given to-be-contained bounding box\n * is visible within the area not obscured by surrounding UI elements, including optional padding.\n * * In other words, calculates a bounding box that ensure the to-be-contained bbox fits within the visible area of the map.\n *\n * @remarks\n * This is useful when you have a specific geographic area (containedBBox) that you want to be fully visible\n * in the unobscured portion of the map (the area not covered by UI components).\n * The function returns a larger bounding box that accounts for the space taken by UI elements.\n *\n * @param options - The options for calculating expanded bounds\n * @returns The expanded bounding box as [west, south, east, north], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculateFittingBBox = (options: CalculateFittingBBoxOptions): BBox | null => {\n const { map, toBeContainedBBox, surroundingElements, paddingPX = 0 } = options;\n\n const result = calculateVisibleAreaBounds(map, surroundingElements, paddingPX);\n if (!result) {\n return null;\n }\n\n const { visibleAreaBounds, mapWidthPX, mapHeightPX } = result;\n\n // Calculate the ratios of how much the visible area is offset from the full container\n // These represent what fraction of the full map the visible area occupies\n const leftRatio = visibleAreaBounds.left / mapWidthPX;\n const rightRatio = visibleAreaBounds.right / mapWidthPX;\n const topRatio = visibleAreaBounds.top / mapHeightPX;\n const bottomRatio = visibleAreaBounds.bottom / mapHeightPX;\n\n // Calculate the width and height ratios of the visible area\n const widthRatio = rightRatio - leftRatio;\n const heightRatio = bottomRatio - topRatio;\n\n // Target bbox coordinates\n const [west, south, east, north] = toBeContainedBBox;\n const targetWidth = east - west;\n const targetHeight = north - south;\n\n // Calculate the full map extent needed so that when cropped by the visible area,\n // the target bbox fits exactly\n const fullWidth = targetWidth / widthRatio;\n const fullHeight = targetHeight / heightRatio;\n\n // Calculate the fitting expanded bounds\n // The visible area starts at leftRatio of the full width, so we need to extend west\n const expandedWest = west - leftRatio * fullWidth;\n const expandedEast = expandedWest + fullWidth;\n\n // For latitude, remember that in pixel coordinates, top is smaller Y but higher latitude\n // topRatio represents the fraction from top, which corresponds to north\n const expandedNorth = north + topRatio * fullHeight;\n const expandedSouth = expandedNorth - fullHeight;\n\n return [expandedWest, expandedSouth, expandedEast, expandedNorth];\n};\n"],"names":["notInTheStyle","actionText","Error","isDOMImageSupported","document","DOMParser","btoa","svgToImg","svgDomElement","img","createElement","src","XMLSerializer","serializeToString","parseSvg","svgString","parseFromString","documentElement","pinSvg","options","element","fillColor","querySelector","setAttribute","outlineColor","outlineOpacity","toString","asDefined","value","isNil","assertDefined","TomTomMapSource","constructor","id","spec","runtimeSource","this","ensureAddedToMap","map","getSource","addSource","AbstractSourceWithLayers","source","layerSpecs","_layerSpecs","_updateSourceAndLayerIDs","isAnyLayerVisible","filter","getLayerSpecs","some","layer","isLayerVisible","areAllLayersVisible","every","_sourceAndLayerIDs","sourceID","layerIDs","getLayoutProperty","setLayersVisible","visible","layerSpec","setLayoutProperty","validate","sourceAndLayerIDs","equalSourceAndLayerIDs","other","length","index","filterLayersBySources","loadedMap","sourceIDs","getStyle","layers","includes","StyleSourceWithLayers","super","sources","AddedSourceWithLayers","sourceId","sourceSpec","ensureLayersAddedToMap","getLayer","addLayer","beforeID","ensureAddedToMapWithVisibility","addLayersToMap","emptyFeatureCollection","type","features","GeoJSONSourceWithLayers","data","promoteId","shownFeatures","show","featureCollection","setData","clear","findFeature","find","f","putEventState","mode","feature","properties","eventState","state","cleanEventState","cleanEventStates","changed","states","waitUntilMapIsReady","async","tomtomMap","mapReady","mapLibreMap","once","deserializeFeatures","Object","keys","key","JSON","parse","_e","changeLayerProps","newLayerProps","prevLayerProps","layerId","maxzoom","minzoom","setLayerZoomRange","getMinZoom","getMaxZoom","setFilter","property","layout","paint","setPaintProperty","entries","changeLayersProps","forEach","layoutPaint","addLayers","layersToAdd","layerIdsAlreadyOnMap","Set","visibility","add","mapIdDependency","has","push","idsWeCanProcess","console","error","stringify","updateStyleWithModule","style","styleModule","include","cannotAddStyleModuleToCustomStyle","ensureAddedToStyle","isStyleLoaded","setStyle","isSourceLoaded","waitUntilSourceIsLoaded","addOrUpdateImage","imageId","imageToLoad","warn","addOrUpdateToMap","imgElement","hasImage","addImage","ensureImageLoaded","complete","naturalWidth","onload","onerror","loadImage","getStandardStyleTheme","standardStyleID","getStyleLightDarkTheme","styleInput","standardStyle","AbstractMapModule","sourceType","config","_initializing","moduleReady","eventsProxy","_eventsProxy","initSourcesWithLayers","applyConfig","addStyleChangeHandler","onStyleAboutToChange","onStyleChanged","restoreDataAndConfig","restore","sourcesWithLayers","_initSourcesWithLayers","fromEntries","name","sourceWithLayers","updateIfRegistered","waitUntilModuleReady","Promise","resolve","interval","setInterval","clearInterval","_applyConfig","resetConfig","requestAnimationFrame","restoreDataAndConfigImpl","getConfig","EventsModule","eventProxy","on","handler","addEventHandler","off","remove","AbstractEventProxy","interactiveLayerIDs","handlers","findHandlers","types","flatMap","sourceEventTypeHandlers","ensureInteractiveLayerIDsAdded","handlerFn","fn","matchesLayers","isEmpty","removeAll","hasSourceID","values","sourceHandlers","isHighPriority","eventType","featureId","featuresToUpdate","i","findFeatureById","updatedFeature","omit","splice","removeEventStateAndShow","newEventType","rawFeature","prevFeaturesToUpdate","updatedIndex","updateEventState","eventFeature","prevEventFeature","prevSourceWithLayers","featureB","featureA","eventsProxyDefaultConfig","precisionMode","paddingBoxPx","cursorOnHover","cursorOnMouseDown","cursorOnMap","longHoverDelayAfterMapMoveMS","longHoverDelayOnStillMapMS","EventsProxy","enabled","firstDelayedHoverSinceMapMove","mapCanvas","getCanvas","cursor","lastCursorStyle","listenToEvents","ev","onMouseMove","onMouseStart","onMouseOut","onMouseDown","onMouseUp","onMapClick","enable","clearLongHoverTimeout","toPaddedBounds","point","padding","x","y","isEnabled","isMoving","getRenderedFeatures","precision","renderedFeatures","queryRenderedFeatures","window","clearTimeout","longHoverTimeoutHandlerID","restartLongHoverTimeout","setTimeout","handleLongHoverTimeout","hoveringSourceWithLayers","hoveringFeature","hoveringLngLat","hoveringFeatures","hoveredTopFeature","hoverChanged","mouseInMotionOverHoveredFeature","hoveringPoint","prevHoveredPoint","prevHoveredFeature","detectHoverState","lngLat","prevHoveredSourceWithLayers","firstHandler","updateHoverCursor","hoverHandlers","hoverMoveHandlers","clickType","clickedFeatures","prevClickedFeature","lastClickedFeature","prevClickedSourceWithLayers","lastClickedSourceWithLayers","clickHandlers","mapStyleLayerIDs","country","lowestPlaceLabel","poi","lowestLabel","lowestRoadLine","lowestBuilding","POI_SOURCE_ID","HILLSHADE_SOURCE_ID","BASE_MAP_SOURCE_ID","TRAFFIC_INCIDENTS_SOURCE_ID","TRAFFIC_FLOW_SOURCE_ID","poiLayerIDs","mapDisplayPoiCategoryMappings","ACCESS_GATEWAY","ADVENTURE_SPORTS_FACILITY","ADVENTURE_SPORTS_VENUE","AGRICULTURE","AIRPORT","AMUSEMENT_PARK","AQUATIC_ZOO","ASHRAM","ATM","AUTOMOTIVE_DEALER","BANK","BEACH","BUS_STOP","BUSINESS_PARK","CAFE_PUB","CAMPING_GROUND","CAR_WASH","CASH_DISPENSER","CASINO","CHURCH","CINEMA","CLOTHING_SHOP","CLUB_ASSOCIATION","COLLEGE_UNIVERSITY","COMMERCIAL_BUILDING","COMMUNITY_CENTER","COMPANY","CONCERT_HALL","COURTHOUSE","CULTURAL_CENTER","DENTIST","DEPARTMENT_STORE","DOCTOR","ELECTRIC_VEHICLE_STATION","EMBASSY","EMERGENCY_MEDICAL_SERVICE","EMERGENCY_ROOM","ENTERTAINMENT","EXCHANGE","EXHIBITION_CONVENTION_CENTER","FERRY_TERMINAL","FIRE_STATION_BRIGADE","FRONTIER_CROSSING","FUEL_FACILITIES","GAS_STATION","GEOGRAPHIC_FEATURE","GOLD_EXCHANGE","GOLF_COURSE","GOVERNMENT_OFFICE","GURUDWARA","HEALTH_CARE_SERVICE","HELIPAD_HELICOPTER_LANDING","HILL","HOLIDAY_RENTAL","HOSPITAL","HOSPITAL_POLYCLINIC","HOTEL_MOTEL","ICE_SKATING_RINK","IMPORTANT_TOURIST_ATTRACTION","INDUSTRIAL_BUILDING","LEISURE_CENTER","LIBRARY","MANUFACTURING_FACILITY","MARINA","MARKET","MEDIA_FACILITY","MILITARY_INSTALLATION","MOSQUE","MOTORING_ORGANIZATION_OFFICE","MOUNTAIN_PASS","MOUNTAIN_PEAK","MOVIE_THEATER","MUSEUM","NATIVE_RESERVATION","NIGHTLIFE","NON_GOVERNMENTAL_ORGANIZATION","OPEN_PARKING_AREA","OPERA_HOUSE","PAGODA","PARK_RECREATION_AREA","PARKING_GARAGE","PETROL_STATION","PHARMACY","PLACE_OF_WORSHIP","POLICE_STATION","PORT_WAREHOUSE_FACILITY","POST_OFFICE","PRIMARY_RESOURCE_UTILITY","PRISON_CORRECTIONAL_FACILITY","PUBLIC_AMENITY","PUBLIC_TRANSPORT_STOP","RAILWAY_STATION","RENT_A_CAR_FACILITY","RENT_A_CAR_PARKING","REPAIR_FACILITY","RESEARCH_FACILITY","RESIDENTIAL_ACCOMMODATION","REST_AREA","RESTAURANT","RESTAURANT_AREA","SCENIC_PANORAMIC_VIEW","SCHOOL","SHOP","SHOPPING_CENTER","SPORTS_CENTER","STADIUM","SUPERMARKETS_HYPERMARKETS","SWIMMING_POOL","SYNAGOG","TAXI_STAND","TEMPLE","TENNIS_COURT","THEATER","TOLL_GATE","TOURIST_INFORMATION_OFFICE","TRAFFIC_CONTROL_DEPARTMENT","TRAFFIC_SERVICE_CENTER","TRAIL_SYSTEM","TRAILS","TRANSPORT_AUTHORITY_VEHICLE_REGISTRATION","TRUCK_STOP","VACATION_RENTAL","VETERINARIAN","WATER_SPORT","WEIGH_STATION","WELFARE_ORGANIZATION","WINERY","ZOOS_ARBORETA_BOTANICAL_GARDEN","completeMapDisplayPoiCategoryMappings","SECURED_ENTRANCE","AGRICULTURAL_BUSINESS","FARM","HORTICULTURE","PRIMARY_PRODUCER","AIRFIELD","AIRLINE_ACCESS","MILITARY_AIRPORT","PRIVATE_AIRPORT","PUBLIC_AIRPORT","ATV_DEALER","BOAT_DEALER","BUS_DEALER","CAR_DEALER","MOTORCYCLE_DEALER","RECREATIONAL_VEHICLE_DEALER","TRUCK_DEALER","VAN_DEALER","DIVERSIFIED_FINANCIALS","SAVINGS_INSTITUTION","BEACH_CLUB","BAR","CAFE","COCKTAIL_BAR","COFFEE_SHOP","INTERNET_CAFE","PUB","TEA_HOUSE","WINE_BAR","CARAVAN_SITE","RECREATIONAL_CAMPING_GROUND","REST_CAMP","TRUCK_WASH","DRIVE_IN_MOVIES","CHILDRENS_CLOTHES","MENS_CLOTHING","SPECIALTY_CLOTHING_SHOP","WOMENS_CLOTHING","PRIVATE_CLUB","JUNIOR_COLLEGE_COMMUNITY_COLLEGE","BUILDING","ADVERTISING_COMPANY","AGRICULTURAL_TECHNOLOGY","AIRLINE_COMPANY","AUTOMOBILE_COMPANY","BUSINESS_SERVICES","BUS_CHARTER_COMPANY","CABLE_TELEPHONE_COMPANY","CLEANING_SERVICES","COMPUTER_DATA_SERVICES","CONSTRUCTION_COMPANY","DELIVERY_SERVICE","ELECTRONICS_COMPANY","EQUIPMENT_RENTAL","FUNERAL_SERVICE_MORTUARIES","IMPORT_EXPORT_AND_DISTRIBUTION","INSURANCE_COMPANY","INVESTMENT_ADVISOR","LEGAL_SERVICES","MINING_COMPANY","MOVING_STORAGE_COMPANY","OIL_NATURAL_GAS","PHARMACEUTICAL_COMPANY","PUBLIC_HEALTH_TECHNOLOGY_COMPANY","PUBLISHING_TECHNOLOGIES","REAL_ESTATE_AGENT","REAL_ESTATE_COMPANY","SERVICE_COMPANY","SOFTWARE_COMPANY","TAX_SERVICES","TELECOMMUNICATIONS","TRANSPORT_COMPANY","TRAVEL_AGENT","WEDDING_SERVICES","GENERAL_PRACTITIONER","SPECIALIST","RIDGE","AMBULANCE_UNIT","ROAD_RESCUE","AMUSEMENT_ARCADE","AMUSEMENT_PLACE","BETTING_STATION","FAIRGROUND","MUSIC_CENTER","CHECKPOINT","BAY","BRIDGE","BRIDGE_TUNNEL_OPERATIONS","CAPE","COVE","DAM","DUNE","ISLAND","LAGOON","LAKESHORE","LOCALE","MARSH","OASIS","PAN","PARKWAY","PLAIN_FLAT","PLATEAU","RAPIDS","REEF","RESERVOIR","RIVER_CROSSING","RIVER_SCENIC_AREA","ROCKS","SEASHORE","TUNNEL","VALLEY","WATER_HOLE","WELL","BUNGALOW_RENTAL","CABINS_LODGES","CHALET_RENTAL","COTTAGE_RENTAL","VILLA_RENTAL","BLOOD_BANK","HOSPITAL_FOR_WOMEN_AND_CHILDREN","HOSPITAL_OF_CHINESE_MEDICINE","SPECIAL_HOSPITAL","B_B_GUEST_HOUSE","HOSTEL","HOTEL","MOTEL","RESORT","QUARRY","AUTOMOBILE_MANUFACTURING","CHEMICAL_COMPANY","MANUFACTURING_COMPANY","MECHANICAL_ENGINEERING","MICROBREWERY","OEM","BOAT_LAUNCHING_RAMP","HARBOR","YACHT_BASIN","FARMERS_MARKET","FOOD_MARKET","INFORMAL_MARKET","PUBLIC_MARKET","PLANETARIUM","CABARET_THEATER","COMEDY_CLUB","DISCO_CLUB","JAZZ_CLUB","KARAOKE_CLUB","FISHING_HUNTING_AREA","FOREST_AREA","HISTORICAL_PARK","NATURAL_RECREATION_ATTRACTION","PARK","PICNIC_AREA","PRESERVE","RECREATION_AREA","WILDERNESS_AREA","OPEN_CAR_PARKING_AREA","DRUG_STORE","MARIJUANA_DISPENSARY","MEDICINAL_MARIJUANA_DISPENSARY","RECREATIONAL_MARIJUANA_DISPENSARY","COURIER_DROP_BOX","LOCAL_POST_OFFICE","PUBLIC_CALL_BOX","PUBLIC_TOILET","BUS_LINES","COACH_STOP","PASSENGER_TRANSPORT_TICKET_OFFICE","PEDESTRIAN_SUBWAY","STREETCAR_STOP","SUBWAY_STATION","INTERNATIONAL_RAILROAD_STATION","NATIONAL_RAILROAD_STATION","RAILROAD_SIDING","STATION_ACCESS","URBAN_STATION","BODYSHOP","CAR_GLASS_REPLACEMENT_SHOP","CAR_REPAIR_AND_SERVICE","HOME_APPLIANCE_REPAIR","MOTORCYCLE_REPAIR","OTHER_REPAIR_SHOPS","REPAIR_SHOP","TIRE_SERVICE","TRUCK_REPAIR_AND_SERVICE","AFGHAN_RESTAURANT","AFRICAN_RESTAURANT","ALGERIAN_RESTAURANT","AMERICAN_RESTAURANT","ARABIAN_RESTAURANT","ARGENTINIAN_RESTAURANT","ARMENIAN_RESTAURANT","ASIAN_RESTAURANT","AUSTRALIAN_RESTAURANT","AUSTRIAN_RESTAURANT","BANQUET_ROOMS","BARBECUE_RESTAURANT","BASQUE_RESTAURANT","BELGIAN_RESTAURANT","BISTRO","BOLIVIAN_RESTAURANT","BOSNIAN_RESTAURANT","BRAZILIAN_RESTAURANT","BRITISH_RESTAURANT","BUFFET_RESTAURANT","BULGARIAN_RESTAURANT","BURMESE_RESTAURANT","CAFETERIA","CALIFORNIAN_RESTAURANT","CAMBODIAN_RESTAURANT","CANADIAN_RESTAURANT","CARIBBEAN_RESTAURANT","CATERING_SERVICES","CHICKEN_RESTAURANT","CHILEAN_RESTAURANT","CHINESE_RESTAURANT","COLOMBIAN_RESTAURANT","CORSICAN_RESTAURANT","CREOLE_RESTAURANT","CREPERIE","CUBAN_RESTAURANT","CYPRIOT_RESTAURANT","CZECH_RESTAURANT","DANISH_RESTAURANT","DINNER_THEATER","DOMINICAN_RESTAURANT","DONGBEI_RESTAURANT","DOUGHNUT_RESTAURANT","DUTCH_RESTAURANT","EGYPTIAN_RESTAURANT","ENGLISH_RESTAURANT","EROTIC_RESTAURANT","ETHIOPIAN_RESTAURANT","EXOTIC_RESTAURANT","FAST_FOOD","FINNISH_RESTAURANT","FONDUE_RESTAURANT","FRENCH_RESTAURANT","FUSION_RESTAURANT","GERMAN_RESTAURANT","GREEK_RESTAURANT","GRILL_RESTAURANT","GUANGDONG_RESTAURANT","HAMBURGER_RESTAURANT","HAWAIIAN_RESTAURANT","HOT_POT_RESTAURANT","HUNAN_RESTAURANT","HUNGARIAN_RESTAURANT","ICE_CREAM_PARLOR","INDIAN_RESTAURANT","INDONESIAN_RESTAURANT","INTERNATIONAL_RESTAURANT","IRANIAN_RESTAURANT","IRISH_RESTAURANT","ISRAELI_RESTAURANT","ITALIAN_RESTAURANT","JAMAICAN_RESTAURANT","JAPANESE_RESTAURANT","JEWISH_RESTAURANT","KOREAN_RESTAURANT","KOSHER_RESTAURANT","LATIN_AMERICAN_RESTAURANT","LEBANESE_RESTAURANT","LUXEMBOURGIAN_RESTAURANT","MACROBIOTIC_RESTAURANT","MAGHRIB_RESTAURANT","MALTESE_RESTAURANT","MAURITIAN_RESTAURANT","MEDITERRANEAN_RESTAURANT","MEXICAN_RESTAURANT","MIDDLE_EASTERN_RESTAURANT","MONGOLIAN_RESTAURANT","MOROCCAN_RESTAURANT","MUSSELS_RESTAURANT","NEPALESE_RESTAURANT","NORWEGIAN_RESTAURANT","ORGANIC_FOOD_RESTAURANT","ORIENTAL_RESTAURANT","PAKISTANI_RESTAURANT","PERUVIAN_RESTAURANT","PHILIPPINE_RESTAURANT","PIZZERIA","POLISH_RESTAURANT","POLYNESIAN_RESTAURANT","PORTUGUESE_RESTAURANT","PROVENCAL_RESTAURANT","PUB_FOOD","ROADSIDE_RESTAURANT","ROMANIAN_RESTAURANT","RUSSIAN_RESTAURANT","SALAD_BAR","SANDWICH_RESTAURANT","SAVOY_RESTAURANT","SCANDINAVIAN_RESTAURANT","SCOTTISH_RESTAURANT","SEAFOOD","SHANDONG_RESTAURANT","SHANGHAI_RESTAURANT","SICHUAN_RESTAURANT","SICILIAN_RESTAURANT","SLAVIC_RESTAURANT","SLOVAK_RESTAURANT","SNACKS_RESTAURANT","SOUL_FOOD","SOUP_RESTAURANT","SPANISH_RESTAURANT","STEAK_HOUSE","SUDANESE_RESTAURANT","SURINAMESE_RESTAURANT","SUSHI_RESTAURANT","SWEDISH_RESTAURANT","SWISS_RESTAURANT","SYRIAN_RESTAURANT","TAIWANESE_RESTAURANT","TAKEOUT_FOOD","TAPAS_RESTAURANT","TEPPANYAKI_RESTAURANT","THAI_RESTAURANT","TIBETAN_RESTAURANT","TUNISIAN_RESTAURANT","TURKISH_RESTAURANT","URUGUAYAN_RESTAURANT","VEGETARIAN_RESTAURANT","VENEZUELAN_RESTAURANT","VIETNAMESE_RESTAURANT","WELSH_RESTAURANT","WESTERN_RESTAURANT","YOGURT_JUICE_BAR","ART_SCHOOL","CHILD_CARE_FACILITY","CULINARY_SCHOOL","DANCE_STUDIO_SCHOOL","DRIVING_SCHOOL","HIGH_SCHOOL","LANGUAGE_SCHOOL","MIDDLE_SCHOOL","PRE_SCHOOL","PRIMARY_SCHOOL","SENIOR_HIGH_SCHOOL","SPECIAL_SCHOOL","SPORT_SCHOOL","TECHNICAL_SCHOOL","VOCATIONAL_SCHOOL","AGRICULTURAL_SUPPLIES","ANTIQUE_ART_SHOP","BAGS_LEATHERWEAR","BAKERY","BEAUTY_SALON","BEAUTY_SUPPLIES","BOATING_EQUIPMENT_ACCESSORIES","BOOK_SHOP","BUTCHER","CAMERAS_PHOTOGRAPHY","CARPET_FLOOR_COVERINGS","CAR_ACCESSORIES","CHRISTMAS_HOLIDAY_SHOP","COMPUTER_COMPUTER_SUPPLIES","CONSTRUCTION_MATERIAL_EQUIPMENT","CONSUMER_ELECTRONICS","CONVENIENCE_STORE","CURTAINS_TEXTILES","C_DS_DVD_VIDEOS","DELICATESSEN","DO_IT_YOURSELF_CENTERS","DRIVE_THROUGH_BOTTLE_SHOP","DRY_CLEANER","ELECTRICAL_APPLIANCES_SHOP","FACTORY_OUTLET","FISHMONGER","FLORISTS","FOOTWEAR_SHOE_REPAIRS","FURNITURE_HOME_FURNISHINGS","GARDEN_CENTERS_SERVICES","GIFTS_CARDS_NOVELTIES_SOUVENIRS","GLASSWARE_CERAMIC_SHOP","GLASS_WINDOWS_STORE","GREENGROCER","GROCERY_STORE","HAIRDRESSER","HARDWARE_STORE","HOBBY_SHOP","HOUSE_GARDEN_FURNITURE_FITTINGS","JEWELRY_CLOCKS_WATCHES","KITCHENS_BATHROOMS","LAUNDRY","LIGHTING_SHOPS","LOCAL_SPECIALITIES_SHOP","LOTTERY_SHOP","MARINE_ELECTRONIC_EQUIPMENT","MEDICAL_SUPPLIES_EQUIPMENT","MOBILE_PHONE_SHOP","MUSIC_INSTRUMENTS_STORE","NAIL_SALON","NEWSAGENTS_TOBACCONISTS","OFFICE_EQUIPMENT","OPTICIAN","OTHER_FOOD_SHOPS","PAINTING_DECORATING","PAWN_SHOP","PERSONAL_CARE_FACILITY","PERSONAL_SERVICE","PET_SUPPLIES","PHOTOCOPY_SHOP","PHOTO_LAB_DEVELOPMENT","RECYCLING_SHOP","RETAIL_OUTLET","SAUNA_SOLARIUM_MASSAGE","SECURITY_PRODUCTS","SHOPPING_SERVICE","SPECIALTY_FOODS","SPORTS_EQUIPMENT_CLOTHING","STAMP_SHOP","TAILOR_SHOP","TOYS_GAMES_SHOP","VARIETY_STORE","VIDEO_RENTAL_SHOP","WHOLESALE_CLUB","WINE_SPIRITS","ATHLETICS_TRACK","BASEBALL_PARK","BASKETBALL_ARENA","BOWLING_CENTER","CRICKET_GROUND","FITNESS_CLUB_CENTER","FLYING_CLUB","HOCKEY_CLUB","HORSE_RACING_TRACK","HORSE_RIDING_CENTER","ICE_HOCKEY_ARENA","OTHER_WINTER_SPORT","RACE_TRACK","RUGBY_GROUND","SKI_RESORT","SNOOKER_POOL_BILLIARD","THEMATIC_SPORT_CENTER","FOOTBALL_STADIUM","MOTOR_RACING_STADIUM","MULTI_PURPOSE_STADIUM","NETBALL_STADIUM","SOCCER_STADIUM","STOCK_EXCHANGE","TAXI_LIMOUSINE_SHUTTLE_SERVICE","AMPHITHEATER","ARCH","BATTLEFIELD","CAVE","CEMETERY","HISTORIC_SITE","MAUSOLEUM_GRAVE","MEMORIAL","MINERAL_HOT_SPRINGS","MONUMENT","NATURAL_TOURIST_ATTRACTION","OBSERVATORY","STATUE","TOURIST_ATTRACTION","TOWER","ROAD_TRAFFIC_CONTROL_CENTER","ADVENTURE_VEHICLE_TRAIL","HIKING_TRAIL","HORSE_RIDING_TRAIL","MOUNTAIN_BIKE_TRAIL","ROCK_CLIMBING_TRAIL","APARTMENT_RENTAL","CONDOMINIUM_COMPLEX","FLATS_APARTMENT_COMPLEX","RESIDENTIAL_ESTATE","RETIREMENT_COMMUNITY","TOWNHOUSE_COMPLEX","ANIMAL_SERVICES","ANIMAL_SHELTER","WEIGH_SCALES","WILDLIFE_PARK","ZOO","toBaseMapPOICategory","category","MAP_BOLD_FONT","DEFAULT_MAX_PIN_SCALE","TITLE","ICON_ID","DEFAULT_TEXT_OFFSET_Y","DEFAULT_TEXT_OFFSET_X","DEFAULT_PLACE_ICON_ID","pinIconBaseLayout","pinIconBasePaint","pinTextBaseLayout","pinTextBasePaint","pinLayerBaseSpec","suffixNumber","text","numberToSuffix","calculateIconScale","image","theme","dimensions","svgElement","viewBox","getAttribute","parts","split","width","Number","parseFloat","height","naturalHeight","extractImageDimensions","heightScale","widthScale","hasChargingAvailability","chargingPark","Boolean","availability","isEVStationWithAvailability","place","classifications","code","getChargingPointAvailability","chargingPointAvailability","available","statusCounts","Available","availableCount","totalCount","count","ratio","defaultFormatAvailabilityText","total","getAvailabilityRatio","getAvailabilityColorExpression","threshold","buildAnchorOffsets","topOffset","sideOffset","pinVerticalAdjustment","customTextOffset","hasCustomOffset","top","left","right","getTextOffset","iconSizeExpression","iconTextOffsetScales","iconScaleMultiplier","expression","Array","isArray","lastValue","at","extractMaxIconScale","isBaseMapTheme","fallbackSideOffset","fallbackOffsets","fallbackAnchorOffset","size","offsetCaseExpression","iconId","scales","offsets","buildLayoutConfig","layerName","textField","textConfig","customLayer","hasCustomIcons","baseLayout","offset","font","iconSize","Map","assign","buildPaintConfig","lightDark","textColor","baseTextColor","haloColor","baseHaloColor","getThemeAdaptiveTextColors","color","haloWidth","isClickEventState","getTextSizeSpec","textSize","replaceAll","hasEventState","SELECTED_COLOR","pinLayerSpec","selectedPinLayerSpec","withConfig","textFieldExpression","evAvailabilityEnabled","evAvailability","buildTextFieldExpression","title","buildPlacesLayerSpecs","styleLightDarkTheme","instanceIndex","customIcons","icon","categoryIcons","suffixedIconId","set","availabilityLevel","availabilitySuffixedId","buildCustomIconScalesMap","main","selected","poiLikeLayerSpec","poiLayer","buildPoiLikeLayerSpec","additional","defaultPin","supportedPinSubcategories","buildPlaceTitle","address","freeformAddress","toImageID","poiCategory","iconTheme","defaultPlaceIconID","categoryID","substring","toPinImageID","poiCategoriesToID","imageID","getIconIDForPlace","imageMapping","mapping","to","evAvailabilityIconID","requiredLevel","customIconWithAvailability","customIcon","getEVAvailabilityIconID","matchingCustomIcon","getPOILayerCategoryForPlace","toPlaces","places","mergeEVAvailabilityProps","extraFeatureProps","evAvailabilityConfig","hasEVStations","hasEVStationsWithAvailability","evAvailabilityText","formatText","buildAvailabilityText","evAvailabilityRatio","preparePlacesForDisplay","placesInput","mergedExtraFeatureProps","prop","generateId","geometry","bbox","iconID","_PlacesModule","get","lastInstanceIndex","layerIDPrefix","buildLayerSpecs","layerSpecTemplates","updateLayersAndData","previousShownFeatures","applyTheme","applyConfigPart","applyIconConfig","iconConfig","applyTextConfig","partialConfig","applyExtraFeatureProps","updateData","setupImages","newLayerSpecs","newLayerSpecsArray","oldLayerSpecsArray","pixelRatio","default","getShown","events","PlacesModule","isExpressionFilter","slice","getMergedAnyFilter","filters","legacy","getMergedAllFilter","filterToAdd","originalFilter","buildMappedValuesFilter","propName","showMode","comparator","filterArrayNew","buildValuesFilter","valuesMapping","poiCategoryGroups","FOOD_DRINKS_GROUP","SHOPPING_GROUP","TRANSPORTATION_GROUP","HEALTH_GROUP","PARKING_GROUP","HOLIDAY_TOURISM_GROUP","EV_CHARGING_STATIONS_GROUP","GAS_STATIONS_GROUP","ACCOMMODATION_GROUP","ENTERTAINMENT_GROUP","SPORTS_LEISURE_GROUP","EDUCATION_GROUP","GOVERNMENT_GROUP","getStyleCategories","categories","categoryIds","POIsModule","poiRuntimeSource","mainLayer","getFilter","setVisible","isVisible","filterCategories","categoriesFilter","poiFilter","layerGroupMappings","land","layerIDMatches","layerTypes","borders","water","buildings2D","buildings3D","houseNumbers","roadLines","roadLabels","roadShields","placeLabels","smallerTownLabels","cityLabels","capitalLabels","stateLabels","countryLabels","isMatching","group","part","toLowerCase","buildBaseMapLayerGroupFilter","layerGroupsFilter","layerGroups","groups","names","filterLayerByGroups","BaseMapModule","vectorTiles","layerGroupsVisibility","buildLayerGroupFilter","baseMapLayerGroupNames","GEOMETRY_TITLE_PROP","GEOMETRY_COLOR_PROP","colorPalettes","warm","browns","cold","fadedBlues","blues","greens","fadedGreenToBlue","blueToRed","greenToYellow","pastel","retro","contrastRetro","fadedRainbow","pastelRainbow","defaultColor","geometryFillSpec","geometryOutlineSpec","buildGeometryLayerSpecs","fillLayerId","outlineLayerId","colorConfig","lineConfig","fillOpacity","lineColor","lineWidth","lineOpacity","buildGeometryTitleLayerSpec","prepareGeometryForDisplay","buildTitle","palette","buildColor","prepareTitleForDisplay","geometries","coordinates","placeCoordinates","biggestPolygon","flat","reduce","result","coord","getLongestArray","bboxFromCoordsArray","bboxCenter","_GeometriesModule","titleSourceID","layerIdPrefix","fillLayerID","outlineLayerID","titleLayerID","titleLayerSpec","titleLayerSpecs","geometryFillLayerSpecs","geometryOutlineLayerSpecs","geometryLabel","updateLayerAndData","beforeLayerConfig","moveBeforeLayer","moveBeforeLayerID","beforeLayerId","moveLayer","layerConfig","newTitleLayerSpecs","GeometriesModule","HillshadeModule","hillshadeSource","hillshade","standardStyleIDs","styleModules","ROUTE_LINE_FOREGROUND_COLOR","ROUTE_LINE_OUTLINE_COLOR","DESELECTED_FOREGROUND_COLOR","DESELECTED_OUTLINE_COLOR","DESELECTED_SECONDARY_COLOR","ROUTE_LINE_FOREGROUND_WIDTH","SELECTED_ROUTE_FILTER","DESELECTED_ROUTE_FILTER","MAJOR_DELAY_COLOR","MODERATE_DELAY_COLOR","MINOR_DELAY_LABEL_COLOR","UNKNOWN_DELAY_COLOR","chargingStopTextField","chargingStopSymbol","commonProps","commonLineProps","instructionOutline","instructionLine","INSTRUCTION_ARROW_IMAGE_ID","instructionArrow","routeFerriesLine","routeFerriesSymbol","routeLineBaseTemplate","outlineLineWidth","routeDeselectedOutline","routeDeselectedLine","routeOutline","routeLineArrows","SELECTED_SUMMARY_POPUP_IMAGE_ID","DESELECTED_SUMMARY_POPUP_IMAGE_ID","routeTollRoadsOutline","routeTollRoadsSymbol","EXTRA_FOREGROUND_LINE_WIDTH","routeIncidentsBGLine","routeIncidentsDashedLine","magnitudeOfDelayTextColor","routeIncidentsSymbolBase","routeIncidentsJamSymbol","routeIncidentsCauseSymbol","routeTunnelsLine","routeVehicleRestrictedBackgroundLine","routeVehicleRestrictedDottedLine","TRAFFIC_CLEAR_IMAGE_ID","TRAFFIC_MAJOR_IMAGE_ID","TRAFFIC_MODERATE_IMAGE_ID","TRAFFIC_MINOR_IMAGE_ID","hasFormattedTraffic","buildSummaryBubbleSymbolPoint","selectedImageID","deselectedImageID","trafficClearID","trafficMajorID","trafficModerateID","trafficMinorID","summaryBubbleSymbolPoint","START_INDEX","MIDDLE_INDEX","FINISH_INDEX","INDEX_TYPE","STOP_DISPLAY_INDEX","WAYPOINT_START_IMAGE_ID","WAYPOINT_STOP_IMAGE_ID","WAYPOINT_SOFT_IMAGE_ID","WAYPOINT_FINISH_IMAGE_ID","pinIndexLabelLayout","waypointSymbols","waypointLabels","prefixBeforeID","startsWith","prefixBeforeIDs","suffixImageID","buildRoutingLayers","configLayers","configSectionLayers","sections","mainColor","mainLines","routeLine","props","waypoints","routeWaypointSymbol","routeWaypointLabel","chargingStops","routeChargingStopSymbol","incident","routeIncidentJamSymbol","routeIncidentCauseSymbol","routeIncidentBackgroundLine","routeIncidentDashedLine","ferry","routeFerryLine","routeFerrySymbol","tollRoad","routeTollRoadOutline","routeTollRoadSymbol","tunnel","routeTunnelLine","vehicleRestricted","routeVehicleRestrictedForegroundLine","instructionLines","routeInstructionLine","routeInstructionOutline","instructionArrows","routeInstructionArrowSymbol","summaryBubbles","routeSummaryBubbleSymbol","defaultRoutingLayers","instructionArrowIconImg","summaryMapBubbleImg","svg","summaryBubbleImageOptions","stretchX","stretchY","content","trafficImg","waypointIcon","foregroundSvg","svgOptions","appendChild","mapLayerSpecs","createLayersSpecs","layerConfigs","ferries","incidents","tollRoads","tunnels","routeModuleConfigWithDefaults","globalDisplayUnits","TomTomConfig","instance","displayUnits","getIconID","chargingStop","basedOn","chargingConnectionInfo","chargingSpeed","formatTitle","chargingParkName","chargingParkOperatorName","toDisplayChargingStops","routes","displayChargingStops","route","leg","summary","chargingInformationAtEndOfLeg","chargingParkId","chargingPower","chargingPowerInkW","chargingDuration","formatDuration","chargingTimeInSeconds","time","routeState","hasJam","sectionProps","toDisplayTrafficSectionProps","delayInSeconds","jamIconID","magnitudeOfDelay","toTrafficJamIconSuffix","toJamIconID","causeIconID","toCauseIconID","getImageIDForWaypoint","waypoint","indexType","radiusMeters","baseImageID","toDisplayWaypoints","hardWaypointIndex","waypointInput","asWaypoint","arrayLength","indexTypeFor","hardWaypoint","isHardWaypoint","placeProperties","buildWaypointTitle","entryPoints","getPosition","useEntryPoint","stopDisplayIndex","toDisplayRouteSections","sectionType","displaySectionPropsBuilder","startPointIndex","endPointIndex","routeIndex","buildRouteSectionsFromRoute","showFeaturesWithRouteSelection","routesWithSelection","toDisplayRoutes","selectedIndex","routesCollection","hasMagnitude","magnitude","section","toDisplayRouteSummaries","routeCoordinates","formattedTraffic","trafficDelayInSeconds","trafficSections","traffic","summaryDelayMagnitude","Math","round","formattedDistance","formatDistance","lengthInMeters","distance","formattedDuration","travelTimeInSeconds","_RoutingModule","createSourcesWithLayers","layersSpecs","sourcePrefix","routingSourcesWithLayers","svgIconOptions","waypointStartImageId","waypointStopImageId","waypointSoftImageId","waypointFinishImageId","instructionArrowImageId","selectedSummaryPopupImageId","deselectedSummaryPopupImageId","trafficClearImageId","trafficMajorImageId","trafficModerateImageId","trafficMinorImageId","addImageIfNotExisting","waypointStartIcon","softWaypointIcon","waypointFinishIcon","customChargingStopIcon","mergedConfig","newLayersSpecs","layersSpecID","oldLayersSpecs","newLayersMap","acc","cur","oldLayersMap","layersToRemove","newLayersToUpdate","oldLayersToUpdate","removeLayer","toBeAddedLayerSpec","updateLayersAndSource","listOfSources","isEqual","previouslyShown","entry","item","showRoutes","displayRoutes","guidance","instructions","instruction","routePath","pathPoint","toDisplayInstructions","instructionLastSegment","lastPointBearingDegrees","bearing","toDisplayInstructionArrows","clearRoutes","selectRoute","updatedRoutes","showWaypoints","displayWaypoints","clearWaypoints","getLayerToRenderLinesUnder","RoutingModule","DEFAULT_STANDARD_STYLE_ID","standardStyleModulesValues","standardLight","trafficIncidents","trafficFlow","standardDark","drivingLight","drivingDark","monoLight","monoDark","satellite","baseMapStyleUrlTemplate","suffix","baseMapStyleUrlTemplates","buildStandardStyleUrl","baseUrl","apiKey","styleURL","URL","replace","version","module","searchParams","append","buildStyleInput","mapParams","commonBaseURL","url","givenUrl","withApiKey","json","TomTomMap","tomtomMapParams","params","styleChangeHandlers","keepState","e","effectiveStyle","previousStyle","withPreviousStyleParts","_params","handleStyleData","mergeFromGlobal","ensureMapLibreCSSLoaded","setWorkerCount","validateStyle","maxTileCacheZoomLevels","cancelPendingTileRequestsWhileZooming","mapLibre","attributionControl","compact","transformRequest","resourceType","headers","generateTomTomHeaders","loadRTLTextPlugin","getRTLTextPluginStatus","setRTLTextPlugin","catch","from","querySelectorAll","textContent","link","rel","href","maplibreVersion","head","_setLanguage","language","mapLanguage","isLayerLocalizable","textFieldValue","setLanguage","getBBox","getBounds","toArray","setSprite","addPinCategoriesSpriteToStyle","incidentCategories","incidentCategoriesMapping","unknown","accident","fog","dangerous_conditions","rain","ice","jam","lane_closed","road_closed","road_works","wind","flooding","broken_down_vehicle","roadCategories","tertiaryRoadCategories","streetRoadCategories","toMultiSyntaxAllFilter","newSyntaxExpressions","legacySyntaxExpressions","addFilter","addValuesFilter","valuesFilter","addCommonFilterExpressions","sdkFilter","roadSubCategories","buildMapLibreIncidentsFilter","incidentCategoryFilter","magnitudes","magnitudesFilter","indexedMagnitudes","indexOf","delays","delayFilter","mustHaveDelay","minDelayMinutes","delaySeconds","delayFilterToMapLibre","buildMapLibreIncidentFilters","incidentFilters","any","mapLibreFilters","mapLibreFilter","buildMapLibreFlowFilter","showRoadClosures","operator","applyFilter","originalFilters","TrafficFlowModule","flowSource","getLayers","_filter","updateConfig","filterExpression","flowFilters","buildMapLibreFlowFilters","omitBy","TrafficIncidentsModule","incidentsSource","_setVisible","icons","setIconsVisible","iconFilters","incidentFilterExpression","getNonSymbolLayers","iconFilterExpression","getSymbolLayers","anyIconLayersVisible","getRelativeElementBounds","elementRect","containerRect","bottom","isElementOutsideContainer","bounds","containerWidth","containerHeight","adjustForHorizontalBar","currentVisible","max","min","adjustForVerticalBar","adjustVisibleBoundsForElement","mapWidthPX","mapHeightPX","clampedBounds","clampBoundsToContainer","spansFullWidth","spansFullHeight","touchesLeft","touchesRight","touchesTop","touchesBottom","distanceFromLeft","distanceFromRight","distanceFromTop","distanceFromBottom","minDistance","adjustForFloatingElement","toElement","ref","getMapLibreMap","calculateVisibleAreaBounds","surroundingElements","paddingPX","getContainer","getBoundingClientRect","visibleAreaBounds","elementRef","elementBounds","calculatePaddedBBox","sw","unproject","ne","lng","lat","calculatePaddedCenter","west","south","east","north","calculateFittingBBox","toBeContainedBBox","leftRatio","rightRatio","topRatio","widthRatio","heightRatio","fullWidth","fullHeight","expandedWest","expandedNorth"],"mappings":"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,UAAW,WAC/FC,WAAY,CACR,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,WAChGC,OAAQ,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,WACtFC,iBAAkB,CACd,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,UAAW,CACP,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,cAAe,CACX,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,OAAQ,CACJ,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,MAAO,CACH,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,cAAe,CACX,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEJC,aAAc,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,WACvGC,cAAe,CACX,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,YAMFC,GAAe,UAKRC,GAA8D,CACvE9nC,KAAM,OACNgD,MAAO,CACH,aAAc,CAAC,WAAY,CAAC,MAAO6jC,IAAsBgB,IACzD,eAAgB,IAChB,kBAAkB,IAObE,GAAiE,CAC1E/nC,KAAM,OACNgD,MAAO,CACH,aAAc6kC,GACd,eAAgB,IAChB,aAAc,IChJTG,GAA0B,CACnCC,EACAC,EACAhiC,KAEA,MAAMiiC,EAAcjiC,GAAQiiC,YACtBC,EAAaliC,GAAQkiC,WAuB3B,MAAO,CArBe,IACfN,GACHprC,GAAIurC,EACJjlC,MAAO,IACA8kC,GAAiB9kC,UACf1G,EAAM6rC,GAAaE,cAAgB,CAAE,eAAgBF,GAAaE,gBACnEF,GAAarsC,WAAa,CAAE,aAAc,CAAC,MAAO,YAIrC,IAClBisC,GACHrrC,GAAIwrC,EACJllC,MAAO,IACA+kC,GAAoB/kC,UAClB1G,EAAM8rC,GAAYE,YAAc,CAAE,aAAcF,GAAYE,eA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"}
1
+ {"version":3,"file":"map.es.js","sources":["../src/traffic/util/trafficFlowMapping.ts","../src/traffic/util/trafficIncidentMapping.ts","../src/shared/errorMessages.ts","../src/shared/imageUtils.ts","../src/shared/resources/index.ts","../src/shared/resources/pin.svg?raw","../src/shared/assertionUtils.ts","../src/shared/TomTomMapSource.ts","../src/shared/SourceWithLayers.ts","../src/shared/mapUtils.ts","../src/shared/AbstractMapModule.ts","../src/shared/EventsModule.ts","../src/shared/AbstractEventProxy.ts","../src/shared/eventUtils.ts","../src/shared/EventsProxy.ts","../src/shared/layers/layerIDs.ts","../src/shared/layers/sourcesIDs.ts","../src/pois/layers/poisLayers.ts","../src/pois/util/poiCategoryMapping.ts","../src/shared/layers/commonLayerProps.ts","../src/shared/layers/symbolLayers.ts","../src/shared/layers/utils.ts","../src/places/utils/customIconScales.ts","../src/places/utils/evAvailabilityHelpers.ts","../src/places/utils/textOffsetCalculator.ts","../src/places/utils/layerConfiguration.ts","../src/places/utils/themeAdaptation.ts","../src/shared/layers/eventState.ts","../src/places/utils/layerSpecBuilders.ts","../src/places/layers/placesLayers.ts","../src/places/resources/index.ts","../src/places/utils/toPinImageID.ts","../src/places/utils/preparePlacesForDisplay.ts","../src/places/PlacesModule.ts","../src/shared/mapLibreFilterUtils.ts","../src/pois/types/poiCategoryGroups.ts","../src/pois/POIsModule.ts","../src/base/layerGroups.ts","../src/base/BaseMapModule.ts","../src/base/types/baseMapModuleConfig.ts","../src/geometry/layers/constants.ts","../src/geometry/layers/geometryLayers.ts","../src/geometry/layers/colorPalettes.ts","../src/geometry/prepareGeometryForDisplay.ts","../src/geometry/GeometriesModule.ts","../src/geometry/prepareReachableRangesForDisplay.ts","../src/geometry/geometryConfigs.ts","../src/geometry/types/geometryTheme.ts","../src/hillshade/HillshadeModule.ts","../src/init/types/mapInit.ts","../src/routing/layers/shared.ts","../src/routing/layers/chargingStopLayers.ts","../src/routing/layers/guidanceLayers.ts","../src/routing/layers/routeFerrySectionLayers.ts","../src/routing/layers/routeMainLineLayers.ts","../src/routing/layers/routeTollRoadLayers.ts","../src/routing/layers/routeTrafficSectionLayers.ts","../src/routing/layers/routeTunnelSectionLayers.ts","../src/routing/layers/routeVehicleRestrictedLayers.ts","../src/routing/layers/summaryBubbleLayers.ts","../src/routing/types/waypointDisplayProps.ts","../src/routing/layers/waypointLayers.ts","../src/routing/layers/routingLayers.ts","../src/routing/resources/index.ts","../src/routing/resources/instruction-line-arrow.svg?raw","../src/routing/resources/summary-map-bubble.svg?raw","../src/routing/resources/traffic.svg?raw","../src/routing/util/config.ts","../src/routing/util/displayChargingStops.ts","../src/routing/util/displayTrafficSectionProps.ts","../src/routing/util/displayWaypoints.ts","../src/routing/util/routeSections.ts","../src/routing/util/routeSelection.ts","../src/routing/util/routes.ts","../src/routing/RoutingModule.ts","../src/routing/resources/start.svg?raw","../src/routing/resources/circle.svg?raw","../src/routing/resources/finish.svg?raw","../src/routing/util/guidance.ts","../src/init/styleInputBuilder.ts","../src/TomTomMap.ts","../src/init/buildMapOptions.ts","../src/shared/localization.ts","../src/traffic/filters/trafficFilters.ts","../src/traffic/TrafficFlowModule.ts","../src/traffic/TrafficIncidentsModule.ts","../src/traffic/types/trafficCommonConfig.ts","../src/utils/paddedBounds.ts"],"sourcesContent":["import type { MapGeoJSONFeature } from 'maplibre-gl';\nimport type { TrafficFlowModuleFeature } from '../types/trafficFlowFeature';\n\n/**\n * @ignore\n */\nexport const FLOW_TAGS = [\n 'road_category',\n 'road_subcategory',\n 'relative_speed',\n 'left_hand_traffic',\n 'road_closure',\n 'absolute_speed',\n 'part_of_two_way_road',\n 'openlr',\n 'display_class',\n] as const;\n\n/**\n * @ignore\n */\nexport const trafficFlowMapping = (feature: MapGeoJSONFeature): TrafficFlowModuleFeature => {\n const { properties } = feature;\n return {\n id: feature.id,\n type: feature.type,\n geometry: feature.geometry,\n properties: {\n roadCategory: properties?.road_category,\n leftHandTraffic: Boolean(properties?.left_hand_traffic),\n roadClosure: Boolean(properties?.road_closure),\n relativeSpeed: properties?.relative_speed,\n ...(properties?.road_subcategory && { roadSubcategory: properties.road_subcategory }),\n ...(properties?.absolute_speed && { absoluteSpeed: properties.absolute_speed }),\n ...(properties?.part_of_two_way_road && { partOfTwoWayRoad: Boolean(properties.part_of_two_way_road) }),\n ...(properties?.openlr && { openlr: properties.openlr }),\n ...(properties?.display_class && { displayClass: properties.display_class }),\n },\n } as TrafficFlowModuleFeature;\n};\n","import { generateId, indexedMagnitudes, type TrafficIncidentCategory } from '@tomtom-org/maps-sdk/core';\nimport type { MapGeoJSONFeature } from 'maplibre-gl';\nimport type { TrafficIncidentsModuleFeature } from '../types/trafficIncidentsFeature';\n\n/**\n * @ignore\n */\nexport const INCIDENT_TAGS = [\n 'icon_category',\n 'left_hand_traffic',\n 'magnitude_of_delay',\n 'road_category',\n 'road_subcategory',\n 'id',\n 'description',\n 'delay',\n 'part_of_two_way_road',\n 'start_time',\n 'end_time',\n 'probability_of_occurrence',\n 'number_of_reports',\n 'last_report_time',\n 'average_speed_kmph',\n 'openlr',\n 'time_validity',\n 'display_class',\n] as const;\n\n/**\n * @ignore\n */\nexport const incidentToIconCategoryMapping: Record<TrafficIncidentCategory, number> = {\n other: 0,\n accident: 1,\n fog: 2,\n danger: 3,\n rain: 4,\n frost: 5,\n jam: 6,\n 'lane-closed': 7,\n 'road-closed': 8,\n roadworks: 9,\n wind: 10,\n flooding: 11,\n 'animals-on-road': 12,\n 'narrow-lanes': 13,\n 'broken-down-vehicle': 14,\n} as const;\n\n// Reverse mapping:\nconst iconToIncidentCategoryMapping = Object.fromEntries(\n Object.entries(incidentToIconCategoryMapping).map(([category, code]) => [code, category]),\n) as Record<number, TrafficIncidentCategory>;\n\n/**\n * @ignore\n */\nexport const trafficIncidentMapping = (feature: MapGeoJSONFeature): TrafficIncidentsModuleFeature => {\n const { properties } = feature;\n return {\n id: feature.id,\n type: feature.type,\n geometry: feature.geometry,\n properties: {\n id: properties.id ?? generateId(), // generateID is a failsafe but properties.id should always be present in the vector tile data\n description: properties?.description_0 ?? '',\n category: iconToIncidentCategoryMapping[properties?.icon_category_0],\n magnitudeOfDelay: indexedMagnitudes[properties?.magnitude_of_delay],\n roadCategory: properties?.road_category,\n roadSubcategory: properties?.road_subcategory,\n leftHandTraffic: Boolean(properties?.left_hand_traffic),\n ...(properties?.delay && { delayInSeconds: properties.delay }),\n ...(properties?.part_of_two_way_road && { partOfTwoWayRoad: Boolean(properties.part_of_two_way_road) }),\n ...(properties?.start_time && { startTime: new Date(properties.start_time) }),\n ...(properties?.end_time && { endTime: new Date(properties.end_time) }),\n ...(properties?.probability_of_occurrence && {\n probabilityOfOccurrence: properties.probability_of_occurrence,\n }),\n ...(properties?.number_of_reports && { numberOfReports: properties.number_of_reports }),\n ...(properties?.last_report_time && { lastReportTime: new Date(properties.last_report_time) }),\n ...(properties?.average_speed_kmph && { averageSpeedKmph: properties.average_speed_kmph }),\n ...(properties?.openlr && { openlr: properties.openlr }),\n ...(properties?.time_validity && { timeValidity: properties.time_validity }),\n ...(properties?.display_class && { displayClass: properties.display_class }),\n ...(properties?.point_type && { pointType: properties.point_type }),\n },\n } as TrafficIncidentsModuleFeature;\n};\n","import type { StyleModule } from '../init';\n\n/**\n * @ignore\n */\nexport const notInTheStyle = (actionText: string): Error =>\n new Error(`Trying to ${actionText} while it is not in the map style. Did you exclude it when loading the map?`);\n\n/**\n * @ignore\n */\nexport const cannotAddStyleModuleToCustomStyle = (styleModule: StyleModule): Error =>\n new Error(`Trying to add style module ${styleModule} to the custom style!`);\n","/**\n * @ignore\n */\nexport const isDOMImageSupported = (): boolean =>\n typeof document != 'undefined' && typeof DOMParser != 'undefined' && typeof btoa != 'undefined';\n\n/**\n * @ignore\n */\nexport const svgToImg = (svgDomElement: SVGElement): HTMLImageElement => {\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const img = document.createElement('img');\n img.src = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgDomElement))}`;\n return img;\n};\n","import { SVGIconStyleOptions } from '../types';\nimport pinSvgRaw from './pin.svg?raw';\n\n/**\n * Helper function to parse SVG string (typically imported from ...svg?raw) to SVGElement\n * @ignore\n */\nexport const parseSvg = (svgString: string): SVGElement =>\n new DOMParser().parseFromString(svgString, 'image/svg+xml').documentElement as unknown as SVGElement;\n\n/**\n * @ignore\n */\nexport const pinSvg = (options: SVGIconStyleOptions | undefined): SVGElement => {\n const element = parseSvg(pinSvgRaw);\n // see pin.svg structure\n if (options?.fillColor) {\n element.querySelector('#background')?.setAttribute('fill', options.fillColor);\n }\n if (options?.outlineColor) {\n element.querySelector('#outline')?.setAttribute('fill', options.outlineColor);\n }\n if (options?.outlineOpacity !== undefined) {\n element.querySelector('#outline')?.setAttribute('fill-opacity', options.outlineOpacity.toString());\n }\n return element;\n};\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path id=\\\"background\\\" d=\\\"M63.95 137.516c6.556-12.737 19.098-21.09 30.828-29.388C110.046 97.324 120 79.603 120 59.574 120 26.671 93.138 0 60 0S0 26.671 0 59.574c0 20.029 9.954 37.75 25.22 48.55 11.734 8.302 24.274 16.652 30.83 29.388 1.706 3.316 6.194 3.316 7.9 0z\\\"\\n fill=\\\"#1988cf\\\"/>\\n <path id=\\\"outline\\\" d=\\\"M91.298 103.272C105.052 93.544 114 77.596 114 59.574c0-29.61-24.176-53.617-54-53.617S6 29.964 6 59.574c0 18.022 8.95 33.97 22.7 43.698l2.27 1.601c10.172 7.153 21.922 15.423 29.03 27.416 7.104-11.993 18.858-20.259 29.03-27.416l2.268-1.601zm1.468 6.274c-11.13 7.845-22.632 15.957-28.816 27.966-1.706 3.316-6.194 3.316-7.9 0-6.182-12.009-17.686-20.121-28.816-27.966l-2.016-1.422C9.954 97.324 0 79.603 0 59.574 0 26.671 26.862 0 60 0s60 26.671 60 59.574c0 20.029-9.954 37.75-25.222 48.55l-2.016 1.422z\\\"\\n fill-opacity=\\\".3\\\"/>\\n</svg>\\n\"","import { isNil } from 'lodash-es';\n\n/**\n * Throws an error if the given value is not defined.\n * @ignore\n */\nexport function assertDefined<T>(value: T | null | undefined): asserts value is T {\n if (isNil(value)) {\n throw new Error(`${value} must not be null/undefined. Something went wrong with its initialization.`);\n }\n}\n\n/**\n * Returns the given value as its defined type, or throws an error if it isn't defined.\n * @ignore\n * @throws\n */\nexport const asDefined = <T>(value: T | null | undefined): T => {\n assertDefined(value);\n return value;\n};\n","import type { Map, Source, SourceSpecification } from 'maplibre-gl';\n\n/**\n * Contains a source relevant for TomTom Maps SDK JS.\n * * The source might already be initialized from the map style, or it might be initialized here.\n * @ignore\n */\nexport class TomTomMapSource<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> {\n constructor(\n readonly id: string,\n readonly spec?: SOURCE_SPEC,\n public runtimeSource?: RUNTIME_SOURCE,\n ) {}\n\n ensureAddedToMap(map: Map) {\n if (!this.runtimeSource) {\n if (!map.getSource(this.id) && this.spec) {\n map.addSource(this.id, this.spec);\n }\n this.runtimeSource = map.getSource(this.id) as RUNTIME_SOURCE;\n }\n }\n}\n","import type { FeatureCollection } from 'geojson';\nimport type {\n GeoJSONSource,\n GeoJSONSourceSpecification,\n LayerSpecification,\n Map,\n Source,\n SourceSpecification,\n} from 'maplibre-gl';\nimport { asDefined } from './assertionUtils';\nimport { TomTomMapSource } from './TomTomMapSource';\nimport type {\n ByIdOrIndex,\n CleanEventStateOptions,\n CleanEventStatesOptions,\n LayerSpecFilter,\n LayerSpecWithSource,\n PutEventStateOptions,\n SourceWithLayerIDs,\n SourceWithLayers,\n ToBeAddedLayerSpec,\n ToBeAddedLayerSpecWithoutSource,\n} from './types';\n\n/**\n * Contains a source and the layers to render its data.\n * @ignore\n */\nexport abstract class AbstractSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n LAYER_SPEC extends LayerSpecification = LayerSpecification,\n> {\n readonly _layerSpecs: LAYER_SPEC[];\n private _sourceAndLayerIDs!: SourceWithLayerIDs;\n\n constructor(\n readonly map: Map,\n readonly source: TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>,\n layerSpecs: LAYER_SPEC[],\n ) {\n this._layerSpecs = layerSpecs;\n this._updateSourceAndLayerIDs();\n }\n\n isAnyLayerVisible(filter?: LayerSpecFilter): boolean {\n return this.getLayerSpecs(filter).some((layer) => this.isLayerVisible(layer));\n }\n\n areAllLayersVisible(filter?: LayerSpecFilter): boolean {\n return this.getLayerSpecs(filter).every((layer) => this.isLayerVisible(layer));\n }\n\n private getLayerSpecs(filter?: LayerSpecFilter) {\n return filter ? this._layerSpecs.filter(filter) : this._layerSpecs;\n }\n\n _updateSourceAndLayerIDs(): void {\n this._sourceAndLayerIDs = { sourceID: this.source.id, layerIDs: this._layerSpecs.map((layer) => layer.id) };\n }\n\n private isLayerVisible(layer: LayerSpecification): boolean {\n return this.map.getLayoutProperty(layer.id, 'visibility') !== 'none';\n }\n\n setLayersVisible(visible: boolean, filter?: LayerSpecFilter): void {\n for (const layerSpec of this.getLayerSpecs(filter)) {\n this.map.setLayoutProperty(layerSpec.id, 'visibility', visible ? 'visible' : 'none', { validate: false });\n }\n }\n\n get sourceAndLayerIDs(): SourceWithLayerIDs {\n return this._sourceAndLayerIDs;\n }\n\n equalSourceAndLayerIDs(other: SourceWithLayers): boolean {\n return (\n this.sourceAndLayerIDs.sourceID === other.sourceAndLayerIDs.sourceID &&\n this.sourceAndLayerIDs.layerIDs.length === other.sourceAndLayerIDs.layerIDs.length &&\n this.sourceAndLayerIDs.layerIDs.every((id, index) => id === other.sourceAndLayerIDs.layerIDs[index])\n );\n }\n}\n\n/**\n * @ignore\n * @param loadedMap\n * @param sourceIDs\n */\nexport const filterLayersBySources = (loadedMap: Map, sourceIDs: string[]): LayerSpecWithSource[] =>\n loadedMap\n .getStyle()\n .layers.filter((layer) => sourceIDs.includes((layer as LayerSpecWithSource)?.source)) as LayerSpecWithSource[];\n\n/**\n * Source with layers which are coming from the downloaded TT map style.\n * * Examples are parts of the base map, traffic, pois, and hillshade.\n * @ignore\n */\nexport class StyleSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> extends AbstractSourceWithLayers<SourceSpecification, RUNTIME_SOURCE> {\n constructor(map: Map, runtimeSource: RUNTIME_SOURCE, filter?: LayerSpecFilter) {\n let layers = filterLayersBySources(map, [runtimeSource.id]);\n if (filter) {\n layers = layers.filter(filter);\n }\n super(\n map,\n new TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>(\n runtimeSource.id,\n map.getStyle().sources[runtimeSource.id] as SOURCE_SPEC,\n runtimeSource,\n ),\n layers,\n );\n }\n}\n\n/**\n * Source with layers which originally is not in the map style, but it's to be added after the map is initialized.\n * @ignore\n */\nexport class AddedSourceWithLayers<\n SOURCE_SPEC extends SourceSpecification = SourceSpecification,\n RUNTIME_SOURCE extends Source = Source,\n> extends AbstractSourceWithLayers<SOURCE_SPEC, RUNTIME_SOURCE, ToBeAddedLayerSpec> {\n constructor(map: Map, sourceId: string, sourceSpec: SOURCE_SPEC, layerSpecs: ToBeAddedLayerSpecWithoutSource[]) {\n super(\n map,\n new TomTomMapSource<SOURCE_SPEC, RUNTIME_SOURCE>(sourceId, sourceSpec),\n // We ensure the source ID is assigned to the layers:\n layerSpecs.map((layerSpec) => ({ ...layerSpec, source: sourceId }) as ToBeAddedLayerSpec),\n );\n }\n\n private ensureLayersAddedToMap(): void {\n for (const layerSpec of this._layerSpecs) {\n if (!this.map.getLayer(layerSpec.id)) {\n this.map.addLayer(layerSpec, layerSpec.beforeID);\n }\n }\n }\n\n ensureAddedToMapWithVisibility(visible: boolean, addLayersToMap: boolean): void {\n this.source.ensureAddedToMap(this.map);\n if (addLayersToMap) {\n this.ensureLayersAddedToMap();\n this.setLayersVisible(visible);\n }\n }\n}\n\nconst emptyFeatureCollection: FeatureCollection = { type: 'FeatureCollection', features: [] };\n\n/**\n * @ignore\n */\nexport class GeoJSONSourceWithLayers<T extends FeatureCollection = FeatureCollection> extends AddedSourceWithLayers<\n GeoJSONSourceSpecification,\n GeoJSONSource\n> {\n shownFeatures: T = emptyFeatureCollection as T;\n\n constructor(map: Map, sourceId: string, layerSpecs: ToBeAddedLayerSpecWithoutSource[], addLayersToMap = true) {\n // MapLibre does not reuse the given feature ID. Either we generate it on the fly or use the one from properties via promotedId value.\n // We must generate \"id\" property based on the feature id on the fly on \"prepareForDisplay\" functions.\n super(map, sourceId, { type: 'geojson', data: emptyFeatureCollection, promoteId: 'id' }, layerSpecs);\n this.ensureAddedToMapWithVisibility(false, addLayersToMap);\n }\n\n show(featureCollection: T): void {\n this.shownFeatures = featureCollection;\n asDefined(this.source.runtimeSource).setData(featureCollection);\n this.setLayersVisible(!!featureCollection.features.length);\n }\n\n clear(): void {\n this.show(emptyFeatureCollection as T);\n }\n\n private findFeature(options: ByIdOrIndex) {\n if ('index' in options) {\n return this.shownFeatures.features[options.index];\n } else if ('id' in options) {\n return this.shownFeatures.features.find((f) => f.id === options.id);\n }\n return undefined;\n }\n\n putEventState(options: PutEventStateOptions) {\n const mode = options.mode ?? 'put';\n if (mode === 'put') {\n for (const feature of this.shownFeatures.features) {\n if (feature.properties?.eventState === options.state) {\n delete feature.properties.eventState;\n }\n }\n }\n\n const feature = this.findFeature(options);\n if (feature) {\n feature.properties = { ...feature.properties, eventState: options.state };\n }\n\n if (options.show !== false) {\n this.show(this.shownFeatures);\n }\n }\n\n cleanEventState(options: CleanEventStateOptions) {\n const feature = this.findFeature(options);\n\n if (feature?.properties?.eventState) {\n delete feature?.properties?.eventState;\n if (options?.show !== false) {\n this.show(this.shownFeatures);\n }\n }\n }\n\n cleanEventStates(options?: CleanEventStatesOptions) {\n let changed = false;\n for (const feature of this.shownFeatures.features) {\n if (!options?.states || options.states.includes(feature.properties?.eventState)) {\n delete feature.properties?.eventState;\n changed = true;\n }\n }\n\n if (options?.show !== false && changed) {\n this.show(this.shownFeatures);\n }\n }\n}\n","import type { GlobalConfig } from '@tomtom-org/maps-sdk/core';\nimport { generateTomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type {\n FilterSpecification,\n Map,\n MapGeoJSONFeature,\n RequestParameters,\n ResourceType,\n StyleImageMetadata,\n} from 'maplibre-gl';\nimport { InternalTomTomMapParams, StandardStyle, StandardStyleID, StyleInput, StyleModule } from '../init';\nimport type { TomTomMap } from '../TomTomMap';\nimport { FLOW_TAGS } from '../traffic/util/trafficFlowMapping';\nimport { INCIDENT_TAGS } from '../traffic/util/trafficIncidentMapping';\nimport { cannotAddStyleModuleToCustomStyle } from './errorMessages';\nimport { svgToImg } from './imageUtils';\nimport { parseSvg } from './resources';\nimport { AbstractSourceWithLayers, filterLayersBySources } from './SourceWithLayers';\nimport type { LightDark, ToBeAddedLayerSpec, ToBeAddedLayerSpecWithoutSource } from './types';\n\n/**\n * Wait until the map is ready.\n * @param tomtomMap The TomTomMap instance.\n * @returns {Promise<boolean>} Returns a Promise<boolean>\n */\nexport const waitUntilMapIsReady = async (tomtomMap: TomTomMap): Promise<void> => {\n if (!tomtomMap.mapReady) {\n await tomtomMap.mapLibreMap.once('styledata');\n // Recursively waiting for map to be ready (in case of style changes quickly in succession):\n await waitUntilMapIsReady(tomtomMap);\n }\n};\n\n/**\n * Wait until the source is ready.\n * @param tomtomMap The TomTomMap instance.\n * @param sourceId we want to check for.\n * @returns {Promise<boolean>} Returns a Promise<boolean>\n */\nexport const waitUntilSourceIsLoaded = async (tomtomMap: TomTomMap, sourceId: string): Promise<void> => {\n if (!tomtomMap.mapLibreMap.getSource(sourceId) || !tomtomMap.mapLibreMap.isSourceLoaded(sourceId)) {\n await tomtomMap.mapLibreMap.once('sourcedata');\n }\n};\n\n/**\n * Deserializes the properties from MapLibre features.\n * * Maplibre has a bug where all properties from a feature are stringified.\n * See: {@link} https://github.com/maplibre/maplibre-gl-js/issues/1325\n *\n * @param features An Array with MapGeoJSONFeatures objects\n *\n * @ignore\n */\nexport const deserializeFeatures = (features: MapGeoJSONFeature[]): void => {\n for (const feature of features) {\n if (!feature || !Object.keys(feature.properties).length) {\n continue;\n }\n\n for (const key in feature.properties) {\n if (typeof feature.properties[key] === 'string') {\n try {\n feature.properties[key] = JSON.parse(feature.properties[key]);\n } catch (_e) {\n // We ignore the error if the object can't be parsed and continue.\n // console.debug('Cannot deserialize feature property', key, 'with value', feature.properties[key], e);\n }\n }\n }\n }\n};\n\n/**\n * Inject TomTom custom headers to requests to TomTom.\n *\n * @ignore\n * @param params Global SDK Map configuration\n */\nexport const transformRequest =\n (params: Partial<GlobalConfig>) =>\n (url: string, resourceType?: ResourceType): RequestParameters => {\n if (url.includes('tomtom.com')) {\n if (resourceType === 'Image') {\n return { url };\n }\n const parsedUrl = new URL(url);\n if (parsedUrl.pathname.includes('incidents')) {\n parsedUrl.searchParams.set('tags', `${INCIDENT_TAGS.join(',')}`);\n } else if (parsedUrl.pathname.includes('flow')) {\n parsedUrl.searchParams.set('tags', `${FLOW_TAGS.join(',')}`);\n }\n return { url: parsedUrl.toString(), headers: { ...generateTomTomHeaders(params) } };\n }\n return { url };\n };\n\n/**\n * Compares two MapLibre features by ID.\n * @ignore\n */\nexport const areBothDefinedAndEqual = (\n featureA: MapGeoJSONFeature | undefined,\n featureB: MapGeoJSONFeature | undefined,\n): boolean => !!featureA && !!featureB && featureA.id === featureB.id;\n\ntype LayerProps = {\n id: string;\n layout?: any;\n paint?: any;\n minzoom?: number;\n maxzoom?: number;\n filter?: FilterSpecification;\n};\n\n/**\n * Applies the layout and paint properties from newLayoutPaint\n * while unsetting (setting as undefined) the ones from previousSpec which no longer exist in newLayoutPaint.\n * * This allows for a quick change of a layer visuals without removing-re-adding the layer.\n * @ignore\n * @param newLayerProps The new layer from which to apply layout/pain props.\n * @param prevLayerProps The previous layer to ensure layout/paint props are removed.\n * @param map\n */\nexport const changeLayerProps = (newLayerProps: LayerProps, prevLayerProps: LayerProps, map: Map) => {\n const layerId = newLayerProps.id;\n if (newLayerProps.maxzoom !== prevLayerProps.maxzoom || newLayerProps.minzoom !== prevLayerProps.minzoom) {\n map.setLayerZoomRange(\n layerId,\n newLayerProps.minzoom ?? map.getMinZoom(),\n newLayerProps.maxzoom ?? map.getMaxZoom(),\n );\n }\n map.setFilter(layerId, newLayerProps.filter, { validate: false });\n for (const property of Object.keys(prevLayerProps.layout ?? [])) {\n if (!newLayerProps.layout?.[property]) {\n map.setLayoutProperty(layerId, property, undefined, { validate: false });\n }\n }\n for (const property of Object.keys(prevLayerProps.paint ?? [])) {\n if (!newLayerProps.paint?.[property]) {\n map.setPaintProperty(layerId, property, undefined, { validate: false });\n }\n }\n for (const [property, value] of Object.entries(newLayerProps.paint ?? [])) {\n map.setPaintProperty(layerId, property, value, { validate: false });\n }\n\n for (const [property, value] of Object.entries(newLayerProps.layout ?? [])) {\n map.setLayoutProperty(layerId, property, value, { validate: false });\n }\n};\n\n/**\n * Applies the layer properties from each layer of newLayoutPaints\n * while unsetting (setting as undefined) the ones from the corresponding layer from prevLayoutPaints\n * which no longer exist in the new one.\n * * The two layer inputs are expected to be parallel arrays.\n * * This allows for quick changes of layer visuals without removing-re-adding the layers.\n * @ignore\n */\nexport const changeLayersProps = (newLayerProps: LayerProps[], prevLayerProps: LayerProps[], map: Map) => {\n newLayerProps.forEach((layoutPaint, index) => changeLayerProps(layoutPaint, prevLayerProps[index], map));\n};\n\n/**\n * Handles new layer specs for the provided source. It will remove layers no longer present,\n * update existing layers and add new one if needed to the source.\n * Adding layers to the map needs to be done correctly, so after calling this method, you should call addLayersInCorrectOrder.\n * If ID of layer to be added already is present on map, MapLibre will through exception.\n * @param newLayersSpecs new layer specification for provided source.\n * @param oldLayersSpecs current layer specification for provided source.\n * @param sourceWithLayers provided source that contains layers.\n * @param map provided map libre map object.\n * @ignore\n */\nexport const updateLayersAndSource = (\n newLayersSpecs: ToBeAddedLayerSpecWithoutSource[],\n oldLayersSpecs: ToBeAddedLayerSpecWithoutSource[],\n sourceWithLayers: AbstractSourceWithLayers,\n map: Map,\n): void => {\n // map layers by id in object for easier access, reduces number of loops\n const newLayersMap: Record<string, ToBeAddedLayerSpecWithoutSource> = newLayersSpecs.reduce(\n (acc, cur) => ({ ...acc, [cur.id]: cur }),\n {},\n );\n const oldLayersMap: Record<string, ToBeAddedLayerSpecWithoutSource> = oldLayersSpecs.reduce(\n (acc, cur) => ({ ...acc, [cur.id]: cur }),\n {},\n );\n\n // we need to store layers in four arrays, layers to add ID, layers to remove ID and layers to update\n const layersToAdd: string[] = [];\n const layersToRemove: string[] = [];\n const newLayersToUpdate: ToBeAddedLayerSpecWithoutSource[] = [];\n const oldLayersToUpdate: ToBeAddedLayerSpecWithoutSource[] = [];\n Object.keys(newLayersMap).forEach((key) => {\n if (oldLayersMap[key]) {\n newLayersToUpdate.push(newLayersMap[key]);\n oldLayersToUpdate.push(oldLayersMap[key]);\n } else {\n layersToAdd.push(key);\n }\n });\n Object.keys(oldLayersMap).forEach((key) => {\n if (!newLayersMap[key]) {\n layersToRemove.push(key);\n }\n });\n\n // remove the old layers no longer present in new layer specification\n const layerSpecs: ToBeAddedLayerSpec[] = sourceWithLayers._layerSpecs;\n layersToRemove.forEach((layerId) => {\n map.removeLayer(layerId);\n for (let i = 0; i < layerSpecs.length; i++) {\n if (layerSpecs[i].id === layerId) {\n layerSpecs.splice(i, 1);\n break;\n }\n }\n });\n // add new layers\n layersToAdd.forEach((layerId) => {\n // add layer spec and map\n const toBeAddedLayerSpec: ToBeAddedLayerSpec = {\n ...newLayersMap[layerId],\n source: sourceWithLayers.source.id,\n } as ToBeAddedLayerSpec;\n layerSpecs.push(toBeAddedLayerSpec);\n });\n sourceWithLayers._updateSourceAndLayerIDs();\n // update existing layers\n changeLayersProps(newLayersToUpdate, oldLayersToUpdate, map);\n};\n\n/**\n * Adds the given layers to the map ensuring they respect their \"beforeID\" properties.\n * * We need to make sure that layers are added in the correct Z order because one layer may depend on another layer.\n * @param layersToAdd\n * @param map MapLibre map\n * @ignore\n */\nexport const addLayers = (layersToAdd: ToBeAddedLayerSpec[], map: Map): void => {\n const layerIdsAlreadyOnMap = new Set<string>();\n const addLayer = (layer: ToBeAddedLayerSpec): void => {\n // we can safely add this layer\n if (!map.getLayer(layer.id)) {\n map.addLayer({ ...layer, layout: { ...layer.layout, visibility: 'none' } }, layer.beforeID);\n }\n layerIdsAlreadyOnMap.add(layer.id);\n };\n\n const mapIdDependency: Record<string, ToBeAddedLayerSpec[]> = {};\n\n layersToAdd.forEach((layer) => {\n if (layer.beforeID) {\n if (layerIdsAlreadyOnMap.has(layer.beforeID) || map.getLayer(layer.beforeID)) {\n layerIdsAlreadyOnMap.add(layer.beforeID);\n addLayer(layer);\n } else if (mapIdDependency[layer.beforeID]) {\n // we cannot add this layer yet\n mapIdDependency[layer.beforeID].push(layer);\n } else {\n mapIdDependency[layer.beforeID] = [layer];\n }\n } else {\n addLayer(layer);\n }\n });\n\n // try to process the rest of layers\n while (Object.keys(mapIdDependency).length > 0) {\n const idsWeCanProcess = Object.keys(mapIdDependency).filter((id) => layerIdsAlreadyOnMap.has(id));\n if (!idsWeCanProcess.length) {\n console.error(\n `Some layers cannot be added. Check for non-existing layers, or circular beforeID dependencies for the following: ${JSON.stringify(Object.keys(mapIdDependency))}`,\n );\n return;\n }\n idsWeCanProcess.forEach((id) => {\n mapIdDependency[id].forEach((layer) => addLayer(layer));\n delete mapIdDependency[id];\n });\n }\n};\n\n/**\n * Adding a style-based module to the given style input, if possible.\n * * This results in a style input which will include such style-based module (e.g. include traffic layers).\n * @param style which we want to update.\n * @param styleModule module we want to add.\n * @ignore\n */\nexport const updateStyleWithModule = (style: StyleInput | undefined, styleModule: StyleModule): StyleInput => {\n switch (typeof style) {\n case 'undefined':\n return { type: 'standard', include: [styleModule] };\n case 'string':\n // this is a standard style\n return { type: 'standard', id: style, include: [styleModule] };\n default:\n if (style.type === 'standard') {\n if (style.include) {\n return { ...style, include: [...style.include, styleModule] };\n }\n return { ...style, include: [styleModule] };\n }\n throw cannotAddStyleModuleToCustomStyle(styleModule);\n }\n};\n\n/**\n * Checks if the source is missing and try to add it to the map by reloading its style.\n * @param map the TomTom map instance.\n * @param sourceId id of the source.\n * @param styleModule style module of the source.\n * @ignore\n */\nexport const ensureAddedToStyle = async (map: TomTomMap, sourceId: string, styleModule: StyleModule): Promise<void> => {\n if (!map.mapLibreMap.getSource(sourceId)) {\n const mapLibreMap = map.mapLibreMap;\n if (!mapLibreMap.isStyleLoaded()) {\n // we let the map settle before changing its style again, so the previous style/data load goes smoother:\n await mapLibreMap.once('idle');\n }\n map.setStyle(updateStyleWithModule(map.getStyle(), styleModule));\n await waitUntilSourceIsLoaded(map, sourceId);\n\n await mapLibreMap.once('styledata');\n // we're loading a bunch of style layers to the map, and we hide them all by default:\n // see TomTomMap.handleStyleData for similar logic\n for (const layer of filterLayersBySources(mapLibreMap, [sourceId])) {\n mapLibreMap.setLayoutProperty(layer.id, 'visibility', 'none', { validate: false });\n }\n // Since we just changed the style visibility, we ensure to wait until the style data is changed before returning to prevent race conditions:\n await mapLibreMap.once('styledata');\n }\n};\n\n/**\n * Sets the given image on the map (loading it if necessary), either adding or updating it.\n * @ignore\n */\nexport const addOrUpdateImage = async (\n mode: 'if-not-in-sprite' | 'add-or-update',\n imageId: string,\n imageToLoad: string | HTMLImageElement,\n map: Map,\n options?: Partial<StyleImageMetadata>,\n) => {\n // defensive check (should not happen but we cannot let it crash):\n if (!imageToLoad) {\n console.warn(`addOrUpdateImage called with empty image for ID ${imageId}`);\n return;\n }\n\n // Helper function to add or update the image\n const addOrUpdateToMap = (imgElement: HTMLImageElement | ImageData | ImageBitmap) => {\n const imageExists = map.hasImage(imageId);\n if (imageExists && mode == 'add-or-update') {\n map.updateImage(imageId, imgElement);\n } else if (!imageExists) {\n map.addImage(imageId, imgElement, options);\n }\n };\n\n const ensureImageLoaded = (imgElement: HTMLImageElement) => {\n // An image is successfully loaded if it's complete AND has valid dimensions\n // (naturalWidth > 0 ensures the image didn't fail to load)\n if (imgElement.complete) {\n if (imgElement.naturalWidth > 0) {\n addOrUpdateToMap(imgElement);\n } else {\n // Image is complete but failed to load\n console.warn(`Failed to load image for ID ${imageId}`);\n }\n } else {\n imgElement.onload = () => addOrUpdateToMap(imgElement);\n imgElement.onerror = () => console.warn(`Failed to load image for ID ${imageId}`);\n }\n };\n\n if (typeof imageToLoad === 'string') {\n if (imageToLoad.includes('<svg')) {\n // Supporting raw SVGs:\n const imgElement = svgToImg(parseSvg(imageToLoad));\n ensureImageLoaded(imgElement);\n } else {\n // Expecting image URL, so the image needs to be downloaded first:\n addOrUpdateToMap((await map.loadImage(imageToLoad)).data);\n }\n } else {\n // Expecting HTMLImageElement, wait for it to be loaded\n ensureImageLoaded(imageToLoad);\n }\n};\n\n/**\n * Returns the light/dark theme for a known standard style.\n * @param standardStyleID\n */\nconst getStandardStyleTheme = (standardStyleID: StandardStyleID): LightDark => {\n switch (standardStyleID) {\n case 'standardDark':\n case 'drivingDark':\n case 'monoDark':\n case 'satellite':\n return 'dark';\n default:\n return 'light';\n }\n};\n\n/**\n * Returns the light/dark theme for a given style input.\n * * Unknown standard styles and custom styles are considered as 'light' theme.\n * @param styleInput The style input to check. If not provided, 'light' is returned.\n * @ignore\n */\nexport const getStyleLightDarkTheme = (styleInput?: StyleInput): LightDark => {\n if (typeof styleInput === 'string') {\n return getStandardStyleTheme(styleInput);\n }\n const standardStyle = styleInput as StandardStyle;\n if (standardStyle?.id) {\n return getStandardStyleTheme(standardStyle.id);\n }\n return 'light';\n};\n\n/**\n * Adds the large POI sprite to the map style.\n * @ignore\n */\nexport const addPinCategoriesSpriteToStyle = async (mapParams: InternalTomTomMapParams, mapLibreMap: Map) => {\n mapLibreMap.setSprite(\n `${mapParams.commonBaseURL}/maps/orbis/assets/sprites/2.*/sprite?key=${mapParams.apiKey}&poi=poi_${getStyleLightDarkTheme(mapParams.style)}&apiVersion=1&apiChannel=preview`,\n { validate: false },\n );\n};\n","import type { Map } from 'maplibre-gl';\nimport type { TomTomMap } from '../TomTomMap';\nimport type { EventsProxy } from './EventsProxy';\nimport { waitUntilMapIsReady } from './mapUtils';\nimport type { MapModuleCommonConfig, SourcesWithLayers, SourceWithLayerIDs } from './types';\n\n/**\n * Whether a map module is based on a map style or on added GeoJSON data.\n */\ntype MapModuleSource = 'style' | 'geojson';\n\n/**\n * Base class for all Maps SDK map modules.\n *\n * This abstract class provides the foundation for creating map modules that can display\n * and manage various types of data on a TomTom map. It handles module lifecycle management,\n * including initialization, configuration updates, and automatic restoration after map style changes.\n *\n * @remarks\n * All map modules extend this class to ensure consistent behavior across the SDK.\n * The class manages the module's sources, layers, and configuration, automatically\n * handling map style changes by restoring the module's state when needed.\n *\n * @typeParam SOURCES_WITH_LAYERS - The type defining the sources and layers used by this module\n * @typeParam CFG - The configuration type for this module, or undefined if no configuration is needed. When defined, must extend MapModuleCommonConfig.\n *\n * @example\n * ```typescript\n * class CustomModule extends AbstractMapModule<MySourcesWithLayers, MyConfig> {\n * constructor(tomtomMap: TomTomMap, config?: MyConfig) {\n * super('geojson', tomtomMap, config);\n * }\n * // Implement abstract methods...\n * }\n * ```\n *\n * @group Shared\n */\nexport abstract class AbstractMapModule<\n SOURCES_WITH_LAYERS extends SourcesWithLayers,\n CFG extends MapModuleCommonConfig | undefined = undefined,\n> {\n private readonly sourceType: MapModuleSource;\n /**\n * @ignore\n */\n protected readonly tomtomMap: TomTomMap;\n /**\n * @ignore\n */\n protected readonly eventsProxy: EventsProxy;\n /**\n * @ignore\n */\n protected readonly mapLibreMap: Map;\n /**\n * @ignore\n */\n protected sourcesWithLayers!: SOURCES_WITH_LAYERS;\n /**\n * @ignore\n */\n protected _sourceAndLayerIDs!: Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs>;\n /**\n * @ignore\n */\n protected config?: CFG;\n /**\n * @ignore\n */\n protected _initializing = true;\n\n /**\n * Indicates that this module is currently adding its sources and layers to the map, so during this time it might not function properly.\n * @see waitUntilModuleReady\n * @private\n */\n private moduleReady = false;\n\n /**\n * Builds this module based on a given Maps SDK map.\n * @param tomtomMap The map. It may or may not be initialized at this stage,\n * but the module ensures to initialize itself once it is.\n * @param sourceType Whether the module is based on a map style or on added GeoJSON data.\n * @param config Optional configuration to initialize directly as soon as the map is ready.\n */\n protected constructor(sourceType: MapModuleSource, tomtomMap: TomTomMap, config?: CFG) {\n this.sourceType = sourceType;\n this.tomtomMap = tomtomMap;\n this.eventsProxy = tomtomMap._eventsProxy;\n this.mapLibreMap = tomtomMap.mapLibreMap;\n // TODO: we need to find a cleaner separation between initSourcesWithLayers and applyConfig for most modules to prevent double work, particularly with adding layers\n this.initSourcesWithLayers(config);\n this.applyConfig(config);\n this.tomtomMap.addStyleChangeHandler({\n onStyleAboutToChange: () => {\n this.moduleReady = false;\n },\n onStyleChanged: () => this.restoreDataAndConfig(),\n });\n this._initializing = false;\n }\n\n /**\n * Initializes the sources with layers of this module.\n * @param config The optional configuration for the module.\n * @param restore Whether we are restoring an existing module after the map style got reloaded.\n * @protected\n * @ignore\n */\n protected initSourcesWithLayers(config?: CFG, restore?: boolean): void {\n this.moduleReady = false;\n this.sourcesWithLayers = this._initSourcesWithLayers(config, restore);\n this._sourceAndLayerIDs = Object.fromEntries(\n Object.entries(this.sourcesWithLayers).map(([name, sourceWithLayers]) => [\n name,\n sourceWithLayers.sourceAndLayerIDs,\n ]),\n ) as Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs>;\n if (restore) {\n this.eventsProxy.updateIfRegistered(this.sourcesWithLayers);\n }\n // Only if the map is still ready, we consider that the module is ready.\n // Otherwise, we assume there's a quick style change in progress and expect that'll trigger the module to restore itself again.\n if (this.tomtomMap.mapReady) {\n this.moduleReady = true;\n }\n }\n\n /**\n * Initializes the sources with layers for the specific module.\n * @protected\n * @ignore\n */\n protected abstract _initSourcesWithLayers(config?: CFG, restore?: boolean): SOURCES_WITH_LAYERS;\n\n protected async waitUntilModuleReady(): Promise<void> {\n await waitUntilMapIsReady(this.tomtomMap);\n if (!this.moduleReady) {\n await new Promise<void>((resolve) => {\n const interval = setInterval(() => {\n if (this.tomtomMap.mapReady && this.moduleReady) {\n clearInterval(interval);\n resolve();\n }\n }, 200);\n });\n }\n }\n\n /**\n * Applies a configuration to this module.\n *\n * This method updates the module's behavior and appearance based on the provided configuration.\n * The configuration is stored internally and will be automatically reapplied if the map style changes.\n *\n * @param config - The configuration object to apply to the module. Pass `undefined` to reset\n * the configuration to default values.\n *\n * @remarks\n * When a configuration is applied, the module updates its visual representation and behavior\n * accordingly. The configuration persists across map style changes, ensuring consistent\n * module behavior even when the map's base style is modified.\n *\n * @example\n * ```typescript\n * // Apply a new configuration\n * myModule.applyConfig({ visible: true, opacity: 0.8 });\n *\n * // Reset to default configuration\n * myModule.applyConfig(undefined);\n * ```\n *\n * @see {@link resetConfig} for a convenience method to reset configuration\n * @see {@link getConfig} to retrieve the current configuration\n */\n applyConfig(config: CFG | undefined) {\n this.config = this._applyConfig(config);\n }\n\n /**\n * Internal implementation to apply config for the specific module.\n * @param config The config to apply. this.config contains the previous configuration (if any).\n * Once the method returns config, it will be assigned to this.config.\n * @protected\n * @ignore\n */\n protected abstract _applyConfig(config: CFG | undefined): CFG | undefined;\n\n /**\n * Resets the configuration of this module to its default values.\n *\n * This is a convenience method that clears any previously applied configuration\n * and restores the module to its initial state. This is equivalent to calling\n * `applyConfig(undefined)`.\n *\n * @remarks\n * After calling this method, the module will behave as if no configuration was ever applied.\n * Any custom settings, styling, or behavior modifications will be removed and replaced\n * with default values.\n *\n * @example\n * ```typescript\n * // Apply some configuration\n * myModule.applyConfig({ visible: true, opacity: 0.5 });\n *\n * // Later, reset to defaults\n * myModule.resetConfig();\n * ```\n *\n * @see {@link applyConfig} to apply a new configuration\n * @see {@link getConfig} to retrieve the current configuration before resetting\n */\n resetConfig(): void {\n this.applyConfig(undefined);\n }\n\n private async restoreDataAndConfig() {\n // defensively declaring the module as not ready to prevent race conditions:\n this.moduleReady = false;\n if (this.sourceType === 'geojson') {\n // Defer GeoJSON layer restoration by one animation frame to work around a MapLibre\n // glitch where symbol layers can't be added right after a styledata event.\n requestAnimationFrame(() => {\n this.restoreDataAndConfigImpl();\n });\n } else {\n this.restoreDataAndConfigImpl();\n }\n }\n\n /**\n * implementation needed to restore the module state (data and config applied to the module).\n * to be used to restore module state after map style change\n * @protected\n * @ignore\n */\n protected restoreDataAndConfigImpl(): void {\n this.initSourcesWithLayers(this.config, true);\n this._applyConfig(this.config);\n }\n\n /**\n * Retrieves a copy of the current module configuration.\n *\n * This method returns a shallow copy of the configuration object that is currently\n * applied to the module. If no configuration has been applied, it returns `undefined`.\n *\n * @returns A shallow copy of the current configuration object, or `undefined` if no\n * configuration is currently applied. The returned object is a copy to prevent\n * unintended modifications to the internal state.\n *\n * @remarks\n * The returned configuration object is a shallow copy, which means that while the\n * top-level properties are copied, any nested objects or arrays are still referenced\n * from the original configuration. This is sufficient for most use cases but should\n * be kept in mind when dealing with complex configurations.\n *\n * @example\n * ```typescript\n * // Apply a configuration\n * myModule.applyConfig({ visible: true, opacity: 0.8 });\n *\n * // Later, retrieve the current configuration\n * const currentConfig = myModule.getConfig();\n * console.log(currentConfig); // { visible: true, opacity: 0.8 }\n *\n * // When no config is applied\n * myModule.resetConfig();\n * console.log(myModule.getConfig()); // undefined\n * ```\n *\n * @see {@link applyConfig} to modify the configuration\n * @see {@link resetConfig} to clear the configuration\n */\n getConfig() {\n return this.config && { ...this.config };\n }\n\n /**\n * Gets the source and layer identifiers for all sources managed by this module.\n *\n * This property provides access to the MapLibre source and layer IDs that were created\n * and are managed by this module. These IDs can be used to interact directly with\n * MapLibre's API or to identify which layers belong to this module.\n *\n * @returns A record mapping each source name to its corresponding source ID and layer IDs.\n * Each entry contains the MapLibre source identifier and an array of layer identifiers\n * associated with that source.\n *\n * @remarks\n * The returned IDs are useful when you need to:\n * - Directly manipulate layers using MapLibre's native API\n * - Identify which layers on the map belong to this module\n * - Set layer ordering or positioning relative to other layers\n * - Access source or layer properties through MapLibre methods\n *\n * @example\n * ```typescript\n * const ids = myModule.sourceAndLayerIDs;\n * console.log(ids);\n * // {\n * // mySource: {\n * // sourceID: 'my-source-id',\n * // layerIDs: ['layer-1', 'layer-2']\n * // }\n * // }\n *\n * // Use with MapLibre API\n * const map = myModule.mapLibreMap;\n * ids.mySource.layerIDs.forEach(layerId => {\n * map.setLayoutProperty(layerId, 'visibility', 'visible');\n * });\n * ```\n */\n get sourceAndLayerIDs(): Record<keyof SOURCES_WITH_LAYERS, SourceWithLayerIDs> {\n return this._sourceAndLayerIDs;\n }\n}\n","import type { MapGeoJSONFeature } from 'maplibre-gl';\nimport type { EventsProxy } from './EventsProxy';\nimport type { EventHandlerConfig, EventType, SourceWithLayers, UserEventHandler } from './types';\n\n/**\n * Event handling interface for map features.\n *\n * Provides a simple API for attaching and removing event handlers for user interactions\n * with map features such as clicks, hovers, and context menus. Each map module (POIs, Routing,\n * Places, etc.) exposes an `events` property that returns an EventsModule instance.\n *\n * @typeParam T - The feature type returned in event handlers (extends MapGeoJSONFeature)\n *\n * @remarks\n * **Supported Event Types:**\n * - `click`: User clicks/taps on a feature\n * - `contextmenu`: User right-clicks on a feature (or long-press on mobile)\n * - `hover`: Mouse enters a feature\n * - `long-hover`: Mouse hovers over feature for configured duration (300-800ms)\n *\n * **Event Handler Signature:**\n * ```typescript\n * (feature: T, lngLat: LngLat, features: T[]) => void\n * ```\n *\n * **Parameters:**\n * - `feature`: The primary feature under the cursor\n * - `lngLat`: Geographic coordinates of the event\n * - `features`: All features at this location (when multiple overlap)\n *\n * **Key Features:**\n * - Automatic cursor management (pointer on hover)\n * - Smart event handling for overlapping features\n * - Configurable hover delays\n * - Memory-safe cleanup when removing handlers\n *\n * @example\n * ```typescript\n * // POI click handler\n * const pois = map.pois();\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('Clicked POI:', feature.properties.name);\n * console.log('Location:', lngLat);\n * showInfoWindow(feature.properties);\n * });\n *\n * // Route waypoint hover\n * const routing = map.routing();\n * routing.events.waypoints.on('hover', (waypoint) => {\n * showTooltip(`Waypoint ${waypoint.properties.index}`);\n * });\n *\n * // Long-hover for detailed info\n * pois.events.on('long-hover', (feature) => {\n * loadAndShowDetailedInfo(feature.properties.id);\n * });\n *\n * // Context menu (right-click)\n * routing.events.mainLines.on('contextmenu', (route, lngLat) => {\n * showContextMenu(lngLat, [\n * { label: 'Add waypoint here', action: () => addWaypoint(lngLat) },\n * { label: 'View route details', action: () => showDetails(route) }\n * ]);\n * });\n *\n * // Remove all click handlers\n * pois.events.off('click');\n * ```\n *\n * @example\n * ```typescript\n * // Complete interaction example\n * const places = map.places();\n *\n * // Show name on hover\n * places.events.on('hover', (place) => {\n * tooltip.show(place.properties.name);\n * });\n *\n * // Show details on click\n * places.events.on('click', (place, lngLat) => {\n * sidebar.show({\n * title: place.properties.name,\n * address: place.properties.address.freeformAddress,\n * coordinates: lngLat\n * });\n * });\n *\n * // Cleanup on component unmount\n * onUnmount(() => {\n * places.events.off('hover');\n * places.events.off('click');\n * });\n * ```\n *\n * @group User Interaction Events\n */\nexport class EventsModule<T = MapGeoJSONFeature> {\n constructor(\n private readonly eventProxy: EventsProxy,\n private readonly sourceWithLayers: SourceWithLayers,\n private readonly config: EventHandlerConfig | undefined,\n /**\n * Optional function that transforms a raw {@link MapGeoJSONFeature} into the\n * feature type `T` exposed by the module. When provided, every feature passed (fired)\n * to event handlers is first run through this function, so callers receive the\n * module's own feature shape instead of the underlying MapLibre feature.\n *\n * When omitted, `T` defaults to `MapGeoJSONFeature` and features are forwarded\n * as-is.\n */\n private readonly mapping?: (feature: MapGeoJSONFeature) => T,\n ) {}\n\n /**\n * Register an event handler for user interactions with map features.\n *\n * Attaches a callback function that will be invoked when users interact with\n * features in this module (e.g., clicking on a POI, hovering over a route).\n *\n * @param type The type of event to listen for (click, contextmenu, hover, long-hover)\n * @param handler Callback function invoked when the event occurs\n *\n * @remarks\n * **Handler Parameters:**\n * - `feature`: The primary feature that triggered the event\n * - `lngLat`: Geographic coordinates [longitude, latitude] of the event\n * - `features`: Array of all features at the event location (for overlapping features)\n *\n * **Behavior:**\n * - Only one handler per event type (calling `on()` again replaces the previous handler)\n * - Handlers are preserved across map style changes\n * - Cursor automatically changes to pointer on hover\n * - Events respect module visibility (hidden features don't trigger events)\n *\n * **Performance:**\n * - Hover events use spatial indexing for fast lookup\n * - Long-hover has configurable delay to prevent accidental triggers\n * - Event handlers should be lightweight to maintain smooth interaction\n *\n * @example\n * ```typescript\n * // Basic click handler\n * module.events.on('click', (feature, lngLat, features) => {\n * console.log('Clicked feature:', feature.properties);\n * console.log('Location:', lngLat);\n * console.log('All features here:', features.length);\n * });\n *\n * // Hover with tooltip\n * module.events.on('hover', (feature) => {\n * const name = feature.properties.name || 'Unnamed';\n * tooltip.show(name);\n * });\n *\n * // Long-hover for detailed preview\n * module.events.on('long-hover', (feature) => {\n * // Only triggered after hovering for 300-800ms\n * loadPreview(feature.properties.id);\n * });\n *\n * // Right-click context menu\n * module.events.on('contextmenu', (feature, lngLat) => {\n * event.preventDefault(); // Prevent browser context menu\n * showCustomMenu(lngLat, feature);\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Route-specific handlers\n * const routing = map.routing();\n *\n * // Handle route line clicks\n * routing.events.mainLines.on('click', (route) => {\n * highlightRoute(route.properties.routeID);\n * showRouteSummary(route.properties.summary);\n * });\n *\n * // Handle waypoint interactions\n * routing.events.waypoints.on('hover', (waypoint) => {\n * const index = waypoint.properties.index;\n * showTooltip(`Stop ${index + 1}`);\n * });\n * ```\n */\n on(type: EventType, handler: UserEventHandler<T>) {\n if (this.mapping) {\n const mapping = this.mapping;\n this.eventProxy.addEventHandler(\n this.sourceWithLayers,\n (feature, ...rest) => handler(mapping(feature), ...rest),\n type,\n this.config,\n );\n } else {\n this.eventProxy.addEventHandler(this.sourceWithLayers, handler, type, this.config);\n }\n }\n\n /**\n * Remove all event handlers for a specific event type.\n *\n * Unregisters the callback function for the specified event type, stopping\n * further event notifications. This is important for cleanup to prevent\n * memory leaks and unwanted behavior.\n *\n * @param type The type of event to stop listening for\n *\n * @remarks\n * **Cleanup Behavior:**\n * - Removes only the specified event type (other types remain active)\n * - Resets cursor behavior for this module\n * - Safe to call multiple times (no error if no handler exists)\n * - Does not affect other modules' event handlers\n *\n * **When to Use:**\n * - Component unmounting/cleanup\n * - Switching between interaction modes\n * - Temporarily disabling interactions\n * - Replacing an existing handler (call `off()` then `on()`)\n *\n * **Best Practices:**\n * - Always clean up event handlers when component unmounts\n * - Remove handlers before removing features from the map\n * - Use framework lifecycle hooks for automatic cleanup\n *\n * @example\n * ```typescript\n * // Remove click handlers\n * module.events.off('click');\n *\n * // Remove all handlers\n * module.events.off('click');\n * module.events.off('hover');\n * module.events.off('long-hover');\n * module.events.off('contextmenu');\n * ```\n *\n * @example\n * ```typescript\n * // React component cleanup\n * useEffect(() => {\n * const pois = map.pois();\n *\n * pois.events.on('click', handlePoiClick);\n * pois.events.on('hover', handlePoiHover);\n *\n * return () => {\n * // Cleanup on unmount\n * pois.events.off('click');\n * pois.events.off('hover');\n * };\n * }, [map]);\n * ```\n *\n * @example\n * ```typescript\n * // Replace handler\n * module.events.off('click'); // Remove old handler\n * module.events.on('click', newHandler); // Add new handler\n *\n * // Disable interactions temporarily\n * const savedHandler = currentHandler;\n * module.events.off('click'); // Disable\n * // ... later ...\n * module.events.on('click', savedHandler); // Re-enable\n * ```\n */\n off(type: EventType) {\n this.eventProxy.remove(this.sourceWithLayers, type);\n }\n}\n","import { isEmpty, remove } from 'lodash-es';\nimport type { LayerSpecification, MapGeoJSONFeature } from 'maplibre-gl';\nimport type { EventHandlerConfig, EventType, SourcesWithLayers, SourceWithLayers, UserEventHandler } from './types';\n\n// TODO: add support for multiple handlers per source, layers, and event type?\n// ... (this means multiple handlers for the same module, or repeated \"on\" calls for the same module)\ntype SourceEventTypeHandler = {\n sourceWithLayers: SourceWithLayers;\n layerIDs: string[];\n fn: UserEventHandler<any>;\n config?: EventHandlerConfig;\n};\n\ntype SourceEventHandlers = Partial<Record<EventType, SourceEventTypeHandler[]>>;\n\n// Source ID to event handlers for that source:\n// Example:\n// const handlers = {\n// vectorTiles: {\n// click: [\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [\"Buildings\", \"Roads - Major\"],\n// fn: UserEventsHandler...\n// },\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [\"Water\", \"Landuse - Parks],\n// fn: UserEventsHandler...\n// }\n// ],\n// hover: [\n// {\n// sourceWithLayers: SourceWithLayers reference,\n// layerIDs: [...],\n// fn: UserEventsHandler...\n// }\n// ]\n// },\ntype EventHandlers = Record<string, SourceEventHandlers>;\n\nconst matchesLayers = (layers: LayerSpecification[], layerIDs: string[]): boolean =>\n layerIDs.every((layerId, index) => layerId === layers[index].id);\n\n/**\n * @ignore\n */\nexport abstract class AbstractEventProxy {\n protected interactiveLayerIDs: string[] = [];\n protected handlers: EventHandlers = {};\n\n /**\n * Adds the given layers as interactive, so we'll listen to them for hover and click.\n * @param sourceWithLayers The sources and layers to listen to.\n */\n private ensureInteractiveLayerIDsAdded(sourceWithLayers: SourceWithLayers) {\n sourceWithLayers._layerSpecs.forEach((layerSpec) => {\n if (!this.interactiveLayerIDs.includes(layerSpec.id)) {\n this.interactiveLayerIDs.push(layerSpec.id);\n }\n });\n }\n\n /**\n * Register an event listener to the list.\n * @param sourceWithLayers The sources and layers to added.\n * @param handlerFn Function that will handle the event.\n * @param type Type of event to listen to.\n * @param config Optional configuration for the event handler.\n */\n addEventHandler<T = MapGeoJSONFeature>(\n sourceWithLayers: SourceWithLayers,\n handlerFn: UserEventHandler<T>,\n type: EventType,\n config: EventHandlerConfig | undefined,\n ) {\n this.ensureInteractiveLayerIDsAdded(sourceWithLayers);\n const sourceId = sourceWithLayers.source.id;\n\n if (!this.handlers[sourceId]) {\n this.handlers[sourceId] = { [type]: [] };\n }\n this.handlers[sourceId][type] ??= [];\n\n this.handlers[sourceId][type]?.push({\n sourceWithLayers,\n layerIDs: sourceWithLayers._layerSpecs.map((layer) => layer.id),\n fn: handlerFn,\n config,\n });\n }\n\n /**\n * Removes the given sources and layers from the interactive list. When not present, nothing happens.\n * @param type The event type to be removed.\n * @param sourceWithLayers The sources and layers to remove, matched by source and layer IDs.\n */\n remove(sourceWithLayers: SourceWithLayers, type: EventType) {\n const sourceEventTypeHandlers = this.handlers[sourceWithLayers.source.id]?.[type];\n if (sourceEventTypeHandlers) {\n // @ts-ignore\n remove(sourceEventTypeHandlers, (handler) => matchesLayers(sourceWithLayers._layerSpecs, handler.layerIDs));\n // cleaning up empty arrays and objects if necessary:\n if (!sourceEventTypeHandlers.length) {\n delete this.handlers[sourceWithLayers.source.id]?.[type];\n if (isEmpty(this.handlers[sourceWithLayers.source.id])) {\n delete this.handlers[sourceWithLayers.source.id];\n }\n }\n }\n for (const layer of sourceWithLayers._layerSpecs) {\n // @ts-ignore\n remove(this.interactiveLayerIDs, (id) => layer.id === id);\n }\n }\n\n /**\n * Removes all interactive sources and layers.\n */\n removeAll() {\n this.interactiveLayerIDs = [];\n this.handlers = {};\n }\n\n /**\n * Returns whether this source is registered for events (has handlers attached).\n * @param sourceId The source id (should be linked to a SourceWithLayers instance).\n */\n hasSourceID(sourceId: string): boolean {\n return !!this.handlers[sourceId];\n }\n\n /**\n * Updates the given sourcesWithLayers, if they have any handlers.\n * * (This is typically called to refresh any registered, stale sourceWithLayers references after a map style has changed).\n * @param sourcesWithLayers The new sources with layers to replace existing ones.\n */\n updateIfRegistered(sourcesWithLayers: SourcesWithLayers): void {\n for (const sourceWithLayers of Object.values(sourcesWithLayers)) {\n const sourceHandlers = this.handlers[sourceWithLayers.source.id];\n if (sourceHandlers) {\n for (const sourceEventTypeHandlers of Object.values(sourceHandlers)) {\n for (const handler of sourceEventTypeHandlers) {\n if (sourceWithLayers.equalSourceAndLayerIDs(handler.sourceWithLayers)) {\n // We keep the reference fresh:\n handler.sourceWithLayers = sourceWithLayers;\n }\n }\n }\n }\n }\n }\n\n protected findHandlers = (\n types: EventType[],\n sourceId: string | undefined,\n layerId: string | undefined,\n ): SourceEventTypeHandler[] =>\n (sourceId &&\n layerId &&\n types.flatMap((type) => {\n const sourceEventTypeHandlers = this.handlers[sourceId]?.[type];\n return sourceEventTypeHandlers?.length === 1\n ? // if there's only handler for that source and type, we just return it, no need to match layers:\n sourceEventTypeHandlers\n : this.handlers[sourceId]?.[type]?.filter((handler) => handler.layerIDs.includes(layerId)) || [];\n })) ||\n [];\n}\n","import type { Feature } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport type { MapGeoJSONFeature, Point2D } from 'maplibre-gl';\nimport { areBothDefinedAndEqual } from './mapUtils';\nimport { GeoJSONSourceWithLayers } from './SourceWithLayers';\nimport type { EventType, SourceWithLayers } from './types';\n\ntype IndexedFeature<F extends Feature = Feature> = { feature: F; index: number };\n\nconst findFeatureById = (features: Feature[], id: string | number | undefined): IndexedFeature | undefined => {\n for (let i = 0; i < features.length; i++) {\n const feature = features[i];\n if (feature.id === id) {\n return { feature, index: i };\n }\n }\n return undefined;\n};\n\nconst isHighPriority = (eventType: EventType): boolean => eventType === 'click' || eventType === 'contextmenu';\n\n/**\n * Puts or removes the given event state to the right feature in featuresToUpdate based on the\n * @param eventState The new event state to apply or to use as reference.\n * @param featureId The ID of the feature to update within featuresToUpdate.\n * @param featuresToUpdate The features list which will be updated.\n * @param mode If updateInProps, replaces the existing eventState. If removeFromProps, removes the existing eventState.\n * @return The index of the updated feature in the mutated featuresToUpdate array.\n * @ignore\n */\nexport const putEventState = (\n eventState: EventType,\n featureId: string | number | undefined,\n featuresToUpdate: Feature[], // \"featuresToUpdate\" will be mutated\n mode: 'updateInProps' | 'removeFromProps' = 'updateInProps',\n): number | undefined => {\n const { feature, index } = findFeatureById(featuresToUpdate, featureId) || {};\n if (feature && (!isHighPriority(feature.properties?.eventState) || isHighPriority(eventState))) {\n const updatedFeature = {\n ...feature,\n properties:\n mode === 'updateInProps'\n ? { ...feature?.properties, eventState }\n : omit(feature?.properties, 'eventState'),\n };\n featuresToUpdate.splice(index as number, 1, updatedFeature);\n return index;\n }\n return undefined;\n};\n\nconst removeEventStateAndShow = (\n newEventType: EventType,\n rawFeature: MapGeoJSONFeature,\n sourceWithLayers: GeoJSONSourceWithLayers,\n): void => {\n const prevFeaturesToUpdate = [...sourceWithLayers.shownFeatures.features];\n const updatedIndex = putEventState(newEventType, rawFeature.id, prevFeaturesToUpdate, 'removeFromProps');\n if (!isNil(updatedIndex)) {\n sourceWithLayers.show({ ...sourceWithLayers.shownFeatures, features: prevFeaturesToUpdate });\n }\n};\n\n/**\n * Updates the event state props of the given features.\n * @param eventState The type of event that is being done or undone.\n * @param eventFeature The current MapLibre feature affected by the event, if any.\n * If undefined, it means the event is being undone from prevRawFeature without going to any other one.\n * @param prevEventFeature The previous MapLibre feature affected by the event type, if any (e.g. previous one clicked or hovered).\n * @param sourceWithLayers The source with layers of eventFeature, if any.\n * @param prevSourceWithLayers The source with layers of prevEventFeature, if any.\n * @ignore\n */\nexport const updateEventState = (\n eventState: EventType,\n eventFeature: MapGeoJSONFeature | undefined,\n prevEventFeature: MapGeoJSONFeature | undefined,\n sourceWithLayers: SourceWithLayers | undefined,\n prevSourceWithLayers: SourceWithLayers | undefined,\n): Partial<IndexedFeature> => {\n if (eventFeature && sourceWithLayers instanceof GeoJSONSourceWithLayers) {\n const featuresToUpdate = [...sourceWithLayers.shownFeatures.features];\n const updatedIndex = putEventState(eventState, eventFeature.id, featuresToUpdate) as number;\n\n if (prevEventFeature && !areBothDefinedAndEqual(prevEventFeature, eventFeature)) {\n // (we have both current and prev features for this event type)\n if (prevSourceWithLayers === sourceWithLayers) {\n // we undo the event state from prev feature next to the new one (they will be shown below):\n putEventState(eventState, prevEventFeature.id, featuresToUpdate, 'removeFromProps');\n } else if (prevSourceWithLayers instanceof GeoJSONSourceWithLayers) {\n // the prev feature is in other source/layers, so we update and show them:\n removeEventStateAndShow(eventState, prevEventFeature, prevSourceWithLayers);\n }\n }\n\n sourceWithLayers.show({ ...sourceWithLayers.shownFeatures, features: featuresToUpdate });\n\n return { feature: featuresToUpdate[updatedIndex], index: updatedIndex };\n }\n if (prevEventFeature && prevSourceWithLayers instanceof GeoJSONSourceWithLayers) {\n removeEventStateAndShow(eventState, prevEventFeature, prevSourceWithLayers);\n }\n return { feature: eventFeature };\n};\n\n/**\n * Detects whether there's been a hovering change with the given event and state params.\n * @param hoveringPoint The current hovering pixel coordinates.\n * @param hoveringFeature The current feature being hovered, if any.\n * @param prevHoveredPoint The pixel coordinates from the previous hovering event, if any.\n * @param prevHoveredFeature The previous feature being hovered, if any (could be the same as hoveringFeature).\n * @ignore\n */\nexport const detectHoverState = (\n hoveringPoint: Point2D,\n hoveringFeature: MapGeoJSONFeature | undefined,\n prevHoveredPoint: Point2D | undefined,\n prevHoveredFeature: MapGeoJSONFeature | undefined,\n): {\n /**\n * Whether a hover state change happened, such as no-hover -> hover or vice-versa.\n */\n hoverChanged?: boolean;\n /**\n * Whether the mouse is moving along the hovered feature (not stopped on it).\n */\n mouseInMotionOverHoveredFeature?: boolean;\n} => {\n if (hoveringFeature) {\n if (!prevHoveredFeature) {\n return { hoverChanged: true };\n }\n if (\n (hoveringFeature.id && hoveringFeature.id !== prevHoveredFeature.id) ||\n (hoveringFeature.properties.id && hoveringFeature.properties.id !== prevHoveredFeature.properties.id) ||\n hoveringFeature.source !== prevHoveredFeature.source ||\n // comparing by layer ID is needed when two id-less features from the same source but different layers are compared\n // this can happen when e.g. hovering over different layer groups for the base map\n hoveringFeature.layer.id !== prevHoveredFeature.layer.id\n ) {\n // hovering from one feature to another one (from the same or different layer/source):\n return { hoverChanged: true };\n }\n // (else we're hovering along the same feature)\n if (\n prevHoveredPoint &&\n (hoveringPoint.x - prevHoveredPoint.x != 0 || hoveringPoint.y - prevHoveredPoint.y != 0)\n ) {\n return { mouseInMotionOverHoveredFeature: true };\n }\n } else if (prevHoveredFeature) {\n return { hoverChanged: true };\n }\n return {};\n};\n","import type { LngLat, Map, MapGeoJSONFeature, MapMouseEvent, Point2D, PointLike } from 'maplibre-gl';\nimport type { MapEventsConfig } from '../init';\nimport { AbstractEventProxy } from './AbstractEventProxy';\nimport { detectHoverState, updateEventState } from './eventUtils';\nimport { deserializeFeatures } from './mapUtils';\nimport type { ClickEventType, EventHandlerConfig, SourceWithLayers } from './types';\n\n// Default values for events\nconst eventsProxyDefaultConfig: Required<MapEventsConfig> = {\n precisionMode: 'box',\n paddingBoxPx: 5,\n cursorOnHover: 'pointer',\n cursorOnMouseDown: 'grabbing',\n cursorOnMap: 'default',\n /**\n * Delayed hover control:\n * * The first hover we do after the map moves is longer\n */\n longHoverDelayAfterMapMoveMS: 800,\n /* Followup hovers with the same non-moving map are quicker (\"hovering around mode\") */\n longHoverDelayOnStillMapMS: 300,\n};\n\n/**\n * This is the place where we handle the user events on the map (mousemove/hover and click mostly).\n * To have full control on hovers and clicks when multiple overlapping layers are present, that logic must be centralized here.\n * @ignore\n */\nexport class EventsProxy extends AbstractEventProxy {\n private readonly map: Map;\n private readonly mapCanvas: HTMLCanvasElement;\n private enabled = true;\n private hoveringLngLat?: LngLat;\n private hoveringPoint?: Point2D;\n private hoveringFeature?: MapGeoJSONFeature;\n private hoveringFeatures?: MapGeoJSONFeature[];\n private hoveringSourceWithLayers?: SourceWithLayers;\n private longHoverTimeoutHandlerID?: number;\n // Control flag to indicate that the coming hover is the first one since the map is \"quiet\" again:\n private firstDelayedHoverSinceMapMove = true;\n private lastClickedFeature?: MapGeoJSONFeature;\n private lastClickedSourceWithLayers?: SourceWithLayers;\n private lastCursorStyle: string;\n // Configuration\n private readonly config: Required<MapEventsConfig>;\n\n constructor(map: Map, config: MapEventsConfig = {}) {\n super();\n this.map = map;\n this.mapCanvas = map.getCanvas();\n this.config = { ...eventsProxyDefaultConfig, ...config };\n this.mapCanvas.style.cursor = this.config.cursorOnMap;\n this.lastCursorStyle = this.config.cursorOnMap;\n this.listenToEvents();\n }\n\n private listenToEvents() {\n this.map.on('mousemove', (ev) => this.onMouseMove(ev));\n this.map.on('movestart', () => this.onMouseStart());\n this.map.on('mouseout', () => this.onMouseOut());\n this.map.on('mouseover', (ev) => this.onMouseMove(ev));\n this.map.on('mousedown', () => this.onMouseDown());\n this.map.on('mouseup', () => this.onMouseUp());\n this.map.on('click', (ev) => this.onMapClick('click', ev));\n this.map.on('contextmenu', (ev) => this.onMapClick('contextmenu', ev));\n }\n\n // Enable/Disable Events\n public enable(enabled: boolean) {\n this.enabled = enabled;\n if (!enabled) {\n this.clearLongHoverTimeout();\n }\n }\n\n private toPaddedBounds(point: Point2D): [[number, number], [number, number]] {\n const padding = this.config.paddingBoxPx;\n return [\n // sw:\n [point.x - padding, point.y + padding],\n // ne:\n [point.x + padding, point.y - padding],\n ];\n }\n\n private isEnabled() {\n return this.enabled && !this.map.isMoving();\n }\n\n private getRenderedFeatures(point: Point2D): MapGeoJSONFeature[] {\n if (!this.interactiveLayerIDs.length) {\n return [];\n }\n const options = { layers: this.interactiveLayerIDs, validate: false };\n const precision = this.config.precisionMode;\n // first optional attempt right in the given coordinates:\n const renderedFeatures =\n precision === 'point-then-box' || precision === 'point'\n ? this.map.queryRenderedFeatures(point as PointLike, options)\n : [];\n return renderedFeatures.length || precision === 'point'\n ? renderedFeatures\n : // second attempt using 'box' = padded bounds (trying to hit something slightly further from the pointer location)\n this.map.queryRenderedFeatures(this.toPaddedBounds(point), options);\n }\n\n private clearLongHoverTimeout() {\n window.clearTimeout(this.longHoverTimeoutHandlerID);\n }\n\n private restartLongHoverTimeout() {\n this.clearLongHoverTimeout();\n this.longHoverTimeoutHandlerID = window.setTimeout(\n () => this.handleLongHoverTimeout(),\n this.firstDelayedHoverSinceMapMove\n ? this.config.longHoverDelayAfterMapMoveMS\n : this.config.longHoverDelayOnStillMapMS,\n );\n }\n\n private handleLongHoverTimeout() {\n // We avoid firing long hovers when the feature is in clicked state:\n this.firstDelayedHoverSinceMapMove = false;\n\n if (this.hoveringSourceWithLayers) {\n const eventState = updateEventState(\n 'long-hover',\n this.hoveringFeature,\n undefined,\n this.hoveringSourceWithLayers,\n undefined,\n );\n\n this.findHandlers(['long-hover'], this.hoveringFeature?.source, this.hoveringFeature?.layer.id).forEach(\n (handler) =>\n handler.fn(\n eventState.feature,\n this.hoveringLngLat as LngLat,\n this.hoveringFeatures as MapGeoJSONFeature[],\n this.hoveringSourceWithLayers as SourceWithLayers,\n ),\n );\n }\n }\n\n private onMouseStart() {\n this.firstDelayedHoverSinceMapMove = true;\n this.clearLongHoverTimeout();\n }\n\n private onMouseOut() {\n // Preventing accidental de-hover event if we actually leave the map canvas.\n // Since this could potentially be about jumping into a map popup, so we leave that up to the caller.\n this.clearLongHoverTimeout();\n }\n\n private onMouseDown() {\n this.lastCursorStyle = this.mapCanvas.style.cursor;\n this.mapCanvas.style.cursor = this.config.cursorOnMouseDown;\n }\n\n private onMouseUp() {\n this.mapCanvas.style.cursor = this.lastCursorStyle;\n }\n\n private onMouseMove(ev: MapMouseEvent) {\n if (!this.isEnabled()) {\n // We ensure no unwanted hover handling while disabled or the map moves\n return;\n }\n\n this.hoveringFeatures = this.getRenderedFeatures(ev.point);\n deserializeFeatures(this.hoveringFeatures);\n const [hoveredTopFeature] = this.hoveringFeatures;\n\n // Check if the layer has any handlers registered.\n // Since hover is the \"lowest\" event type, having a handler for any event type justifies supporting hover state.\n // However, we'll only fire the hover events if there are handlers for hover specifically.\n if (hoveredTopFeature && !this.hasSourceID(hoveredTopFeature.source)) {\n return;\n }\n\n // hoverChangeDetected: whether a change happened, such as no-hover -> hover or vice versa:\n // mouseInMotionOverHoveredFeature: whether the mouse is moving along the hovered feature (not stopped on it):\n const { hoverChanged, mouseInMotionOverHoveredFeature } = detectHoverState(\n ev.point,\n hoveredTopFeature,\n this.hoveringPoint,\n this.hoveringFeature,\n );\n\n if (hoverChanged || mouseInMotionOverHoveredFeature) {\n this.hoveringLngLat = ev.lngLat;\n this.hoveringPoint = ev.point;\n const prevHoveredFeature = this.hoveringFeature;\n this.hoveringFeature = hoveredTopFeature;\n const prevHoveredSourceWithLayers = this.hoveringSourceWithLayers;\n\n // Hovering basic event states are still processed if any other handlers are registered for that source/layers.\n // We do so because basic hovering states indicate a feature is interactive.\n // (e.g. if there's a click handler, we'll still apply basic hover states, even if we don't fire hover events)\n const firstHandler = this.findHandlers(\n ['hover', 'hover-move', 'long-hover', 'click', 'contextmenu'],\n hoveredTopFeature?.source,\n hoveredTopFeature?.layer.id,\n )?.[0];\n\n // NOTE: handlers overlapping in source and layer IDs won't be supported properly:\n this.hoveringSourceWithLayers = firstHandler?.sourceWithLayers;\n\n if (hoverChanged) {\n this.updateHoverCursor(firstHandler?.config);\n\n const eventState = updateEventState(\n 'hover',\n this.hoveringFeature,\n prevHoveredFeature,\n this.hoveringSourceWithLayers,\n prevHoveredSourceWithLayers,\n );\n\n const hoverHandlers = this.findHandlers(\n ['hover'],\n hoveredTopFeature?.source,\n hoveredTopFeature?.layer.id,\n );\n\n for (const handler of hoverHandlers) {\n handler.fn(eventState.feature, ev.lngLat, this.hoveringFeatures, this.hoveringSourceWithLayers);\n }\n }\n\n if (mouseInMotionOverHoveredFeature) {\n const hoverMoveHandlers = this.findHandlers(\n ['hover-move'],\n this.hoveringFeature?.source,\n this.hoveringFeature?.layer.id,\n );\n\n for (const handler of hoverMoveHandlers) {\n handler.fn(this.hoveringFeature, ev.lngLat, this.hoveringFeatures, this.hoveringSourceWithLayers);\n }\n }\n\n this.restartLongHoverTimeout();\n }\n }\n\n private updateHoverCursor(config: EventHandlerConfig | undefined) {\n if (this.hoveringFeature) {\n this.mapCanvas.style.cursor = config?.cursorOnHover ?? this.config.cursorOnHover;\n } else {\n this.mapCanvas.style.cursor = this.config.cursorOnMap;\n }\n }\n\n private onMapClick(clickType: ClickEventType, ev: MapMouseEvent) {\n if (!this.isEnabled()) {\n // We avoid any accidental click handling while disabled or the map moves\n return;\n }\n\n const clickedFeatures = this.getRenderedFeatures(ev.point);\n // Deserialize Features from maplibre queryRenderedFeatures response\n deserializeFeatures(clickedFeatures);\n\n const prevClickedFeature = this.lastClickedFeature;\n this.lastClickedFeature = clickedFeatures[0];\n const prevClickedSourceWithLayers = this.lastClickedSourceWithLayers;\n const clickHandlers = this.findHandlers(\n [clickType],\n this.lastClickedFeature?.source,\n this.lastClickedFeature?.layer.id,\n );\n\n // NOTE: handlers overlapping in source and layer IDs won't be supported properly:\n this.lastClickedSourceWithLayers = clickHandlers?.[0]?.sourceWithLayers;\n\n const eventState = updateEventState(\n clickType,\n this.lastClickedFeature,\n prevClickedFeature,\n this.lastClickedSourceWithLayers,\n prevClickedSourceWithLayers,\n );\n\n for (const handler of clickHandlers) {\n handler.fn(eventState.feature, ev.lngLat, clickedFeatures, this.lastClickedSourceWithLayers);\n }\n }\n}\n","/**\n * Key layer IDs from the vector map style for positioning custom layers.\n *\n * @remarks\n * This constant provides reference layer IDs from TomTom's vector map styles that are used\n * as anchors when positioning custom layers in the map's layer stack. By referencing these\n * layer IDs, SDK modules and custom implementations can control where their layers appear\n * in the rendering order (visual stacking).\n *\n * **Understanding Layer Order:**\n *\n * \"Lowest\" refers to position in the layer stack, not physical elevation. Layers are rendered\n * from bottom to top - layers lower in the stack are drawn first and appear underneath layers\n * higher in the stack. When you add a layer \"before\" a reference layer, it's inserted lower\n * in the stack and will be drawn underneath that reference layer.\n *\n * **How SDK Modules Use These IDs:**\n *\n * **RoutingModule** - Positions route visualizations below labels for readability:\n * ```typescript\n * // Routes appear below labels but above the base map\n * const routingModule = await RoutingModule.get(map);\n * // Uses mapStyleLayerIDs.lowestLabel by default\n * ```\n *\n * **GeometriesModule** - Configurable layer positioning for polygon areas:\n * ```typescript\n * // Default: below labels\n * const geometriesModule = await GeometriesModule.get(map);\n *\n * // Custom: below buildings to show at ground level\n * const geometriesModule = await GeometriesModule.get(map, {\n * beforeLayerConfig: 'lowestBuilding'\n * });\n *\n * // On top of everything\n * const geometriesModule = await GeometriesModule.get(map, {\n * beforeLayerConfig: 'top'\n * });\n * ```\n *\n * **Custom Layer Implementation:**\n * ```typescript\n * // Add a highlight layer below labels\n * map.addLayer({\n * id: 'search-results',\n * type: 'fill',\n * source: 'results-source',\n * paint: { 'fill-color': '#ffcc00', 'fill-opacity': 0.4 }\n * }, mapStyleLayerIDs.lowestLabel);\n * ```\n *\n * **Style Persistence:**\n *\n * These layer IDs remain consistent across TomTom map style changes (e.g., switching from\n * 'standardLight' to 'standardDark'). The SDK automatically repositions module layers using\n * the corresponding reference layers in the new style, maintaining the intended visual hierarchy.\n *\n * **Important Notes:**\n * - `lowestBuilding` is not available in the Satellite style\n * - If a reference layer doesn't exist, custom layers will be added on top of the style\n * - Both RoutingModule and GeometriesModule default to `lowestLabel` for optimal visibility\n *\n * @see {@link GeometryBeforeLayerConfig} for GeometriesModule layer positioning options\n * @see {@link RouteLayersConfig} for RoutingModule layer configuration\n *\n * @group Map Style\n */\nexport const mapStyleLayerIDs = {\n /**\n * Country name label layer.\n *\n * @remarks\n * Lower in the stack than most labels. Use to position elements below country\n * labels but above other map features.\n */\n country: 'Places - Country name',\n /**\n * The lowest layer for place labels.\n *\n * @remarks\n * Lower in the stack than city labels. Use to position content below all place\n * name labels while keeping it visible above geographic features.\n */\n lowestPlaceLabel: 'Places - Village / Hamlet',\n /**\n * Points of Interest layer.\n *\n * @remarks\n * Use to position custom content in the layer stack relative to POI icons and labels.\n */\n poi: 'POI',\n /**\n * The lowest labels layer in the stack.\n *\n * @remarks\n * **Most commonly used reference layer.** Positioning layers before this ensures\n * content appears below all text labels, maintaining map readability while keeping\n * visualizations prominent.\n *\n * **Default for SDK modules:**\n * - RoutingModule uses this for route lines and waypoints\n * - GeometriesModule uses this as the default for polygon fills and borders\n *\n * This provides optimal balance between visibility and not obscuring map labels.\n */\n lowestLabel: 'Borders - Treaty label',\n /**\n * The lowest road line layer in the stack.\n *\n * @remarks\n * Represents tunnel railway outlines. Use to position content below the road network,\n * useful for showing base layers or features that should appear beneath transportation\n * infrastructure.\n */\n lowestRoadLine: 'Tunnel - Railway outline',\n /**\n * The lowest building layer in the stack.\n *\n * @remarks\n * Use to position content below 3D building extrusions while keeping it above\n * flat map features like roads and terrain.\n *\n * **Note:** Not available in the Satellite style.\n */\n lowestBuilding: 'Buildings - Underground',\n} as const;\n","/**\n * Source identifier for POI (Point of Interest) vector tiles.\n *\n * @remarks\n * Used to reference the POI layer in the map style, which contains\n * business locations, landmarks, and other points of interest.\n *\n * @group POIs\n */\nexport const POI_SOURCE_ID = 'vectorTiles';\n\n/**\n * Source identifier for hillshade terrain visualization.\n *\n * @remarks\n * References the raster source that provides terrain shading to visualize\n * elevation and topography on the map.\n *\n * @group Hillshade\n */\nexport const HILLSHADE_SOURCE_ID = 'hillshade';\n\n/**\n * Source identifier for base map vector tiles.\n *\n * @remarks\n * References the primary vector tile source containing roads, buildings,\n * land use, water bodies, and other fundamental map features.\n *\n * @group Base Map\n */\nexport const BASE_MAP_SOURCE_ID = 'vectorTiles';\n\n/**\n * Source identifier for traffic incidents vector tiles.\n *\n * @remarks\n * References the vector tile source containing real-time traffic incident data\n * such as accidents, road closures, and construction.\n *\n * @group Traffic Incidents\n */\nexport const TRAFFIC_INCIDENTS_SOURCE_ID = 'vectorTilesIncidents';\n\n/**\n * Source identifier for traffic flow vector tiles.\n *\n * @remarks\n * References the vector tile source containing real-time traffic flow data\n * showing current traffic speeds and congestion levels.\n *\n * @group Traffic Flow\n */\nexport const TRAFFIC_FLOW_SOURCE_ID = 'vectorTilesFlow';\n","/**\n * Layer IDs for POIs on the map.\n * @ignore\n */\nexport const poiLayerIDs = ['POI', 'POI - Micro'];\n","import type { POICategory } from '@tomtom-org/maps-sdk/core';\n\n// mappings based on https://docs.tomtom.com/map-display-api/documentation/tomtom-orbis-maps/vector/content#poi\n\n/**\n * POI category mappings that have a direct equivalent in Map Display POI categories.\n * @ignore\n */\nexport const mapDisplayPoiCategoryMappings: Partial<Record<POICategory, string>> = {\n ACCESS_GATEWAY: 'border_control',\n ADVENTURE_SPORTS_FACILITY: 'sport_facility',\n ADVENTURE_SPORTS_VENUE: 'sport_facility',\n AGRICULTURE: 'commercial_area',\n AIRPORT: 'airport',\n AMUSEMENT_PARK: 'amusement_park',\n AQUATIC_ZOO: 'zoo_or_aquarium',\n ASHRAM: 'religious_site',\n ATM: 'atm',\n AUTOMOTIVE_DEALER: 'vehicle_dealer',\n BANK: 'bank',\n BEACH: 'beach_resort',\n BUS_STOP: 'bus_station',\n BUSINESS_PARK: 'commercial_area',\n CAFE_PUB: 'cafe',\n CAMPING_GROUND: 'camping_ground',\n CAR_WASH: 'vehicle_wash',\n CASH_DISPENSER: 'atm',\n CASINO: 'nightlife',\n CHURCH: 'religious_site',\n CINEMA: 'cinema',\n CLOTHING_SHOP: 'clothing_shop',\n CLUB_ASSOCIATION: 'consumer_service',\n COLLEGE_UNIVERSITY: 'college_or_university',\n COMMERCIAL_BUILDING: 'commercial_area',\n COMMUNITY_CENTER: 'cultural_center',\n COMPANY: 'commercial_area',\n CONCERT_HALL: 'nightlife',\n COURTHOUSE: 'courthouse',\n CULTURAL_CENTER: 'cultural_center',\n DENTIST: 'dentist',\n DEPARTMENT_STORE: 'department_store',\n DOCTOR: 'doctor',\n ELECTRIC_VEHICLE_STATION: 'charging_location',\n EMBASSY: 'government_office',\n EMERGENCY_MEDICAL_SERVICE: 'emergency_ward_entrance',\n EMERGENCY_ROOM: 'emergency_ward_entrance',\n ENTERTAINMENT: 'nightlife',\n EXCHANGE: 'bank',\n EXHIBITION_CONVENTION_CENTER: 'conference_center',\n FERRY_TERMINAL: 'ferry_terminal',\n FIRE_STATION_BRIGADE: 'fire_station',\n FRONTIER_CROSSING: 'border_control',\n FUEL_FACILITIES: 'fuel_station',\n GAS_STATION: 'fuel_station',\n GEOGRAPHIC_FEATURE: 'landmark',\n GOLD_EXCHANGE: 'bank',\n GOLF_COURSE: 'golf_course',\n GOVERNMENT_OFFICE: 'government_office',\n GURUDWARA: 'religious_site',\n HEALTH_CARE_SERVICE: 'clinic',\n HELIPAD_HELICOPTER_LANDING: 'heliport_or_helipad',\n HILL: 'viewpoint',\n HOLIDAY_RENTAL: 'hotel_or_motel',\n HOSPITAL: 'hospital',\n HOSPITAL_POLYCLINIC: 'hospital',\n HOTEL_MOTEL: 'hotel_or_motel',\n ICE_SKATING_RINK: 'ice_rink',\n IMPORTANT_TOURIST_ATTRACTION: 'tourist_attraction',\n INDUSTRIAL_BUILDING: 'industrial_area',\n LEISURE_CENTER: 'park_and_recreation_facility',\n LIBRARY: 'public_library',\n MANUFACTURING_FACILITY: 'industrial_area',\n MARINA: 'transport_access',\n MARKET: 'marketplace',\n MEDIA_FACILITY: 'commercial_area',\n MILITARY_INSTALLATION: 'military_facility',\n MOSQUE: 'religious_site',\n MOTORING_ORGANIZATION_OFFICE: 'vehicle_inspection',\n MOUNTAIN_PASS: 'mountain_pass',\n MOUNTAIN_PEAK: 'viewpoint',\n MOVIE_THEATER: 'cinema',\n MUSEUM: 'museum',\n NATIVE_RESERVATION: 'aboriginal_land',\n NIGHTLIFE: 'nightlife',\n NON_GOVERNMENTAL_ORGANIZATION: 'consumer_service',\n OPEN_PARKING_AREA: 'parking_facility',\n OPERA_HOUSE: 'theater',\n PAGODA: 'religious_site',\n PARK_RECREATION_AREA: 'park_and_recreation_area',\n PARKING_GARAGE: 'parking_facility',\n PETROL_STATION: 'fuel_station',\n PHARMACY: 'pharmacy',\n PLACE_OF_WORSHIP: 'religious_site',\n POLICE_STATION: 'police_station',\n PORT_WAREHOUSE_FACILITY: 'industrial_area',\n POST_OFFICE: 'post_office',\n PRIMARY_RESOURCE_UTILITY: 'industrial_area',\n PRISON_CORRECTIONAL_FACILITY: 'prison',\n PUBLIC_AMENITY: 'consumer_service',\n PUBLIC_TRANSPORT_STOP: 'public_transport_stop',\n RAILWAY_STATION: 'railway_station',\n RENT_A_CAR_FACILITY: 'car_rental',\n RENT_A_CAR_PARKING: 'rental_car_parking',\n REPAIR_FACILITY: 'vehicle_repair',\n RESEARCH_FACILITY: 'commercial_area',\n RESIDENTIAL_ACCOMMODATION: 'commercial_area',\n REST_AREA: 'rest_and_service_area',\n RESTAURANT: 'restaurant',\n RESTAURANT_AREA: 'restaurant',\n SCENIC_PANORAMIC_VIEW: 'viewpoint',\n SCHOOL: 'school_or_kindergarten',\n SHOP: 'shop_or_store',\n SHOPPING_CENTER: 'mall',\n SPORTS_CENTER: 'sport_facility',\n STADIUM: 'stadium',\n SUPERMARKETS_HYPERMARKETS: 'supermarket',\n SWIMMING_POOL: 'sport_facility',\n SYNAGOG: 'religious_site',\n TAXI_STAND: 'taxi_stand',\n TEMPLE: 'religious_site',\n TENNIS_COURT: 'sport_facility',\n THEATER: 'theater',\n TOLL_GATE: 'toll_booth',\n TOURIST_INFORMATION_OFFICE: 'tourist_attraction',\n TRAFFIC_CONTROL_DEPARTMENT: 'vehicle_inspection',\n TRAFFIC_SERVICE_CENTER: 'vehicle_inspection',\n TRAIL_SYSTEM: 'park_and_recreation_area',\n TRAILS: 'park_and_recreation_area',\n TRANSPORT_AUTHORITY_VEHICLE_REGISTRATION: 'vehicle_inspection',\n TRUCK_STOP: 'truck_stop',\n VACATION_RENTAL: 'hotel_or_motel',\n VETERINARIAN: 'veterinary',\n WATER_SPORT: 'park_and_recreation_facility',\n WEIGH_STATION: 'weighbridge',\n WELFARE_ORGANIZATION: 'consumer_service',\n WINERY: 'food_and_drinks_shop',\n ZOOS_ARBORETA_BOTANICAL_GARDEN: 'zoo_or_aquarium',\n} as const;\n\n/**\n * Complete POI category mappings including all POI categories.\n * Extends the original mappings with additional categories mapped to semantically appropriate values.\n * @ignore\n */\nconst completeMapDisplayPoiCategoryMappings: Record<POICategory, string> = {\n ...(mapDisplayPoiCategoryMappings as Record<POICategory, string>),\n // Additional POI categories not in the original mapping\n SECURED_ENTRANCE: 'border_control',\n AGRICULTURAL_BUSINESS: 'commercial_area',\n FARM: 'commercial_area',\n HORTICULTURE: 'commercial_area',\n PRIMARY_PRODUCER: 'commercial_area',\n AIRFIELD: 'airport',\n AIRLINE_ACCESS: 'airport',\n MILITARY_AIRPORT: 'military_airfield',\n PRIVATE_AIRPORT: 'airport',\n PUBLIC_AIRPORT: 'airport',\n ATV_DEALER: 'vehicle_dealer',\n BOAT_DEALER: 'vehicle_dealer',\n BUS_DEALER: 'vehicle_dealer',\n CAR_DEALER: 'vehicle_dealer',\n MOTORCYCLE_DEALER: 'vehicle_dealer',\n RECREATIONAL_VEHICLE_DEALER: 'vehicle_dealer',\n TRUCK_DEALER: 'vehicle_dealer',\n VAN_DEALER: 'vehicle_dealer',\n DIVERSIFIED_FINANCIALS: 'bank',\n SAVINGS_INSTITUTION: 'bank',\n BEACH_CLUB: 'beach_resort',\n BAR: 'bar',\n CAFE: 'cafe',\n COCKTAIL_BAR: 'bar',\n COFFEE_SHOP: 'coffee_shop',\n INTERNET_CAFE: 'cafe',\n PUB: 'pub',\n TEA_HOUSE: 'cafe',\n WINE_BAR: 'bar',\n CARAVAN_SITE: 'camping_ground',\n RECREATIONAL_CAMPING_GROUND: 'camping_ground',\n REST_CAMP: 'camping_ground',\n TRUCK_WASH: 'vehicle_wash',\n DRIVE_IN_MOVIES: 'cinema',\n CHILDRENS_CLOTHES: 'clothing_shop',\n MENS_CLOTHING: 'clothing_shop',\n SPECIALTY_CLOTHING_SHOP: 'clothing_shop',\n WOMENS_CLOTHING: 'clothing_shop',\n PRIVATE_CLUB: 'consumer_service',\n JUNIOR_COLLEGE_COMMUNITY_COLLEGE: 'college_or_university',\n BUILDING: 'commercial_area',\n ADVERTISING_COMPANY: 'commercial_area',\n AGRICULTURAL_TECHNOLOGY: 'commercial_area',\n AIRLINE_COMPANY: 'commercial_area',\n AUTOMOBILE_COMPANY: 'commercial_area',\n BUSINESS_SERVICES: 'commercial_area',\n BUS_CHARTER_COMPANY: 'commercial_area',\n CABLE_TELEPHONE_COMPANY: 'commercial_area',\n CLEANING_SERVICES: 'commercial_area',\n COMPUTER_DATA_SERVICES: 'commercial_area',\n CONSTRUCTION_COMPANY: 'commercial_area',\n DELIVERY_SERVICE: 'commercial_area',\n ELECTRONICS_COMPANY: 'commercial_area',\n EQUIPMENT_RENTAL: 'commercial_area',\n FUNERAL_SERVICE_MORTUARIES: 'commercial_area',\n IMPORT_EXPORT_AND_DISTRIBUTION: 'commercial_area',\n INSURANCE_COMPANY: 'commercial_area',\n INVESTMENT_ADVISOR: 'commercial_area',\n LEGAL_SERVICES: 'commercial_area',\n MINING_COMPANY: 'commercial_area',\n MOVING_STORAGE_COMPANY: 'commercial_area',\n OIL_NATURAL_GAS: 'commercial_area',\n PHARMACEUTICAL_COMPANY: 'commercial_area',\n PUBLIC_HEALTH_TECHNOLOGY_COMPANY: 'commercial_area',\n PUBLISHING_TECHNOLOGIES: 'commercial_area',\n REAL_ESTATE_AGENT: 'commercial_area',\n REAL_ESTATE_COMPANY: 'commercial_area',\n SERVICE_COMPANY: 'commercial_area',\n SOFTWARE_COMPANY: 'commercial_area',\n TAX_SERVICES: 'commercial_area',\n TELECOMMUNICATIONS: 'commercial_area',\n TRANSPORT_COMPANY: 'commercial_area',\n TRAVEL_AGENT: 'commercial_area',\n WEDDING_SERVICES: 'commercial_area',\n GENERAL_PRACTITIONER: 'doctor',\n SPECIALIST: 'doctor',\n RIDGE: 'viewpoint',\n AMBULANCE_UNIT: 'emergency_ward_entrance',\n ROAD_RESCUE: 'emergency_ward_entrance',\n AMUSEMENT_ARCADE: 'nightlife',\n AMUSEMENT_PLACE: 'nightlife',\n BETTING_STATION: 'nightlife',\n FAIRGROUND: 'nightlife',\n MUSIC_CENTER: 'nightlife',\n CHECKPOINT: 'border_control',\n BAY: 'landmark',\n BRIDGE: 'landmark',\n BRIDGE_TUNNEL_OPERATIONS: 'landmark',\n CAPE: 'landmark',\n COVE: 'landmark',\n DAM: 'landmark',\n DUNE: 'landmark',\n ISLAND: 'landmark',\n LAGOON: 'landmark',\n LAKESHORE: 'landmark',\n LOCALE: 'landmark',\n MARSH: 'landmark',\n OASIS: 'landmark',\n PAN: 'landmark',\n PARKWAY: 'landmark',\n PLAIN_FLAT: 'landmark',\n PLATEAU: 'landmark',\n RAPIDS: 'landmark',\n REEF: 'landmark',\n RESERVOIR: 'landmark',\n RIVER_CROSSING: 'landmark',\n RIVER_SCENIC_AREA: 'landmark',\n ROCKS: 'landmark',\n SEASHORE: 'landmark',\n TUNNEL: 'landmark',\n VALLEY: 'landmark',\n WATER_HOLE: 'landmark',\n WELL: 'landmark',\n BUNGALOW_RENTAL: 'hotel_or_motel',\n CABINS_LODGES: 'hotel_or_motel',\n CHALET_RENTAL: 'hotel_or_motel',\n COTTAGE_RENTAL: 'hotel_or_motel',\n VILLA_RENTAL: 'hotel_or_motel',\n BLOOD_BANK: 'hospital',\n HOSPITAL_FOR_WOMEN_AND_CHILDREN: 'hospital',\n HOSPITAL_OF_CHINESE_MEDICINE: 'hospital',\n SPECIAL_HOSPITAL: 'hospital',\n B_B_GUEST_HOUSE: 'hotel_or_motel',\n HOSTEL: 'hotel_or_motel',\n HOTEL: 'hotel_or_motel',\n MOTEL: 'hotel_or_motel',\n RESORT: 'hotel_or_motel',\n QUARRY: 'industrial_area',\n AUTOMOBILE_MANUFACTURING: 'industrial_area',\n CHEMICAL_COMPANY: 'industrial_area',\n MANUFACTURING_COMPANY: 'industrial_area',\n MECHANICAL_ENGINEERING: 'industrial_area',\n MICROBREWERY: 'food_and_drinks_shop',\n OEM: 'industrial_area',\n BOAT_LAUNCHING_RAMP: 'transport_access',\n HARBOR: 'transport_access',\n YACHT_BASIN: 'transport_access',\n FARMERS_MARKET: 'marketplace',\n FOOD_MARKET: 'marketplace',\n INFORMAL_MARKET: 'marketplace',\n PUBLIC_MARKET: 'marketplace',\n PLANETARIUM: 'landmark',\n CABARET_THEATER: 'nightlife',\n COMEDY_CLUB: 'nightlife',\n DISCO_CLUB: 'nightlife',\n JAZZ_CLUB: 'nightlife',\n KARAOKE_CLUB: 'nightlife',\n FISHING_HUNTING_AREA: 'park_and_recreation_area',\n FOREST_AREA: 'park_and_recreation_area',\n HISTORICAL_PARK: 'park_and_recreation_area',\n NATURAL_RECREATION_ATTRACTION: 'park_and_recreation_area',\n PARK: 'park_and_recreation_area',\n PICNIC_AREA: 'park_and_recreation_area',\n PRESERVE: 'park_and_recreation_area',\n RECREATION_AREA: 'park_and_recreation_area',\n WILDERNESS_AREA: 'park_and_recreation_area',\n OPEN_CAR_PARKING_AREA: 'parking_facility',\n DRUG_STORE: 'pharmacy',\n MARIJUANA_DISPENSARY: 'pharmacy',\n MEDICINAL_MARIJUANA_DISPENSARY: 'pharmacy',\n RECREATIONAL_MARIJUANA_DISPENSARY: 'pharmacy',\n COURIER_DROP_BOX: 'post_office',\n LOCAL_POST_OFFICE: 'post_office',\n PUBLIC_CALL_BOX: 'consumer_service',\n PUBLIC_TOILET: 'consumer_service',\n BUS_LINES: 'public_transport_stop',\n COACH_STOP: 'public_transport_stop',\n PASSENGER_TRANSPORT_TICKET_OFFICE: 'public_transport_stop',\n PEDESTRIAN_SUBWAY: 'public_transport_stop',\n STREETCAR_STOP: 'public_transport_stop',\n SUBWAY_STATION: 'public_transport_stop',\n INTERNATIONAL_RAILROAD_STATION: 'railway_station',\n NATIONAL_RAILROAD_STATION: 'railway_station',\n RAILROAD_SIDING: 'railway_station',\n STATION_ACCESS: 'railway_station',\n URBAN_STATION: 'railway_station',\n BODYSHOP: 'vehicle_repair',\n CAR_GLASS_REPLACEMENT_SHOP: 'vehicle_repair',\n CAR_REPAIR_AND_SERVICE: 'vehicle_repair',\n HOME_APPLIANCE_REPAIR: 'vehicle_repair',\n MOTORCYCLE_REPAIR: 'vehicle_repair',\n OTHER_REPAIR_SHOPS: 'vehicle_repair',\n REPAIR_SHOP: 'vehicle_repair',\n TIRE_SERVICE: 'vehicle_repair',\n TRUCK_REPAIR_AND_SERVICE: 'vehicle_repair',\n AFGHAN_RESTAURANT: 'restaurant',\n AFRICAN_RESTAURANT: 'restaurant',\n ALGERIAN_RESTAURANT: 'restaurant',\n AMERICAN_RESTAURANT: 'restaurant',\n ARABIAN_RESTAURANT: 'restaurant',\n ARGENTINIAN_RESTAURANT: 'restaurant',\n ARMENIAN_RESTAURANT: 'restaurant',\n ASIAN_RESTAURANT: 'restaurant',\n AUSTRALIAN_RESTAURANT: 'restaurant',\n AUSTRIAN_RESTAURANT: 'restaurant',\n BANQUET_ROOMS: 'restaurant',\n BARBECUE_RESTAURANT: 'restaurant',\n BASQUE_RESTAURANT: 'restaurant',\n BELGIAN_RESTAURANT: 'restaurant',\n BISTRO: 'restaurant',\n BOLIVIAN_RESTAURANT: 'restaurant',\n BOSNIAN_RESTAURANT: 'restaurant',\n BRAZILIAN_RESTAURANT: 'restaurant',\n BRITISH_RESTAURANT: 'restaurant',\n BUFFET_RESTAURANT: 'restaurant',\n BULGARIAN_RESTAURANT: 'restaurant',\n BURMESE_RESTAURANT: 'restaurant',\n CAFETERIA: 'restaurant',\n CALIFORNIAN_RESTAURANT: 'restaurant',\n CAMBODIAN_RESTAURANT: 'restaurant',\n CANADIAN_RESTAURANT: 'restaurant',\n CARIBBEAN_RESTAURANT: 'restaurant',\n CATERING_SERVICES: 'restaurant',\n CHICKEN_RESTAURANT: 'restaurant',\n CHILEAN_RESTAURANT: 'restaurant',\n CHINESE_RESTAURANT: 'restaurant',\n COLOMBIAN_RESTAURANT: 'restaurant',\n CORSICAN_RESTAURANT: 'restaurant',\n CREOLE_RESTAURANT: 'restaurant',\n CREPERIE: 'restaurant',\n CUBAN_RESTAURANT: 'restaurant',\n CYPRIOT_RESTAURANT: 'restaurant',\n CZECH_RESTAURANT: 'restaurant',\n DANISH_RESTAURANT: 'restaurant',\n DINNER_THEATER: 'restaurant',\n DOMINICAN_RESTAURANT: 'restaurant',\n DONGBEI_RESTAURANT: 'restaurant',\n DOUGHNUT_RESTAURANT: 'restaurant',\n DUTCH_RESTAURANT: 'restaurant',\n EGYPTIAN_RESTAURANT: 'restaurant',\n ENGLISH_RESTAURANT: 'restaurant',\n EROTIC_RESTAURANT: 'restaurant',\n ETHIOPIAN_RESTAURANT: 'restaurant',\n EXOTIC_RESTAURANT: 'restaurant',\n FAST_FOOD: 'fast_food',\n FINNISH_RESTAURANT: 'restaurant',\n FONDUE_RESTAURANT: 'restaurant',\n FRENCH_RESTAURANT: 'restaurant',\n FUSION_RESTAURANT: 'restaurant',\n GERMAN_RESTAURANT: 'restaurant',\n GREEK_RESTAURANT: 'restaurant',\n GRILL_RESTAURANT: 'restaurant',\n GUANGDONG_RESTAURANT: 'restaurant',\n HAMBURGER_RESTAURANT: 'restaurant',\n HAWAIIAN_RESTAURANT: 'restaurant',\n HOT_POT_RESTAURANT: 'restaurant',\n HUNAN_RESTAURANT: 'restaurant',\n HUNGARIAN_RESTAURANT: 'restaurant',\n ICE_CREAM_PARLOR: 'restaurant',\n INDIAN_RESTAURANT: 'restaurant',\n INDONESIAN_RESTAURANT: 'restaurant',\n INTERNATIONAL_RESTAURANT: 'restaurant',\n IRANIAN_RESTAURANT: 'restaurant',\n IRISH_RESTAURANT: 'restaurant',\n ISRAELI_RESTAURANT: 'restaurant',\n ITALIAN_RESTAURANT: 'restaurant',\n JAMAICAN_RESTAURANT: 'restaurant',\n JAPANESE_RESTAURANT: 'restaurant',\n JEWISH_RESTAURANT: 'restaurant',\n KOREAN_RESTAURANT: 'restaurant',\n KOSHER_RESTAURANT: 'restaurant',\n LATIN_AMERICAN_RESTAURANT: 'restaurant',\n LEBANESE_RESTAURANT: 'restaurant',\n LUXEMBOURGIAN_RESTAURANT: 'restaurant',\n MACROBIOTIC_RESTAURANT: 'restaurant',\n MAGHRIB_RESTAURANT: 'restaurant',\n MALTESE_RESTAURANT: 'restaurant',\n MAURITIAN_RESTAURANT: 'restaurant',\n MEDITERRANEAN_RESTAURANT: 'restaurant',\n MEXICAN_RESTAURANT: 'restaurant',\n MIDDLE_EASTERN_RESTAURANT: 'restaurant',\n MONGOLIAN_RESTAURANT: 'restaurant',\n MOROCCAN_RESTAURANT: 'restaurant',\n MUSSELS_RESTAURANT: 'restaurant',\n NEPALESE_RESTAURANT: 'restaurant',\n NORWEGIAN_RESTAURANT: 'restaurant',\n ORGANIC_FOOD_RESTAURANT: 'restaurant',\n ORIENTAL_RESTAURANT: 'restaurant',\n PAKISTANI_RESTAURANT: 'restaurant',\n PERUVIAN_RESTAURANT: 'restaurant',\n PHILIPPINE_RESTAURANT: 'restaurant',\n PIZZERIA: 'restaurant',\n POLISH_RESTAURANT: 'restaurant',\n POLYNESIAN_RESTAURANT: 'restaurant',\n PORTUGUESE_RESTAURANT: 'restaurant',\n PROVENCAL_RESTAURANT: 'restaurant',\n PUB_FOOD: 'restaurant',\n ROADSIDE_RESTAURANT: 'restaurant',\n ROMANIAN_RESTAURANT: 'restaurant',\n RUSSIAN_RESTAURANT: 'restaurant',\n SALAD_BAR: 'restaurant',\n SANDWICH_RESTAURANT: 'restaurant',\n SAVOY_RESTAURANT: 'restaurant',\n SCANDINAVIAN_RESTAURANT: 'restaurant',\n SCOTTISH_RESTAURANT: 'restaurant',\n SEAFOOD: 'restaurant',\n SHANDONG_RESTAURANT: 'restaurant',\n SHANGHAI_RESTAURANT: 'restaurant',\n SICHUAN_RESTAURANT: 'restaurant',\n SICILIAN_RESTAURANT: 'restaurant',\n SLAVIC_RESTAURANT: 'restaurant',\n SLOVAK_RESTAURANT: 'restaurant',\n SNACKS_RESTAURANT: 'restaurant',\n SOUL_FOOD: 'restaurant',\n SOUP_RESTAURANT: 'restaurant',\n SPANISH_RESTAURANT: 'restaurant',\n STEAK_HOUSE: 'restaurant',\n SUDANESE_RESTAURANT: 'restaurant',\n SURINAMESE_RESTAURANT: 'restaurant',\n SUSHI_RESTAURANT: 'restaurant',\n SWEDISH_RESTAURANT: 'restaurant',\n SWISS_RESTAURANT: 'restaurant',\n SYRIAN_RESTAURANT: 'restaurant',\n TAIWANESE_RESTAURANT: 'restaurant',\n TAKEOUT_FOOD: 'restaurant',\n TAPAS_RESTAURANT: 'restaurant',\n TEPPANYAKI_RESTAURANT: 'restaurant',\n THAI_RESTAURANT: 'restaurant',\n TIBETAN_RESTAURANT: 'restaurant',\n TUNISIAN_RESTAURANT: 'restaurant',\n TURKISH_RESTAURANT: 'restaurant',\n URUGUAYAN_RESTAURANT: 'restaurant',\n VEGETARIAN_RESTAURANT: 'restaurant',\n VENEZUELAN_RESTAURANT: 'restaurant',\n VIETNAMESE_RESTAURANT: 'restaurant',\n WELSH_RESTAURANT: 'restaurant',\n WESTERN_RESTAURANT: 'restaurant',\n YOGURT_JUICE_BAR: 'restaurant',\n ART_SCHOOL: 'school_or_kindergarten',\n CHILD_CARE_FACILITY: 'childcare',\n CULINARY_SCHOOL: 'school_or_kindergarten',\n DANCE_STUDIO_SCHOOL: 'school_or_kindergarten',\n DRIVING_SCHOOL: 'school_or_kindergarten',\n HIGH_SCHOOL: 'school_or_kindergarten',\n LANGUAGE_SCHOOL: 'school_or_kindergarten',\n MIDDLE_SCHOOL: 'school_or_kindergarten',\n PRE_SCHOOL: 'childcare',\n PRIMARY_SCHOOL: 'school_or_kindergarten',\n SENIOR_HIGH_SCHOOL: 'school_or_kindergarten',\n SPECIAL_SCHOOL: 'school_or_kindergarten',\n SPORT_SCHOOL: 'school_or_kindergarten',\n TECHNICAL_SCHOOL: 'school_or_kindergarten',\n VOCATIONAL_SCHOOL: 'school_or_kindergarten',\n AGRICULTURAL_SUPPLIES: 'shop_or_store',\n ANTIQUE_ART_SHOP: 'shop_or_store',\n BAGS_LEATHERWEAR: 'shop_or_store',\n BAKERY: 'shop_or_store',\n BEAUTY_SALON: 'personal_care_service',\n BEAUTY_SUPPLIES: 'shop_or_store',\n BOATING_EQUIPMENT_ACCESSORIES: 'shop_or_store',\n BOOK_SHOP: 'shop_or_store',\n BUTCHER: 'shop_or_store',\n CAMERAS_PHOTOGRAPHY: 'shop_or_store',\n CARPET_FLOOR_COVERINGS: 'shop_or_store',\n CAR_ACCESSORIES: 'shop_or_store',\n CHRISTMAS_HOLIDAY_SHOP: 'shop_or_store',\n COMPUTER_COMPUTER_SUPPLIES: 'shop_or_store',\n CONSTRUCTION_MATERIAL_EQUIPMENT: 'shop_or_store',\n CONSUMER_ELECTRONICS: 'shop_or_store',\n CONVENIENCE_STORE: 'shop_or_store',\n CURTAINS_TEXTILES: 'shop_or_store',\n C_DS_DVD_VIDEOS: 'shop_or_store',\n DELICATESSEN: 'shop_or_store',\n DO_IT_YOURSELF_CENTERS: 'shop_or_store',\n DRIVE_THROUGH_BOTTLE_SHOP: 'shop_or_store',\n DRY_CLEANER: 'shop_or_store',\n ELECTRICAL_APPLIANCES_SHOP: 'shop_or_store',\n FACTORY_OUTLET: 'shop_or_store',\n FISHMONGER: 'shop_or_store',\n FLORISTS: 'shop_or_store',\n FOOTWEAR_SHOE_REPAIRS: 'shop_or_store',\n FURNITURE_HOME_FURNISHINGS: 'shop_or_store',\n GARDEN_CENTERS_SERVICES: 'shop_or_store',\n GIFTS_CARDS_NOVELTIES_SOUVENIRS: 'shop_or_store',\n GLASSWARE_CERAMIC_SHOP: 'shop_or_store',\n GLASS_WINDOWS_STORE: 'shop_or_store',\n GREENGROCER: 'shop_or_store',\n GROCERY_STORE: 'shop_or_store',\n HAIRDRESSER: 'personal_care_service',\n HARDWARE_STORE: 'shop_or_store',\n HOBBY_SHOP: 'shop_or_store',\n HOUSE_GARDEN_FURNITURE_FITTINGS: 'shop_or_store',\n JEWELRY_CLOCKS_WATCHES: 'shop_or_store',\n KITCHENS_BATHROOMS: 'shop_or_store',\n LAUNDRY: 'shop_or_store',\n LIGHTING_SHOPS: 'shop_or_store',\n LOCAL_SPECIALITIES_SHOP: 'shop_or_store',\n LOTTERY_SHOP: 'shop_or_store',\n MARINE_ELECTRONIC_EQUIPMENT: 'shop_or_store',\n MEDICAL_SUPPLIES_EQUIPMENT: 'shop_or_store',\n MOBILE_PHONE_SHOP: 'shop_or_store',\n MUSIC_INSTRUMENTS_STORE: 'shop_or_store',\n NAIL_SALON: 'personal_care_service',\n NEWSAGENTS_TOBACCONISTS: 'shop_or_store',\n OFFICE_EQUIPMENT: 'shop_or_store',\n OPTICIAN: 'shop_or_store',\n OTHER_FOOD_SHOPS: 'shop_or_store',\n PAINTING_DECORATING: 'shop_or_store',\n PAWN_SHOP: 'shop_or_store',\n PERSONAL_CARE_FACILITY: 'personal_care_service',\n PERSONAL_SERVICE: 'personal_care_service',\n PET_SUPPLIES: 'shop_or_store',\n PHOTOCOPY_SHOP: 'shop_or_store',\n PHOTO_LAB_DEVELOPMENT: 'shop_or_store',\n RECYCLING_SHOP: 'shop_or_store',\n RETAIL_OUTLET: 'shop_or_store',\n SAUNA_SOLARIUM_MASSAGE: 'personal_care_service',\n SECURITY_PRODUCTS: 'shop_or_store',\n SHOPPING_SERVICE: 'shop_or_store',\n SPECIALTY_FOODS: 'shop_or_store',\n SPORTS_EQUIPMENT_CLOTHING: 'shop_or_store',\n STAMP_SHOP: 'shop_or_store',\n TAILOR_SHOP: 'shop_or_store',\n TOYS_GAMES_SHOP: 'shop_or_store',\n VARIETY_STORE: 'shop_or_store',\n VIDEO_RENTAL_SHOP: 'shop_or_store',\n WHOLESALE_CLUB: 'shop_or_store',\n WINE_SPIRITS: 'shop_or_store',\n ATHLETICS_TRACK: 'sport_facility',\n BASEBALL_PARK: 'sport_facility',\n BASKETBALL_ARENA: 'sport_facility',\n BOWLING_CENTER: 'sport_facility',\n CRICKET_GROUND: 'sport_facility',\n FITNESS_CLUB_CENTER: 'fitness_center',\n FLYING_CLUB: 'sport_facility',\n HOCKEY_CLUB: 'sport_facility',\n HORSE_RACING_TRACK: 'sport_facility',\n HORSE_RIDING_CENTER: 'sport_facility',\n ICE_HOCKEY_ARENA: 'ice_rink',\n OTHER_WINTER_SPORT: 'winter_sports',\n RACE_TRACK: 'sport_facility',\n RUGBY_GROUND: 'sport_facility',\n SKI_RESORT: 'winter_sports',\n SNOOKER_POOL_BILLIARD: 'sport_facility',\n THEMATIC_SPORT_CENTER: 'sport_facility',\n FOOTBALL_STADIUM: 'stadium',\n MOTOR_RACING_STADIUM: 'stadium',\n MULTI_PURPOSE_STADIUM: 'stadium',\n NETBALL_STADIUM: 'stadium',\n SOCCER_STADIUM: 'stadium',\n STOCK_EXCHANGE: 'bank',\n TAXI_LIMOUSINE_SHUTTLE_SERVICE: 'taxi_stand',\n AMPHITHEATER: 'theater',\n ARCH: 'landmark',\n BATTLEFIELD: 'historic_site',\n CAVE: 'tourist_attraction',\n CEMETERY: 'cemetery',\n HISTORIC_SITE: 'historic_site',\n MAUSOLEUM_GRAVE: 'cemetery',\n MEMORIAL: 'historic_site',\n MINERAL_HOT_SPRINGS: 'tourist_attraction',\n MONUMENT: 'landmark',\n NATURAL_TOURIST_ATTRACTION: 'tourist_attraction',\n OBSERVATORY: 'landmark',\n STATUE: 'landmark',\n TOURIST_ATTRACTION: 'tourist_attraction',\n TOWER: 'landmark',\n ROAD_TRAFFIC_CONTROL_CENTER: 'vehicle_inspection',\n ADVENTURE_VEHICLE_TRAIL: 'park_and_recreation_area',\n HIKING_TRAIL: 'park_and_recreation_area',\n HORSE_RIDING_TRAIL: 'park_and_recreation_area',\n MOUNTAIN_BIKE_TRAIL: 'park_and_recreation_area',\n ROCK_CLIMBING_TRAIL: 'park_and_recreation_area',\n APARTMENT_RENTAL: 'commercial_area',\n CONDOMINIUM_COMPLEX: 'commercial_area',\n FLATS_APARTMENT_COMPLEX: 'commercial_area',\n RESIDENTIAL_ESTATE: 'commercial_area',\n RETIREMENT_COMMUNITY: 'commercial_area',\n TOWNHOUSE_COMPLEX: 'commercial_area',\n ANIMAL_SERVICES: 'veterinary',\n ANIMAL_SHELTER: 'animal_service',\n WEIGH_SCALES: 'weighbridge',\n WILDLIFE_PARK: 'zoo_or_aquarium',\n ZOO: 'zoo_or_aquarium',\n} as const;\n\n/**\n * Map style POI category type.\n *\n * @remarks\n * Represents all available POI (Point of Interest) categories used in the map style.\n * These categories correspond to Search API classification codes and are used for\n * filtering, styling, and displaying POI icons on the map.\n *\n * Each category maps to a specific icon sprite and can be used with custom icon configurations.\n *\n * @example\n * ```ts\n * const category: MapStylePOICategory = 'RESTAURANT';\n * ```\n *\n * @see {@link POICategoryGroup} - For grouped collections of related categories\n *\n * @group POIs\n */\nexport type MapStylePOICategory = keyof typeof mapDisplayPoiCategoryMappings;\n\n/**\n * @ignore\n */\nexport const toBaseMapPOICategory = (category: POICategory): string => completeMapDisplayPoiCategoryMappings[category];\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * Bold font face used in the TomTom map style.\n *\n * @remarks\n * This is the primary bold font used for prominent labels and headings in the map.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_BOLD_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_BOLD_FONT],\n * 'text-field': ['get', 'name']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_BOLD_FONT = 'Noto-Bold';\n\n/**\n * Regular font face used in the TomTom map style.\n *\n * @remarks\n * This is the standard font used for most text labels on the map.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_REGULAR_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_REGULAR_FONT],\n * 'text-field': ['get', 'description']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_REGULAR_FONT = 'Noto-Regular';\n\n/**\n * Medium weight font face used in the TomTom map style.\n *\n * @remarks\n * This font provides a middle ground between regular and bold weights.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_MEDIUM_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_MEDIUM_FONT],\n * 'text-field': ['get', 'title']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_MEDIUM_FONT = 'Noto-Medium';\n\n/**\n * Italic font face used in the TomTom map style.\n *\n * @remarks\n * This is the italic variant used for emphasized or secondary text labels.\n * Use this constant when creating custom layers to maintain visual consistency with the map style.\n *\n * @example\n * ```typescript\n * import { MAP_ITALIC_FONT } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [MAP_ITALIC_FONT],\n * 'text-field': ['get', 'subtitle']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const MAP_ITALIC_FONT = 'NotoSans-MediumItalic';\n\n/**\n * Default text size expression used in the TomTom map style.\n *\n * @remarks\n * This MapLibre expression defines zoom-dependent text sizing that scales from 12px at zoom 10\n * to 14px at zoom 16. Use this for consistent text sizing across zoom levels.\n *\n * @example\n * ```typescript\n * import { DEFAULT_TEXT_SIZE } from '@tomtom-international/maps-sdk-js/map';\n *\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-size': DEFAULT_TEXT_SIZE,\n * 'text-field': ['get', 'name']\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const DEFAULT_TEXT_SIZE: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 10, 14, 18, 16];\n\n/**\n * Array of all available font faces in the TomTom map style.\n *\n * @remarks\n * Contains all font variants available in the map style. Useful for font selection\n * or validation when creating custom layers.\n *\n * @example\n * ```typescript\n * import { mapFonts } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Check if a font is available\n * if (mapFonts.includes('Noto-Bold')) {\n * // Use the font\n * }\n *\n * // Use as fallback list\n * const textLayer = {\n * type: 'symbol',\n * layout: {\n * 'text-font': [...mapFonts]\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport const mapFonts = [MAP_REGULAR_FONT, MAP_ITALIC_FONT, MAP_BOLD_FONT, MAP_MEDIUM_FONT] as const;\n\n/**\n * Type representing valid font faces available in the TomTom map style.\n *\n * @remarks\n * Use this type to ensure type-safe font selection when working with text layers.\n * Restricts values to only the fonts available in the map style.\n *\n * @example\n * ```typescript\n * import type { MapFont } from '@tomtom-international/maps-sdk-js/map';\n *\n * function createTextLayer(font: MapFont) {\n * return {\n * type: 'symbol',\n * layout: {\n * 'text-font': [font],\n * 'text-field': ['get', 'name']\n * }\n * };\n * }\n *\n * // Type-safe: only accepts valid map fonts\n * const layer = createTextLayer('Noto-Bold');\n * ```\n *\n * @group Map Style\n */\nexport type MapFont = (typeof mapFonts)[number];\n\n/**\n * Default pin icon scale at max zoom level (zoom 22).\n * This is the icon-size scale factor - 1.0 would be the original icon size.\n * @ignore\n */\nexport const DEFAULT_MAX_PIN_SCALE = 0.8;\n\n/**\n * @ignore\n */\nexport const PIN_ICON_SIZE: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 8,\n 0.6,\n 22,\n DEFAULT_MAX_PIN_SCALE,\n];\n\n/**\n * @ignore\n */\nexport const SELECTED_PIN_ICON_SIZE: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 8, 0.8, 22, 1];\n","import { SymbolLayerSpecification } from 'maplibre-gl';\nimport { LayerSpecTemplate } from '../types';\nimport { DEFAULT_TEXT_SIZE, MAP_BOLD_FONT, PIN_ICON_SIZE } from './commonLayerProps';\n\n/**\n * @ignore\n */\nexport const TITLE = 'title';\n\n/**\n * @ignore\n */\nexport const ICON_ID = 'iconID';\n\n/**\n * Y-offset multiplier for text positioned below icons (top anchor).\n * In ems, relative to icon scale.\n * @ignore\n */\nexport const DEFAULT_TEXT_OFFSET_Y = 0.7;\n\n/**\n * X-offset multiplier for text positioned beside icons (left/right anchors).\n * In ems, relative to icon scale.\n * @ignore\n */\nexport const DEFAULT_TEXT_OFFSET_X = 1.4;\n\n/**\n * @ignore\n */\nexport const DEFAULT_PLACE_ICON_ID = 'default_place';\n\n/**\n * @ignore\n */\nexport const pinIconBaseLayout: SymbolLayerSpecification['layout'] = {\n 'icon-image': ['get', ICON_ID],\n 'icon-anchor': 'bottom',\n 'icon-size': PIN_ICON_SIZE,\n 'icon-allow-overlap': true,\n 'icon-padding': 0,\n};\n\n/**\n * @ignore\n */\nexport const pinIconBasePaint: SymbolLayerSpecification['paint'] = {\n 'icon-translate': [0, 5],\n 'icon-translate-anchor': 'viewport',\n};\n\n/**\n * @ignore\n */\nexport const pinTextBaseLayout: SymbolLayerSpecification['layout'] = {\n 'text-optional': true,\n 'text-font': [MAP_BOLD_FONT],\n 'text-field': ['get', TITLE],\n 'text-justify': 'auto',\n 'text-variable-anchor': ['top', 'left', 'right'],\n // NOTE: make sure to text against pins and waypoints, in a way that there's enough distance from the pin so the text doesn't disappear\n 'text-variable-anchor-offset': [\n 'top',\n [0, DEFAULT_TEXT_OFFSET_Y],\n 'left',\n [DEFAULT_TEXT_OFFSET_X, -DEFAULT_TEXT_OFFSET_X],\n 'right',\n [-DEFAULT_TEXT_OFFSET_X, -DEFAULT_TEXT_OFFSET_X],\n ],\n 'text-size': DEFAULT_TEXT_SIZE,\n 'text-padding': 5,\n};\n\n/**\n * @ignore\n */\nexport const pinTextBasePaint: SymbolLayerSpecification['paint'] = {\n 'text-color': '#333333',\n 'text-halo-color': '#FFFFFF',\n 'text-halo-width': ['interpolate', ['linear'], ['zoom'], 6, 1, 10, 1.5],\n 'text-translate-anchor': 'viewport',\n};\n\n/**\n * Pin places, base layer with mostly the icon portion.\n * @ignore\n */\nexport const pinLayerBaseSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n layout: { ...pinIconBaseLayout, ...pinTextBaseLayout },\n paint: { ...pinIconBasePaint, ...pinTextBasePaint },\n};\n","/**\n * Returns a text with a number suffixed after a hyphen.\n * @ignore\n */\nexport const suffixNumber = (text: string, numberToSuffix: number): string => `${text}-${numberToSuffix}`;\n","import { suffixNumber } from '../../shared/layers/utils';\nimport { parseSvg } from '../../shared/resources';\nimport type { PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\n\n/**\n * Map of icon IDs to their text offset scale factors.\n * @ignore\n */\nexport type IconScalesMap = Map<string, { heightScale: number; widthScale: number }>;\n\n/**\n * Default pin dimensions (from base pin.svg)\n */\nconst DEFAULT_PIN_HEIGHT_PX = 140;\nconst DEFAULT_PIN_WIDTH_PX = 120;\n\n/**\n * Default base-map POI icon dimensions (from base-theme POI icons)\n */\nconst DEFAULT_MAP_POI_HEIGHT_PX = 54;\nconst DEFAULT_MAP_POI_WIDTH_PX = 54;\n\n/**\n * Extracts dimensions from an SVG string or HTMLImageElement.\n * For SVGs, parses the viewBox or width/height attributes.\n * For images, uses naturalWidth/naturalHeight.\n * @ignore\n */\nexport const extractImageDimensions = (image: string | HTMLImageElement): { width: number; height: number } | null => {\n try {\n if (typeof image === 'string') {\n if (image.includes('<svg')) {\n // Parse SVG to extract dimensions\n const svgElement = parseSvg(image);\n\n // Try viewBox first (format: \"minX minY width height\")\n const viewBox = svgElement.getAttribute('viewBox');\n if (viewBox) {\n const parts = viewBox.split(/\\s+/);\n if (parts.length === 4) {\n return {\n width: Number.parseFloat(parts[2]),\n height: Number.parseFloat(parts[3]),\n };\n }\n }\n\n // Fallback to width/height attributes\n const width = svgElement.getAttribute('width');\n const height = svgElement.getAttribute('height');\n if (width && height) {\n return {\n width: Number.parseFloat(width),\n height: Number.parseFloat(height),\n };\n }\n }\n // For URL strings, we can't extract dimensions synchronously\n return null;\n } else {\n // HTMLImageElement - check if loaded\n if (image.complete && image.naturalWidth > 0) {\n return {\n width: image.naturalWidth,\n height: image.naturalHeight,\n };\n }\n return null;\n }\n } catch (error) {\n console.warn('Failed to extract image dimensions:', error);\n return null;\n }\n};\n\n/**\n * Calculate the scale factors for a custom icon based on its dimensions relative to standard icon sizes.\n *\n * Calculates both height and width scales independently:\n * - Height scale is used for vertical text offset\n * - Width scale is used for horizontal text offset\n *\n *\n * @param image The image to extract dimensions from\n * @param theme The places theme ('base-map' for circles, 'pin' for pins)\n * @returns Object with heightScale and widthScale, or undefined if icon is standard-sized (within tolerance)\n * @ignore\n */\nexport const calculateIconScale = (\n image: string | HTMLImageElement | undefined,\n theme?: PlacesTheme,\n): { heightScale: number; widthScale: number } | undefined => {\n if (!image) {\n return undefined;\n }\n\n const dimensions = extractImageDimensions(image);\n if (!dimensions) {\n return undefined;\n }\n\n const isBaseMapTheme = theme === 'base-map';\n\n // For base-map theme, we need to scale relative to POI icons\n if (isBaseMapTheme) {\n const heightScale = dimensions.height / DEFAULT_MAP_POI_HEIGHT_PX;\n const widthScale = dimensions.width / DEFAULT_MAP_POI_WIDTH_PX;\n\n return { heightScale, widthScale };\n } else {\n // For pin theme, check if it's different from default pin\n const heightScale = dimensions.height / DEFAULT_PIN_HEIGHT_PX;\n const widthScale = dimensions.width / DEFAULT_PIN_WIDTH_PX;\n\n return { heightScale, widthScale };\n }\n};\n\n/**\n * Builds a map of icon IDs to their text offset scales for custom icons.\n * @ignore\n */\nexport const buildCustomIconScalesMap = (\n config: PlacesModuleConfig | undefined,\n instanceIndex: number,\n): IconScalesMap => {\n const iconTextOffsetScales = new Map<string, { heightScale: number; widthScale: number }>();\n const customIcons = config?.icon?.categoryIcons ?? [];\n\n for (const icon of customIcons) {\n if (icon.image) {\n const scales = calculateIconScale(icon.image, config?.theme);\n if (scales !== undefined) {\n // Base icon ID with instance suffix\n const suffixedIconId = suffixNumber(icon.id, instanceIndex);\n iconTextOffsetScales.set(suffixedIconId, scales);\n\n // If this icon has an availability level, also add the availability-suffixed version\n // (e.g., \"ELECTRIC_VEHICLE_STATION-available-0\")\n if (icon.availabilityLevel) {\n const availabilitySuffixedId = suffixNumber(`${icon.id}-${icon.availabilityLevel}`, instanceIndex);\n iconTextOffsetScales.set(availabilitySuffixedId, scales);\n }\n }\n }\n }\n\n return iconTextOffsetScales;\n};\n","import type { ChargingPark, ChargingParkWithAvailability, Place, POICategory } from '@tomtom-org/maps-sdk/core';\nimport type { ExpressionSpecification } from 'maplibre-gl';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport type { AvailabilityLevel } from '../../shared/types/image';\nimport type { EVAvailabilityConfig, PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\n\n/**\n * Type guard to check if a charging park has availability data.\n * @ignore\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n\n/**\n * Check if a place is an EV charging station with availability data.\n * @ignore\n */\nexport const isEVStationWithAvailability = (place: Place): boolean => {\n const category = place.properties.poi?.classifications?.[0]?.code;\n return category === 'ELECTRIC_VEHICLE_STATION' && hasChargingAvailability(place.properties.chargingPark);\n};\n\n/**\n * Calculate charging point availability information.\n * @ignore\n */\nexport const getChargingPointAvailability = (\n place: Place,\n): { availableCount: number; totalCount: number; ratio: number } | undefined => {\n const chargingPark = place.properties.chargingPark;\n if (hasChargingAvailability(chargingPark)) {\n const availability = chargingPark.availability.chargingPointAvailability;\n const available = availability.statusCounts.Available ?? 0;\n return {\n availableCount: available,\n totalCount: availability.count,\n ratio: available / availability.count,\n };\n }\n return undefined;\n};\n\n/**\n * Default threshold for EV availability - available vs occupied.\n * @ignore\n */\nexport const DEFAULT_EV_AVAILABILITY_THRESHOLD = 0;\n\n/**\n * Default formatter for EV availability text.\n * @ignore\n */\nexport const defaultFormatAvailabilityText = (available: number, total: number): string => `${available}/${total}`;\n\n/**\n * Build availability text for a place with EV availability data.\n * @ignore\n */\nexport const buildAvailabilityText = (place: Place, config?: EVAvailabilityConfig): string => {\n const availability = getChargingPointAvailability(place);\n if (!availability) {\n return '';\n }\n\n const formatFn = config?.formatText ?? defaultFormatAvailabilityText;\n return formatFn(availability.availableCount, availability.totalCount);\n};\n\n/**\n * Build availability ratio for a place with EV availability data.\n * @ignore\n */\nexport const getAvailabilityRatio = (place: Place): number => {\n const availability = getChargingPointAvailability(place);\n return availability?.ratio ?? 0;\n};\n\n/**\n * Get the color expression for EV availability based on ratio.\n * @ignore\n */\nexport const getAvailabilityColorExpression = (config?: EVAvailabilityConfig): ExpressionSpecification => {\n const threshold = config?.threshold ?? DEFAULT_EV_AVAILABILITY_THRESHOLD;\n\n return ['case', ['>=', ['get', 'evAvailabilityRatio'], threshold], 'green', 'red'] as ExpressionSpecification;\n};\n\n/**\n * Handles icon selection for EV stations with availability data.\n * Returns icon ID if availability-specific icon should be used, or undefined to fall through to regular selection.\n * @ignore\n */\nexport const getEVAvailabilityIconID = (\n place: Place,\n poiCategory: POICategory,\n instanceIndex: number,\n config: PlacesModuleConfig,\n iconTheme: PlacesTheme,\n): string | undefined => {\n if (!config.evAvailability?.enabled || !isEVStationWithAvailability(place)) {\n return undefined;\n }\n\n const ratio = getAvailabilityRatio(place);\n const threshold = config.evAvailability.threshold ?? 0.3;\n const requiredLevel: AvailabilityLevel = ratio >= threshold ? 'available' : 'occupied';\n const hasCustomIcons = config.icon?.categoryIcons && config.icon.categoryIcons.length > 0;\n\n const customIconWithAvailability = config.icon?.categoryIcons?.find(\n (customIcon) => customIcon.id === poiCategory && customIcon.availabilityLevel === requiredLevel,\n );\n\n // If a custom icon with the required availability level exists, use it\n if (customIconWithAvailability) {\n return suffixNumber(`${customIconWithAvailability.id}-${requiredLevel}`, instanceIndex);\n }\n\n // For pin theme: use CDN availability sprites when no custom icons are defined\n if (!hasCustomIcons && iconTheme === 'pin') {\n return `7309-${requiredLevel}`;\n }\n\n // Otherwise, fall through to regular icon selection\n return undefined;\n};\n","import type {\n DataDrivenPropertyValueSpecification,\n ExpressionSpecification,\n SymbolLayerSpecification,\n} from 'maplibre-gl';\nimport { DEFAULT_MAX_PIN_SCALE } from '../../shared/layers/commonLayerProps';\nimport { DEFAULT_TEXT_OFFSET_X, DEFAULT_TEXT_OFFSET_Y, ICON_ID } from '../../shared/layers/symbolLayers';\nimport type { PlacesTheme } from '../types/placesModuleConfig';\n\ntype VariableAnchorOffset = NonNullable<SymbolLayerSpecification['layout']>['text-variable-anchor-offset'];\n\n/**\n * Extracts the maximum icon scale factor from an icon-size expression.\n * For interpolate expressions, returns the last value (max zoom scale).\n * @ignore\n */\nconst extractMaxIconScale = (expression: DataDrivenPropertyValueSpecification<number> | undefined): number => {\n if (!expression) {\n return DEFAULT_MAX_PIN_SCALE;\n }\n\n if (typeof expression === 'number') {\n return expression;\n }\n\n if (Array.isArray(expression)) {\n const lastValue = expression.at(-1);\n if (typeof lastValue === 'number') {\n return lastValue;\n }\n }\n\n return DEFAULT_MAX_PIN_SCALE;\n};\n\n/**\n * Layout properties for text offset - can be spread directly into layer layout.\n */\ntype TextOffsetLayout = {\n 'text-offset'?: [number, number];\n 'text-variable-anchor-offset'?: VariableAnchorOffset;\n};\n\n/**\n * Builds anchor offsets for variable-anchor-offset property.\n * @ignore\n */\nconst buildAnchorOffsets = (\n topOffset: number,\n sideOffset: number,\n pinVerticalAdjustment: number,\n customTextOffset?: number,\n): { top: [number, number]; left: [number, number]; right: [number, number] } => {\n const hasCustomOffset = customTextOffset !== undefined;\n // For pin theme with custom offset, override only the primary direction for each anchor\n // top anchor → custom offset applies to vertical, left/right → custom offset applies to horizontal\n return {\n top: hasCustomOffset ? [0, customTextOffset] : [0, topOffset],\n left: hasCustomOffset ? [customTextOffset, pinVerticalAdjustment] : [sideOffset, pinVerticalAdjustment],\n right: hasCustomOffset ? [-customTextOffset, pinVerticalAdjustment] : [-sideOffset, pinVerticalAdjustment],\n };\n};\n\n/**\n * Calculates text offset for place labels\n *\n * @param iconSizeExpression The icon-size property from the layer specification\n * @param iconTextOffsetScales Map of icon IDs to their scale factors (heightScale for vertical, widthScale for horizontal)\n * @param theme The places theme ('base-map' for circles, 'pin' for pins)\n * @param customTextOffset Custom text offset multiplier (overrides default TEXT_OFFSET constants)\n * @returns Configuration object with the MapLibre property type and value to apply\n * @ignore\n */\nexport const getTextOffset = (\n iconSizeExpression: DataDrivenPropertyValueSpecification<number> | undefined,\n iconTextOffsetScales: Map<string, { heightScale: number; widthScale: number }>,\n theme?: PlacesTheme,\n customTextOffset?: number,\n): TextOffsetLayout => {\n const maxIconScale = extractMaxIconScale(iconSizeExpression);\n const iconScaleMultiplier = maxIconScale / DEFAULT_MAX_PIN_SCALE;\n const isBaseMapTheme = theme === 'base-map';\n const hasCustomOffset = customTextOffset !== undefined;\n\n // For base-map theme with custom offset, use simple text-offset (circles are centered)\n if (isBaseMapTheme && hasCustomOffset) {\n return {\n 'text-offset': [customTextOffset, customTextOffset],\n };\n }\n\n // Calculate fallback offsets (used when no custom icons or as default case)\n const fallbackTopOffset = DEFAULT_TEXT_OFFSET_Y * iconScaleMultiplier;\n const fallbackSideOffset = DEFAULT_TEXT_OFFSET_X * iconScaleMultiplier;\n const fallbackPinVerticalAdjustment = isBaseMapTheme ? 0 : -fallbackSideOffset;\n const fallbackOffsets = buildAnchorOffsets(\n fallbackTopOffset,\n fallbackSideOffset,\n fallbackPinVerticalAdjustment,\n customTextOffset,\n );\n const fallbackAnchorOffset = [\n 'top',\n fallbackOffsets.top,\n 'left',\n fallbackOffsets.left,\n 'right',\n fallbackOffsets.right,\n ];\n\n // No custom icons - return literal value directly (no case expression needed)\n if (iconTextOffsetScales.size === 0) {\n return {\n 'text-variable-anchor-offset': fallbackAnchorOffset as VariableAnchorOffset,\n };\n }\n\n // Build case expression for custom icons\n const offsetCaseExpression: (string | number | ExpressionSpecification)[] = ['case'];\n\n for (const [iconId, scales] of iconTextOffsetScales.entries()) {\n // Base-map POI layer uses larger vertical offsets than pin theme to match native map styling\n const baseTopOffset = isBaseMapTheme ? DEFAULT_TEXT_OFFSET_Y * 2 : DEFAULT_TEXT_OFFSET_Y;\n const topOffset = baseTopOffset * scales.heightScale;\n const sideOffset = DEFAULT_TEXT_OFFSET_X * scales.widthScale;\n\n // Base-map theme uses circles (centered) → no vertical adjustment for side anchors\n // Pin theme uses pins (bottom-anchored) → shift labels upward to align with visual center\n const pinVerticalAdjustment = isBaseMapTheme ? 0 : -sideOffset;\n\n const offsets = buildAnchorOffsets(topOffset, sideOffset, pinVerticalAdjustment, customTextOffset);\n offsetCaseExpression.push(\n ['==', ['get', ICON_ID], iconId],\n ['literal', ['top', offsets.top, 'left', offsets.left, 'right', offsets.right]],\n );\n }\n\n // Add fallback for icons not in the custom scales map\n offsetCaseExpression.push(['literal', fallbackAnchorOffset]);\n\n return {\n 'text-variable-anchor-offset': offsetCaseExpression as VariableAnchorOffset,\n };\n};\n","import type { DataDrivenPropertyValueSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { TITLE } from '../../shared/layers/symbolLayers';\nimport type { LightDark } from '../../shared/types/style';\nimport type { PlaceLayerName, PlacesModuleConfig } from '../types/placesModuleConfig';\nimport type { IconScalesMap } from './customIconScales';\nimport { getAvailabilityColorExpression } from './evAvailabilityHelpers';\nimport { getTextOffset } from './textOffsetCalculator';\nimport { getThemeAdaptiveTextColors } from './themeAdaptation';\n\n/**\n * Builds the text field expression for place labels\n * Supports EV availability text when enabled.\n * @ignore\n */\nexport const buildTextFieldExpression = (\n config: PlacesModuleConfig | undefined,\n evAvailabilityEnabled: boolean,\n): DataDrivenPropertyValueSpecification<string> => {\n if (!evAvailabilityEnabled) {\n return ['get', TITLE];\n }\n\n return [\n 'case',\n ['has', 'evAvailabilityText'],\n // If has EV availability, show two-line format with colored availability\n [\n 'format',\n ['get', TITLE],\n {},\n '\\n',\n {},\n ['get', 'evAvailabilityText'],\n {\n 'font-scale': 1.1,\n 'text-color': getAvailabilityColorExpression(config?.evAvailability),\n },\n ],\n // Otherwise, show normal title\n ['get', TITLE],\n ];\n};\n\n/**\n * Builds the layout configuration\n * @ignore\n */\nexport const buildLayoutConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n textField: DataDrivenPropertyValueSpecification<string>,\n iconTextOffsetScales?: IconScalesMap,\n): SymbolLayerSpecification['layout'] => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const hasCustomIcons = iconTextOffsetScales && iconTextOffsetScales.size > 0;\n\n // Start with base layout\n const baseLayout = { ...layerSpec.layout };\n\n // Remove offset properties we'll replace\n if (hasCustomIcons || textConfig?.offset !== undefined) {\n delete baseLayout['text-offset'];\n delete baseLayout['text-variable-anchor-offset'];\n delete baseLayout['text-radial-offset'];\n }\n\n const layout = {\n ...baseLayout,\n ...customLayer?.layout,\n ...(textConfig?.size && { 'text-size': textConfig.size }),\n ...(textConfig?.font && { 'text-font': textConfig.font }),\n 'text-field': textField,\n };\n\n // Apply offset configuration\n if (hasCustomIcons || textConfig?.offset !== undefined) {\n // Dynamic offset calculation handles custom icons and/or custom offset\n // For pin theme, this ensures proper vertical adjustments for left/right anchors\n const iconSize = layerSpec.layout?.['icon-size'];\n const scales = iconTextOffsetScales ?? new Map();\n Object.assign(layout, getTextOffset(iconSize, scales, config?.theme, textConfig?.offset));\n }\n\n return layout;\n};\n\n/**\n * Builds the paint configuration with theme-adaptive colors.\n * @ignore\n */\nexport const buildPaintConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n lightDark: LightDark,\n): SymbolLayerSpecification['paint'] => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const { textColor: baseTextColor, haloColor: baseHaloColor } = getThemeAdaptiveTextColors(lightDark);\n return {\n ...layerSpec.paint,\n // Apply theme-adaptive colors as defaults\n ...(!textConfig?.color && { 'text-color': baseTextColor }),\n ...(!textConfig?.haloColor && { 'text-halo-color': baseHaloColor }),\n // User config takes precedence\n ...(textConfig?.color && { 'text-color': textConfig.color }),\n ...(textConfig?.haloColor && { 'text-halo-color': textConfig.haloColor }),\n ...(textConfig?.haloWidth && { 'text-halo-width': textConfig.haloWidth }),\n ...customLayer?.paint,\n };\n};\n","import { LightDark } from '../../shared/types/style';\n/**\n * Calculates theme-adaptive text colors based on light/dark mode.\n * @param lightDark Whether the current theme is light or dark\n * @returns Object with text and halo colors appropriate for the theme\n * @ignore\n *\n * TODO: Should these colors adapt to the POI context colors,\n * or should they remain as fixed defaults?\n */\nexport const getThemeAdaptiveTextColors = (\n lightDark: LightDark,\n): {\n textColor: string;\n haloColor: string;\n} => {\n return {\n textColor: lightDark === 'dark' ? '#FFFFFF' : '#333333',\n haloColor: lightDark === 'dark' ? '#333333' : '#FFFFFF',\n };\n};\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * @ignore\n */\nexport const isClickEventState: ExpressionSpecification = [\n 'in',\n ['get', 'eventState'],\n ['literal', ['click', 'contextmenu']],\n];\n","import type { DataDrivenPropertyValueSpecification, Map as MapLibreMap, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { isClickEventState } from '../../shared/layers/eventState';\nimport { ICON_ID, TITLE } from '../../shared/layers/symbolLayers';\n\n/**\n * Replaces placeholders in text size spec with the actual title property.\n * @ignore\n */\nexport const getTextSizeSpec = (\n textSize?: DataDrivenPropertyValueSpecification<number>,\n): DataDrivenPropertyValueSpecification<number> => {\n return JSON.parse(JSON.stringify(textSize)?.replaceAll('name', TITLE));\n};\n\n/**\n * Builds a POI-like layer spec that matches the base map style.\n * @ignore\n */\nexport const buildPoiLikeLayerSpec = (map: MapLibreMap): LayerSpecTemplate<SymbolLayerSpecification> => {\n const poiLayer = (map.getStyle().layers.find((layer) => layer.id === 'POI') as SymbolLayerSpecification) || {};\n const textSize = poiLayer.layout?.['text-size'];\n return {\n filter: ['!', isClickEventState],\n type: 'symbol',\n paint: poiLayer.paint,\n layout: {\n ...poiLayer.layout,\n 'text-field': ['get', TITLE],\n 'icon-image': ['get', ICON_ID],\n ...(textSize && { 'text-size': getTextSizeSpec(textSize) }),\n },\n };\n};\n","import type { ExpressionSpecification, Map as MapLibreMap, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate, LightDark } from '../../shared';\nimport { SELECTED_PIN_ICON_SIZE } from '../../shared/layers/commonLayerProps';\nimport { pinLayerBaseSpec } from '../../shared/layers/symbolLayers';\nimport type { PlaceLayerName, PlaceLayersConfig, PlacesModuleConfig } from '../types/placesModuleConfig';\nimport { buildCustomIconScalesMap, type IconScalesMap } from '../utils/customIconScales';\nimport { buildLayoutConfig, buildPaintConfig, buildTextFieldExpression } from '../utils/layerConfiguration';\nimport { buildPoiLikeLayerSpec } from '../utils/layerSpecBuilders';\n\n/**\n * @ignore\n */\nexport const hasEventState: ExpressionSpecification = ['has', 'eventState'];\n\n/**\n * @ignore\n */\nexport const SELECTED_COLOR = '#3f9cd9';\n\n/**\n * @ignore\n */\nexport const pinLayerSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...pinLayerBaseSpec,\n filter: ['!', hasEventState],\n};\n\n/**\n * We use an extra layer for highlighted text since it's not easy to enforce z-ordering with icons and text\n * while text has different collision rules.\n * @ignore\n */\nexport const selectedPinLayerSpec: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...pinLayerBaseSpec,\n filter: hasEventState,\n layout: {\n ...pinLayerBaseSpec.layout,\n 'icon-size': SELECTED_PIN_ICON_SIZE,\n 'text-allow-overlap': true,\n },\n paint: {\n ...pinLayerBaseSpec.paint,\n 'text-color': SELECTED_COLOR,\n },\n};\n\n/**\n * Applies configuration to a layer specification template.\n * @ignore\n */\nconst withConfig = (\n layerSpec: LayerSpecTemplate<SymbolLayerSpecification>,\n config: PlacesModuleConfig | undefined,\n layerName: PlaceLayerName,\n lightDark: LightDark,\n iconTextOffsetScales?: IconScalesMap,\n): LayerSpecTemplate<SymbolLayerSpecification> => {\n const textConfig = config?.text;\n const customLayer = config?.layers?.[layerName];\n const evAvailabilityEnabled = config?.evAvailability?.enabled === true;\n\n const textFieldExpression = buildTextFieldExpression(config, evAvailabilityEnabled);\n const textField =\n textConfig?.title && typeof textConfig?.title !== 'function' ? textConfig.title : textFieldExpression;\n\n return {\n ...layerSpec,\n layout: buildLayoutConfig(layerSpec, config, layerName, textField, iconTextOffsetScales),\n paint: buildPaintConfig(layerSpec, config, layerName, lightDark),\n ...customLayer,\n };\n};\n\n/**\n * Builds layer specifications for places display.\n * @ignore\n */\nexport const buildPlacesLayerSpecs = (\n config: PlacesModuleConfig | undefined,\n mapLibreMap: MapLibreMap,\n styleLightDarkTheme: LightDark,\n instanceIndex: number,\n): PlaceLayersConfig => {\n const iconTextOffsetScales = buildCustomIconScalesMap(config, instanceIndex);\n\n let main: LayerSpecTemplate<SymbolLayerSpecification>;\n let selected: LayerSpecTemplate<SymbolLayerSpecification>;\n\n if (config?.theme === 'base-map') {\n const poiLikeLayerSpec = buildPoiLikeLayerSpec(mapLibreMap);\n main = poiLikeLayerSpec;\n selected = {\n ...poiLikeLayerSpec,\n filter: hasEventState,\n layout: {\n ...poiLikeLayerSpec.layout,\n 'text-allow-overlap': true,\n },\n paint: {\n ...poiLikeLayerSpec.paint,\n 'text-color': SELECTED_COLOR,\n },\n };\n } else {\n // pin / circle\n main = pinLayerSpec;\n selected = selectedPinLayerSpec;\n }\n\n return {\n main: withConfig(main, config, 'main', styleLightDarkTheme, iconTextOffsetScales),\n selected: withConfig(selected, config, 'selected', styleLightDarkTheme, iconTextOffsetScales),\n ...config?.layers?.additional,\n };\n};\n","import { SVGIconStyleOptions } from '../../shared';\nimport { isDOMImageSupported, svgToImg } from '../../shared/imageUtils';\nimport { pinSvg } from '../../shared/resources';\n\n/**\n * Default pin for selected images without a specific category on it.\n * @ignore\n */\nexport const defaultPin = (options?: SVGIconStyleOptions): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return svgToImg(pinSvg(options));\n};\n","// Supported sub-categories for map display pins\n// See: https://github.com/tomtom-internal/mdt-backend-mapbox-gl-js-styles/blob/orbis-preview/src/orbis/sprites/poi_light/config.json\n// For the rest we'll use the main categories (which are the first 4 digits of a category)\nconst supportedPinSubcategories: Set<number> = new Set([\n 7339002, 8099002, 7369004, 7321003, 7376006, 9362003, 9362004, 9160004, 9376007, 7315015, 9376006, 9376002, 7315078,\n 7315149, 9376005, 7372003, 9352045, 9352032, 9361048, 9902003, 9378005, 7383004, 9910004, 7320002, 9362016, 7320003,\n 9362025, 9942002, 9942003, 7380005, 9663003, 9361021, 9379009, 9379004, 7315147, 9376003, 9160002, 9160003, 9352008,\n 9361006, 7389004, 9910006,\n]);\n\n/**\n * @ignore\n */\nexport const toPinImageID = (categoryID: number | undefined): string | undefined => {\n if (!categoryID) {\n return undefined;\n }\n\n // Check if the category ID is in our supported subcategories:\n if (supportedPinSubcategories.has(categoryID)) {\n return categoryID.toString();\n }\n\n // If not, fall back to the main category (first 4 digits of the category ID):\n return categoryID.toString().substring(0, 4);\n};\n","import { generateId, Place, Places, POICategory, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { toBaseMapPOICategory } from '../../pois/util/poiCategoryMapping';\nimport { DEFAULT_PLACE_ICON_ID } from '../../shared/layers/symbolLayers';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport type { DisplayPlaceProps } from '../types/placeDisplayProps';\nimport type { PlacesModuleConfig, PlacesTheme } from '../types/placesModuleConfig';\nimport {\n buildAvailabilityText,\n getAvailabilityRatio,\n getEVAvailabilityIconID,\n isEVStationWithAvailability,\n} from './evAvailabilityHelpers';\nimport { toPinImageID } from './toPinImageID';\n\n/**\n * Builds the title of the place to display it on the map.\n * @param place The place to display.\n * @ignore\n */\nexport const buildPlaceTitle = (place: Place): string =>\n place.properties.poi?.name ?? place.properties.address.freeformAddress;\n\n/**\n * Resolves the image ID to use for the given POI category and icon theme.\n */\nconst toImageID = (poiCategory: POICategory, iconTheme: PlacesTheme, defaultPlaceIconID: string): string => {\n if (iconTheme === 'pin') {\n const imageID = toPinImageID(poiCategoriesToID[poiCategory]);\n return imageID ?? defaultPlaceIconID;\n } else {\n const imageID = toBaseMapPOICategory(poiCategory);\n return imageID ? `poi-${imageID}` : defaultPlaceIconID;\n }\n};\n\n/**\n * Gets the map style sprite image ID to display on the map for the give place.\n * @ignore\n */\nexport const getIconIDForPlace = (place: Place, instanceIndex: number, config: PlacesModuleConfig = {}): string => {\n const iconTheme = config.theme ?? 'pin';\n const defaultPlaceIconID = suffixNumber(DEFAULT_PLACE_ICON_ID, instanceIndex);\n\n const imageMapping = config.icon?.mapping;\n // First, try custom mapping if provided:\n if (imageMapping) {\n if (imageMapping.to === 'imageID') {\n // Direct image ID mapping\n return imageMapping.fn(place);\n } else {\n // POI category mapping - resolve category to icon ID\n return toImageID(imageMapping.fn(place), iconTheme, defaultPlaceIconID);\n }\n }\n\n // Next, try to match any custom icon:\n const poiCategory = place.properties.poi?.classifications?.[0]?.code as POICategory;\n\n // Check for EV availability-specific icon selection\n const evAvailabilityIconID = getEVAvailabilityIconID(place, poiCategory, instanceIndex, config, iconTheme);\n if (evAvailabilityIconID) {\n return evAvailabilityIconID;\n }\n\n // Regular custom icon matching (no availability)\n const matchingCustomIcon = config.icon?.categoryIcons?.find((customIcon) => customIcon.id === poiCategory);\n if (matchingCustomIcon) {\n return suffixNumber(matchingCustomIcon.id, instanceIndex);\n }\n\n // Else: if no custom icon matched, we map to the map style icons or default:\n const baseIconID = toImageID(poiCategory, iconTheme, defaultPlaceIconID);\n return baseIconID;\n};\n\n/**\n * Maps a Place category to the poi layer one, so the latter's style can apply it.\n * @ignore\n */\nexport const getPOILayerCategoryForPlace = (place: Place): string | undefined => {\n const category = place.properties.poi?.classifications?.[0]?.code;\n // if it's one of the different categories between search and poi layer, use poi layer category\n return category && toBaseMapPOICategory(category);\n};\n\n/**\n * Transforms the input of a \"show\" call to FeatureCollection \"Places\".\n * @ignore\n */\nexport const toPlaces = (places: Place | Place[] | Places): Places => {\n if (Array.isArray(places)) {\n return { type: 'FeatureCollection', features: places };\n }\n return places.type === 'Feature' ? { type: 'FeatureCollection', features: [places] } : places;\n};\n\n/**\n * Merges EV availability props into extraFeatureProps if enabled.\n * This makes EV stations use the same mechanism as any other custom properties.\n * @ignore\n */\nconst mergeEVAvailabilityProps = (\n extraFeatureProps: PlacesModuleConfig['extraFeatureProps'],\n evAvailabilityConfig: PlacesModuleConfig['evAvailability'],\n places: Places,\n): PlacesModuleConfig['extraFeatureProps'] => {\n if (evAvailabilityConfig?.enabled !== true) {\n return extraFeatureProps;\n }\n\n // Check if any EV stations exist but lack availability data\n let hasEVStations = false;\n let hasEVStationsWithAvailability = false;\n\n for (const place of places.features) {\n const isEVStation = place.properties.poi?.classifications?.[0]?.code === 'ELECTRIC_VEHICLE_STATION';\n if (isEVStation) {\n hasEVStations = true;\n if (isEVStationWithAvailability(place)) {\n hasEVStationsWithAvailability = true;\n break;\n }\n }\n }\n\n if (hasEVStations && !hasEVStationsWithAvailability) {\n console.warn(\n 'PlacesModule: evAvailability is enabled but no availability data found. ' +\n 'Did you call getPlacesWithEVAvailability()?',\n );\n }\n\n return {\n ...extraFeatureProps,\n evAvailabilityText: (place: Place) =>\n isEVStationWithAvailability(place) ? buildAvailabilityText(place, evAvailabilityConfig) : '',\n evAvailabilityRatio: (place: Place) => (isEVStationWithAvailability(place) ? getAvailabilityRatio(place) : 0),\n };\n};\n\n/**\n * prepare places features to be displayed on map by adding needed properties for title, icon and style\n * @ignore\n */\nexport const preparePlacesForDisplay = (\n placesInput: Place | Place[] | Places,\n instanceIndex: number,\n config: PlacesModuleConfig = {},\n): Places<DisplayPlaceProps> => {\n const places = toPlaces(placesInput);\n\n // Only merge EV availability props when explicitly enabled\n const mergedExtraFeatureProps =\n config.evAvailability?.enabled === true\n ? mergeEVAvailabilityProps(config.extraFeatureProps, config.evAvailability, places)\n : config.extraFeatureProps;\n\n return {\n ...places,\n features: places.features.map((place) => {\n const title =\n typeof config?.text?.title === 'function' ? config?.text?.title(place) : buildPlaceTitle(place);\n\n const extraFeatureProps = mergedExtraFeatureProps\n ? Object.fromEntries(\n Object.entries(mergedExtraFeatureProps).map(([prop, value]) => [\n prop,\n typeof value === 'function' ? value(place) : value,\n ]),\n )\n : {};\n\n const id = place.id ?? generateId();\n\n return {\n ...place,\n id,\n geometry: { ...place.geometry, bbox: place.bbox },\n properties: {\n ...place.properties,\n id, // we need id in properties due to promoteId feature\n title,\n iconID: getIconIDForPlace(place, instanceIndex, config),\n ...(config?.theme === 'base-map' && { category: getPOILayerCategoryForPlace(place) }),\n ...extraFeatureProps,\n },\n };\n }),\n };\n};\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport type {\n CleanEventStateOptions,\n CleanEventStatesOptions,\n PutEventStateOptions,\n SymbolLayerSpecWithoutSource,\n} from '../shared';\nimport { AbstractMapModule, EventsModule, GeoJSONSourceWithLayers } from '../shared';\nimport { DEFAULT_PLACE_ICON_ID } from '../shared/layers/symbolLayers';\nimport { suffixNumber } from '../shared/layers/utils';\nimport { addOrUpdateImage, changeLayersProps, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { buildPlacesLayerSpecs } from './layers/placesLayers';\nimport { defaultPin } from './resources';\nimport type { DisplayPlaceProps } from './types/placeDisplayProps';\nimport {\n PlaceIconConfig,\n PlaceLayerName,\n PlacesModuleConfig,\n PlacesTheme,\n PlaceTextConfig,\n} from './types/placesModuleConfig';\nimport { preparePlacesForDisplay } from './utils/preparePlacesForDisplay';\n\ntype PlacesSourcesAndLayers = {\n /**\n * Places source id with corresponding layers ids.\n */\n places: GeoJSONSourceWithLayers<Places<DisplayPlaceProps>>;\n};\n\n/**\n * Map module for displaying and managing place markers.\n *\n * The PlacesModule provides functionality to display location markers (pins) on the map\n * for points of interest, search results, or custom locations. It supports various marker\n * styles, custom icons, text labels, and interactive events.\n *\n * @remarks\n * **Features:**\n * - Multiple marker styles (pin, circle, POI-like)\n * - Custom icons per POI category\n * - Text labels with styling options\n * - Data-driven styling via MapLibre expressions\n * - Interactive events (click, hover, etc.)\n * - Support for custom feature properties\n * - EV charging station availability display (opt-in)\n *\n * **Marker Styles:**\n * - `pin`: Traditional teardrop-shaped map pins\n * - `circle`: Simple circular markers\n * - `base-map`: Mimics built-in POI layer styling\n *\n * **EV Charging Station Availability:**\n * When displaying EV charging stations with availability data from\n * {@link getPlacesWithEVAvailability}, the module can:\n * - Show available/total charging points (e.g., \"3/10\")\n * - Color-code availability (green = good, orange = limited, red = none/low)\n * - Display as formatted text within the station's label\n *\n * This feature is disabled by default. To enable it, set `evAvailability.enabled` to `true`\n * in the configuration.\n *\n * **Common Use Cases:**\n * - Search result visualization\n * - Custom location markers\n * - Store locators\n * - EV charging station maps with real-time availability\n * - Delivery/pickup points\n * - Saved locations display\n *\n * @example\n * ```typescript\n * // Create places module with pin markers\n * const placesModule = await PlacesModule.get(map, {\n * icon: {\n * categoryIcons: []\n * },\n * text: {\n * field: (place) => place.properties.poi?.name || 'Unknown'\n * },\n * theme: 'pin'\n * });\n *\n * // Display places from search\n * await placesModule.show(searchResults);\n *\n * // EV Charging Stations - Opt-in to availability display\n * const evStations = await PlacesModule.get(map, {\n * evAvailability: { enabled: true }\n * });\n * const results = await search({ poiCategories: ['ELECTRIC_VEHICLE_STATION'] });\n * evStations.show(await getPlacesWithEVAvailability(results)); // Shows availability\n *\n * // Granular control: Enable for searched stations only, background stations without\n * const bgStations = await PlacesModule.get(map); // EV availability disabled\n * const searched = await PlacesModule.get(map, {\n * evAvailability: { enabled: true }\n * });\n *\n * // Handle clicks\n * placesModule.events.on('click', (feature) => {\n * console.log('Clicked:', feature.properties);\n * });\n *\n * placesModule.events.on('hover', (feature) => {\n * showTooltip(feature.properties.poi?.name);\n * });\n * ```\n *\n * @see [Places Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/places)\n *\n * @group Places\n */\nexport class PlacesModule extends AbstractMapModule<PlacesSourcesAndLayers, PlacesModuleConfig> {\n private static lastInstanceIndex = -1;\n private layerSpecs!: Record<PlaceLayerName, SymbolLayerSpecWithoutSource>;\n private sourceID!: string;\n private layerIDPrefix!: string;\n /**\n * The index of this instance, to generate unique source and layer IDs.\n * * Starts with 0 and each instance increments it by one.\n * @private\n */\n private instanceIndex!: number;\n private defaultPlaceIconID!: string;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n */\n static async get(tomtomMap: TomTomMap, config?: PlacesModuleConfig): Promise<PlacesModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new PlacesModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: PlacesModuleConfig) {\n super('geojson', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: PlacesModuleConfig, restore?: boolean): PlacesSourcesAndLayers {\n // Only increment the instance index for new instances, not for restore operations\n if (!restore) {\n PlacesModule.lastInstanceIndex++;\n this.instanceIndex = PlacesModule.lastInstanceIndex;\n this.sourceID = `places-${this.instanceIndex}`;\n this.layerIDPrefix = this.sourceID;\n this.defaultPlaceIconID = suffixNumber(DEFAULT_PLACE_ICON_ID, this.instanceIndex);\n }\n\n // Update each layer id with the instance-specific prefix\n this.layerSpecs = this.buildLayerSpecs(config);\n\n return {\n places: new GeoJSONSourceWithLayers(this.mapLibreMap, this.sourceID, [\n this.layerSpecs.main,\n this.layerSpecs.selected,\n ]),\n };\n }\n\n private buildLayerSpecs(config?: PlacesModuleConfig) {\n const layerSpecTemplates = buildPlacesLayerSpecs(\n config,\n this.tomtomMap.mapLibreMap,\n this.tomtomMap.styleLightDarkTheme,\n this.instanceIndex,\n );\n\n // Update each layer id with the instance-specific prefix\n return Object.fromEntries(\n Object.entries(layerSpecTemplates).map(([key, spec]) => [\n key,\n { ...spec, id: `${this.layerIDPrefix}-${key}` },\n ]),\n ) as Record<PlaceLayerName, SymbolLayerSpecWithoutSource>;\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: PlacesModuleConfig | undefined) {\n this.updateLayersAndData(config);\n return config;\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previousShownFeatures = this.sourcesWithLayers.places.shownFeatures;\n this.initSourcesWithLayers(this.config, true);\n this.config && this._applyConfig(this.config);\n this.show(previousShownFeatures);\n }\n\n /**\n * Updates the visual theme for displayed places.\n *\n * @param theme - The theme style to apply to place markers.\n *\n * @remarks\n * **Available Themes:**\n * - `pin`: Traditional teardrop-shaped map pins\n * - `circle`: Simple circular markers\n * - `base-map`: Mimics the map's built-in POI layer style with category icons\n *\n * Changes apply immediately to all currently shown places. Other configuration\n * properties (icon config, text config) remain unchanged.\n *\n * @example\n * ```typescript\n * // Switch to pin markers\n * placesModule.applyTheme('pin');\n *\n * // Use simple circles\n * placesModule.applyTheme('circle');\n *\n * // Match map's POI style (ideal to blend in)\n * placesModule.applyTheme('base-map');\n * ```\n */\n applyTheme(theme: PlacesTheme): void {\n this.applyConfigPart({ theme });\n }\n\n /**\n * Updates the icon configuration for displayed places.\n *\n * @param iconConfig - New icon configuration settings.\n *\n * @remarks\n * - Changes apply immediately to currently shown places\n * - Custom icons are loaded if not already in style\n * - Other configuration properties remain unchanged\n *\n * @example\n * ```typescript\n * placesModule.applyIconConfig({\n * categoryIcons: [\n * { category: 'RESTAURANT', id: 'restaurant-icon', image: '/icons/food.png' }\n * ]\n * });\n * ```\n */\n applyIconConfig(iconConfig: PlaceIconConfig): void {\n this.applyConfigPart({ icon: iconConfig });\n }\n\n /**\n * Updates the text/label configuration for displayed places.\n *\n * @param textConfig - New text configuration settings.\n *\n * @remarks\n * Supports both functions and MapLibre expressions for dynamic text.\n *\n * @example\n * ```typescript\n * // Use function\n * placesModule.applyTextConfig({\n * field: (place) => place.properties.poi?.name || 'Unknown'\n * });\n *\n * // Use MapLibre expression\n * placesModule.applyTextConfig({\n * field: ['get', 'title'],\n * size: 14,\n * color: '#333'\n * });\n * ```\n */\n applyTextConfig(textConfig: PlaceTextConfig): void {\n this.applyConfigPart({ text: textConfig });\n }\n\n private applyConfigPart(partialConfig: Partial<PlacesModuleConfig>): void {\n const config = { ...this.config, ...partialConfig };\n this.updateLayersAndData(config);\n this.config = config;\n }\n\n /**\n * Applies additional feature properties to displayed places.\n *\n * @param extraFeatureProps - Object mapping property names to values or functions.\n *\n * @remarks\n * Useful for adding computed properties or metadata for styling/filtering.\n *\n * @example\n * ```typescript\n * placesModule.applyExtraFeatureProps({\n * category: (place) => place.properties.poi?.categories?.[0],\n * rating: (place) => place.properties.poi?.rating || 0,\n * isOpen: true\n * });\n * ```\n */\n applyExtraFeatureProps(extraFeatureProps: { [key: string]: any }): void {\n const config = { ...this.config, extraFeatureProps };\n this.updateData(config);\n this.config = config;\n }\n\n private updateLayersAndData(config: PlacesModuleConfig | undefined): void {\n this.setupImages(config);\n const newLayerSpecs = this.buildLayerSpecs(config);\n // Convert layerSpecs objects to arrays for changeLayersProps\n const newLayerSpecsArray = [newLayerSpecs.main, newLayerSpecs.selected];\n const oldLayerSpecsArray = [this.layerSpecs.main, this.layerSpecs.selected];\n changeLayersProps(newLayerSpecsArray, oldLayerSpecsArray, this.mapLibreMap);\n this.layerSpecs = newLayerSpecs;\n this.updateData(config);\n }\n\n private setupImages(config: PlacesModuleConfig | undefined): void {\n // Ensure default pin is added:\n if (config?.icon) {\n // If we have custom icons, ensure they're added to the map style:\n for (const customIcon of config.icon.categoryIcons ?? []) {\n // Create unique ID for each custom icon, including availability level if present\n const iconID = customIcon.availabilityLevel\n ? `${customIcon.id}-${customIcon.availabilityLevel}`\n : customIcon.id;\n\n addOrUpdateImage(\n 'if-not-in-sprite',\n suffixNumber(iconID, this.instanceIndex),\n customIcon.image as string | HTMLImageElement,\n this.mapLibreMap,\n {\n pixelRatio: customIcon.pixelRatio ?? 2,\n },\n );\n }\n\n if (config.icon.default) {\n if (config.icon.default.image) {\n addOrUpdateImage(\n 'if-not-in-sprite',\n this.defaultPlaceIconID,\n config.icon.default.image.image as string | HTMLImageElement,\n this.mapLibreMap,\n {\n pixelRatio: config.icon.default.image.pixelRatio ?? 2,\n },\n );\n }\n if (config.icon.default.style) {\n addOrUpdateImage(\n 'if-not-in-sprite',\n this.defaultPlaceIconID,\n defaultPin(config.icon.default.style),\n this.mapLibreMap,\n { pixelRatio: 2 },\n );\n }\n }\n } else {\n // Ensure default pin is added:\n addOrUpdateImage('if-not-in-sprite', this.defaultPlaceIconID, defaultPin(), this.mapLibreMap, {\n pixelRatio: 2,\n });\n }\n }\n\n private updateData(config: PlacesModuleConfig | undefined): void {\n this.sourcesWithLayers.places.source.runtimeSource?.setData(\n preparePlacesForDisplay(this.sourcesWithLayers.places.shownFeatures, this.instanceIndex, config),\n );\n }\n\n /**\n * Displays the given places on the map.\n *\n * @param places - Place data to display. Can be a single Place, array of Places,\n * or a Places FeatureCollection.\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown places\n * - Applies current module styling configuration\n * - Automatically generates labels if text config is set\n * - Waits for module to be ready before displaying\n *\n * **Data Sources:**\n * - TomTom Search API results\n * - Custom place objects matching the Place interface\n * - GeoJSON Point features\n *\n * @example\n * Display search results:\n * ```typescript\n * import { search } from '@tomtom-international/maps-sdk-js/services';\n *\n * const results = await search({ query: 'coffee' });\n * await placesModule.show(results);\n * ```\n *\n * @example\n * Display single place:\n * ```typescript\n * await placesModule.show({\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [4.9041, 52.3676] },\n * properties: {\n * address: { freeformAddress: 'Amsterdam' },\n * poi: { name: 'Amsterdam Central' }\n * }\n * });\n * ```\n *\n * @example\n * Display multiple places:\n * ```typescript\n * await placesModule.show([place1, place2, place3]);\n * ```\n */\n async show(places: Place | Place[] | Places) {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.places.show(preparePlacesForDisplay(places, this.instanceIndex, this.config));\n }\n\n /**\n * Removes all places from the map.\n *\n * @remarks\n * - Clears all displayed places\n * - Does not reset styling configuration\n * - Module remains initialized and ready for new data\n *\n * @example\n * ```typescript\n * await placesModule.clear();\n * ```\n */\n async clear() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.places.clear();\n }\n\n /**\n * Returns the currently shown places.\n *\n * @returns The places currently displayed on the map.\n *\n * @remarks\n * Returns the exact data that was passed to the `show()` method.\n *\n * @example\n * ```typescript\n * const shown = placesModule.getShown();\n * console.log(`Showing ${shown.places.features.length} places`);\n * ```\n */\n getShown() {\n return {\n places: this.sourcesWithLayers.places.shownFeatures,\n };\n }\n\n /**\n * Programmatically sets an event state on a specific place.\n *\n * @param options - Configuration for the event state to apply.\n *\n * @remarks\n * Use this to make places appear clicked or hovered programmatically.\n *\n * @example\n * ```typescript\n * // Make first place appear clicked\n * placesModule.putEventState({\n * index: 0,\n * state: 'click',\n * mode: 'put'\n * });\n * ```\n */\n putEventState(options: PutEventStateOptions) {\n this.sourcesWithLayers.places.putEventState(options);\n }\n\n /**\n * Removes an event state from a specific place.\n *\n * @param options - Configuration for which event state to remove.\n *\n * @example\n * ```typescript\n * placesModule.cleanEventState({ index: 0 });\n * ```\n */\n cleanEventState(options: CleanEventStateOptions): void {\n this.sourcesWithLayers.places.cleanEventState(options);\n }\n\n /**\n * Removes event states from multiple places.\n *\n * @param options - Optional filter for which states to remove.\n *\n * @example\n * ```typescript\n * // Remove all event states\n * placesModule.cleanEventStates();\n *\n * // Remove only hover states\n * placesModule.cleanEventStates({ states: ['hover'] });\n * ```\n */\n cleanEventStates(options?: CleanEventStatesOptions) {\n this.sourcesWithLayers.places.cleanEventStates(options);\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule<Place<DisplayPlaceProps>>(\n this.eventsProxy,\n this.sourcesWithLayers.places,\n this.config?.events,\n );\n }\n}\n","import type { ExpressionFilterSpecification, FilterSpecification, LegacyFilterSpecification } from 'maplibre-gl';\nimport type { FilterShowMode, FilterSyntaxVersion, MultiSyntaxFilter, ValuesFilter } from './types';\n\n/**\n * @ignore\n */\nconst isExpressionFilter = (filter: FilterSpecification): filter is ExpressionFilterSpecification => {\n if (filter === true || filter === false) {\n return true;\n }\n\n if (!Array.isArray(filter) || filter.length === 0) {\n return false;\n }\n switch (filter[0]) {\n case 'has':\n return filter.length >= 2 && filter[1] !== '$id' && filter[1] !== '$type';\n\n case 'in':\n return filter.length >= 3 && (typeof filter[1] !== 'string' || Array.isArray(filter[2]));\n\n case '!in':\n case '!has':\n case 'none':\n return false;\n\n case '==':\n case '!=':\n case '>':\n case '>=':\n case '<':\n case '<=':\n return filter.length !== 3 || Array.isArray(filter[1]) || Array.isArray(filter[2]);\n\n case 'any':\n case 'all':\n for (const f of filter.slice(1)) {\n if (!isExpressionFilter(f as FilterSpecification) && typeof f !== 'boolean') {\n return false;\n }\n }\n return true;\n\n default:\n return true;\n }\n};\n\n/**\n * @ignore\n */\nexport const getSyntaxVersion = (expression: FilterSpecification): FilterSyntaxVersion =>\n isExpressionFilter(expression) ? 'expression' : 'legacy';\n\n/**\n * @ignore\n */\nexport const getMergedAnyFilter = (filters: MultiSyntaxFilter[]): MultiSyntaxFilter | null => {\n if (!filters?.length) {\n return null;\n }\n if (filters.length === 1) {\n return filters[0];\n }\n return {\n expression: ['any', ...filters.map((filter) => filter?.expression)],\n legacy: ['any', ...filters.map((filter) => filter?.legacy)],\n };\n};\n\n/**\n * @ignore\n */\nexport const getMergedAllFilter = (\n filterToAdd: MultiSyntaxFilter,\n originalFilter: FilterSpecification | undefined,\n): FilterSpecification => {\n if (originalFilter) {\n return ['all', filterToAdd[getSyntaxVersion(originalFilter)], originalFilter] as FilterSpecification;\n }\n return filterToAdd.expression;\n};\n\n/**\n * @ignore\n */\nexport const buildMappedValuesFilter = <T>(\n propName: string,\n showMode: FilterShowMode,\n values: T[],\n): MultiSyntaxFilter => {\n if (values.length === 1) {\n const comparator = showMode === 'only' ? '==' : '!=';\n return {\n expression: [comparator, ['get', propName], values[0]] as ExpressionFilterSpecification,\n legacy: [comparator, propName, values[0]] as LegacyFilterSpecification,\n };\n }\n const filterArrayNew = ['in', ['get', propName], ['literal', values]];\n if (showMode === 'only') {\n return {\n expression: filterArrayNew as ExpressionFilterSpecification,\n legacy: ['in', propName, ...values] as LegacyFilterSpecification,\n };\n }\n return {\n expression: ['!', filterArrayNew as ExpressionFilterSpecification],\n legacy: ['!in', propName, ...values] as LegacyFilterSpecification,\n };\n};\n\n/**\n * @ignore\n */\nexport const buildValuesFilter = <T>(\n propName: string,\n filter: ValuesFilter<T>,\n valuesMapping?: (value: T) => unknown,\n): MultiSyntaxFilter =>\n buildMappedValuesFilter(propName, filter.show, valuesMapping ? filter.values.map(valuesMapping) : filter.values);\n","import type { MapStylePOICategory } from '../../places';\n\n/**\n * Predefined groups of related POI categories for convenient filtering.\n *\n * @remarks\n * This object maps group names to arrays of {@link MapStylePOICategory} values.\n * Each group contains POI categories that share a common theme or purpose,\n * making it easier to filter multiple related POI types with a single identifier.\n *\n * **Available Groups:**\n * - `FOOD_DRINKS_GROUP` - Dining and beverage establishments (restaurants, cafes, pubs, etc.)\n * - `SHOPPING_GROUP` - Retail stores and shopping centers (shops, markets, supermarkets, etc.)\n * - `TRANSPORTATION_GROUP` - Transportation hubs and stops (airports, stations, terminals, etc.)\n * - `HEALTH_GROUP` - Healthcare facilities and services (hospitals, clinics, pharmacies, etc.)\n * - `PARKING_GROUP` - Parking facilities (garages and open parking areas)\n * - `HOLIDAY_TOURISM_GROUP` - Tourist attractions and recreational sites (museums, parks, beaches, etc.)\n * - `EV_CHARGING_STATIONS_GROUP` - Electric vehicle charging locations\n * - `GAS_STATIONS_GROUP` - Fuel stations for traditional vehicles\n * - `ACCOMMODATION_GROUP` - Lodging facilities (hotels, motels, camping grounds, etc.)\n * - `ENTERTAINMENT_GROUP` - Entertainment venues (cinemas, theaters, nightlife, casinos, etc.)\n * - `SPORTS_LEISURE_GROUP` - Sports and leisure facilities (stadiums, gyms, pools, golf courses, etc.)\n * - `EDUCATION_GROUP` - Educational institutions (schools, universities, libraries, etc.)\n * - `GOVERNMENT_GROUP` - Government and public safety facilities (offices, courts, embassies, police, fire stations)\n *\n * @example\n * Filter to show only food-related POIs:\n * ```ts\n * import { poiCategoryGroups } from '@tomtom-international/maps-sdk-js/map';\n *\n * const foodCategories = poiCategoryGroups.FOOD_DRINKS_GROUP;\n * console.log(foodCategories);\n * // ['RESTAURANT', 'FAST_FOOD', 'CAFE_PUB', 'PUB', 'WINERY', ...]\n * ```\n *\n * @example\n * Use with POI module filtering:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'show',\n * values: ['FOOD_DRINKS_GROUP', 'ENTERTAINMENT_GROUP']\n * }\n * });\n * ```\n *\n * @see {@link POICategoryGroup} - Type representing all available group names\n * @see {@link MapStylePOICategory} - Individual POI category identifiers\n *\n * @group POIs\n */\nexport const poiCategoryGroups: Record<string, MapStylePOICategory[]> = {\n FOOD_DRINKS_GROUP: [\n 'RESTAURANT',\n 'FAST_FOOD',\n 'CAFE_PUB',\n 'PUB',\n 'WINERY',\n 'PUB_FOOD',\n 'SOUL_FOOD',\n 'DELICATESSEN',\n ],\n SHOPPING_GROUP: [\n 'SHOP',\n 'SHOPPING_CENTER',\n 'CLOTHING_SHOP',\n 'MARKET',\n 'FOOD_MARKET',\n 'SUPERMARKETS_HYPERMARKETS',\n 'DEPARTMENT_STORE',\n 'CONVENIENCE_STORE',\n 'GROCERY_STORE',\n 'HARDWARE_STORE',\n 'ELECTRICAL_APPLIANCES_SHOP',\n ],\n TRANSPORTATION_GROUP: [\n 'AIRPORT',\n 'FERRY_TERMINAL',\n 'HELIPAD_HELICOPTER_LANDING',\n 'PUBLIC_TRANSPORT_STOP',\n 'RAILWAY_STATION',\n 'BUS_STOP',\n 'TAXI_STAND',\n ],\n HEALTH_GROUP: [\n 'DOCTOR',\n 'EMERGENCY_MEDICAL_SERVICE',\n 'EMERGENCY_ROOM',\n 'HEALTH_CARE_SERVICE',\n 'HOSPITAL',\n 'HOSPITAL_POLYCLINIC',\n 'PHARMACY',\n 'DENTIST',\n 'WELFARE_ORGANIZATION',\n ],\n PARKING_GROUP: ['PARKING_GARAGE', 'OPEN_PARKING_AREA'],\n HOLIDAY_TOURISM_GROUP: [\n 'AMUSEMENT_PARK',\n 'BEACH',\n 'HOLIDAY_RENTAL',\n 'GEOGRAPHIC_FEATURE',\n 'IMPORTANT_TOURIST_ATTRACTION',\n 'LEISURE_CENTER',\n 'MOUNTAIN_PASS',\n 'MOUNTAIN_PEAK',\n 'MUSEUM',\n 'SCENIC_PANORAMIC_VIEW',\n 'TOURIST_INFORMATION_OFFICE',\n ],\n EV_CHARGING_STATIONS_GROUP: ['ELECTRIC_VEHICLE_STATION'],\n GAS_STATIONS_GROUP: ['GAS_STATION', 'PETROL_STATION'],\n ACCOMMODATION_GROUP: ['CAMPING_GROUND', 'HOTEL_MOTEL', 'HOLIDAY_RENTAL'],\n ENTERTAINMENT_GROUP: [\n 'CINEMA',\n 'THEATER',\n 'MOVIE_THEATER',\n 'NIGHTLIFE',\n 'CONCERT_HALL',\n 'ENTERTAINMENT',\n 'CLUB_ASSOCIATION',\n 'CASINO',\n ],\n SPORTS_LEISURE_GROUP: [\n 'SPORTS_CENTER',\n 'WATER_SPORT',\n 'SWIMMING_POOL',\n 'GOLF_COURSE',\n 'STADIUM',\n 'BEACH',\n 'ICE_SKATING_RINK',\n 'LEISURE_CENTER',\n 'MOUNTAIN_PASS',\n 'MOUNTAIN_PEAK',\n ],\n EDUCATION_GROUP: ['SCHOOL', 'COLLEGE_UNIVERSITY', 'LIBRARY', 'CULTURAL_CENTER'],\n GOVERNMENT_GROUP: ['GOVERNMENT_OFFICE', 'COURTHOUSE', 'EMBASSY', 'FIRE_STATION_BRIGADE', 'POLICE_STATION'],\n};\n\n/**\n * POI category group type.\n *\n * @remarks\n * Represents predefined groups of related POI categories for convenient filtering.\n * Each group contains multiple {@link MapStylePOICategory} values that share a common theme.\n *\n * Using category groups simplifies filtering by allowing you to show or hide\n * multiple related POI types with a single filter value.\n *\n * **Available groups:**\n * - `FOOD_DRINKS_GROUP` - Restaurants, cafes, fast food, wineries, etc.\n * - `SHOPPING_GROUP` - Stores, malls, markets, supermarkets, etc.\n * - `TRANSPORTATION_GROUP` - Airports, train stations, bus stops, ferry terminals, etc.\n * - `HEALTH_GROUP` - Hospitals, clinics, pharmacies, doctors, dentists, etc.\n * - `PARKING_GROUP` - Parking garages and open parking areas\n * - `HOLIDAY_TOURISM_GROUP` - Tourist attractions, museums, beaches, scenic views, etc.\n * - `EV_CHARGING_STATIONS_GROUP` - Electric vehicle charging stations\n * - `GAS_STATIONS_GROUP` - Gas and petrol stations\n * - `ACCOMMODATION_GROUP` - Hotels, motels, camping grounds, etc.\n * - `ENTERTAINMENT_GROUP` - Cinemas, theaters, nightlife, casinos, etc.\n * - `SPORTS_LEISURE_GROUP` - Stadiums, sports centers, swimming pools, golf courses, etc.\n * - `EDUCATION_GROUP` - Schools, universities, libraries, cultural centers\n * - `GOVERNMENT_GROUP` - Government offices, courthouses, embassies, police, fire stations\n *\n * @example\n * Filter to show only food-related POIs:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'show',\n * values: ['FOOD_DRINKS_GROUP']\n * }\n * });\n * ```\n *\n * @example\n * Hide parking and gas stations:\n * ```ts\n * poisModule.configure({\n * categoryFilter: {\n * mode: 'hide',\n * values: ['PARKING_GROUP', 'GAS_STATIONS_GROUP']\n * }\n * });\n * ```\n *\n * @example\n * Combine multiple groups for tourism use case:\n * ```ts\n * const tourismFilter = {\n * mode: 'show',\n * values: [\n * 'HOLIDAY_TOURISM_GROUP',\n * 'ACCOMMODATION_GROUP',\n * 'FOOD_DRINKS_GROUP',\n * 'ENTERTAINMENT_GROUP'\n * ]\n * };\n * ```\n *\n * @see {@link poiCategoryGroups} - The object containing all group definitions\n * @see {@link MapStylePOICategory} - For individual POI categories\n *\n * @group POIs\n */\nexport type POICategoryGroup = keyof typeof poiCategoryGroups;\n","import { POICategory } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport { toBaseMapPOICategory } from '../places';\nimport type { ValuesFilter } from '../shared';\nimport { AbstractMapModule, EventsModule, POI_SOURCE_ID, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { buildMappedValuesFilter, getMergedAllFilter } from '../shared/mapLibreFilterUtils';\nimport { waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { poiLayerIDs } from './layers/poisLayers';\nimport { poiCategoryGroups } from './types/poiCategoryGroups';\nimport type { FilterablePOICategory, POIsModuleConfig } from './types/poisModuleConfig';\nimport type { POIsModuleFeature } from './types/poisModuleFeature';\n\n/**\n * Gets the specified filtered categories icon IDs to be used in map filtering.\n * @param categories list of filtered categories.\n * @ignore\n */\nexport const getStyleCategories = (categories: FilterablePOICategory[]): string[] => {\n const categoryIds: string[] = [];\n categories.forEach((category: FilterablePOICategory) => {\n if (category in poiCategoryGroups) {\n categoryIds.push(...poiCategoryGroups[category].map(toBaseMapPOICategory));\n } else {\n categoryIds.push(toBaseMapPOICategory(category as POICategory));\n }\n });\n return [...new Set(categoryIds)];\n};\n\n/**\n * IDs of sources and layers for places of interest module.\n */\ntype PoIsSourcesAndLayers = {\n /**\n * Places of interest with corresponding layer ids.\n * TODO: technically source ID is vectorTiles if POIs stay included in base map for Orbis\n */\n poi: StyleSourceWithLayers;\n};\n\n/**\n * POIs Module for controlling Points of Interest displayed in the map style.\n *\n * This module manages the built-in POI layer from the vector map, allowing you to\n * show/hide POIs and filter them by category. POIs are already part of the map style\n * and include businesses, landmarks, and other points of interest.\n *\n * @remarks\n * **Features:**\n * - Toggle POI visibility on/off\n * - Filter by POI categories or category groups\n * - Event handling for POI interactions\n * - Based on vector tile data in the map style\n *\n * **POI Categories:**\n * - Individual categories (e.g., RESTAURANT, HOTEL_MOTEL, PARKING_GARAGE)\n * - Category groups (e.g., FOOD_DRINKS_GROUP, SHOPPING_GROUP, TRANSPORTATION_GROUP)\n *\n * **Difference from PlacesModule:**\n * - POIsModule: Controls existing POIs in the map style\n * - PlacesModule: Displays custom place data from Search API or other sources\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { POIsModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module\n * const poisModule = await POIsModule.get(map);\n *\n * // Toggle visibility\n * poisModule.setVisible(false);\n * poisModule.setVisible(true);\n * ```\n *\n * @example\n * Filter specific categories:\n * ```typescript\n * // Show only restaurants and hotels\n * poisModule.filterCategories({\n * show: 'only',\n * values: ['RESTAURANT', 'HOTEL_MOTEL']\n * });\n *\n * // Hide parking garages\n * poisModule.filterCategories({\n * show: 'all_except',\n * values: ['PARKING_GARAGE', 'OPEN_PARKING_AREA']\n * });\n * ```\n *\n * @example\n * Filter using category groups:\n * ```typescript\n * // Show only food and shopping POIs\n * poisModule.filterCategories({\n * show: 'only',\n * values: ['FOOD_DRINKS_GROUP', 'SHOPPING_GROUP']\n * });\n *\n * // Hide transportation POIs\n * pois.filterCategories({\n * show: 'all_except',\n * values: ['TRANSPORTATION_GROUP']\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('Clicked POI:', feature.properties.name);\n * console.log('Category:', feature.properties.category);\n * });\n * ```\n *\n * @see [POIs Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/pois)\n *\n * @group POIs\n */\nexport class POIsModule extends AbstractMapModule<PoIsSourcesAndLayers, POIsModuleConfig> {\n private categoriesFilter?: ValuesFilter<FilterablePOICategory> | null;\n private originalFilter?: FilterSpecification;\n\n /**\n * Retrieves a POIsModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional initial configuration for visibility and filters.\n *\n * @returns A promise that resolves to the initialized POIsModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `filters.categories`: Category filter to apply on initialization\n *\n * @throws Error if the POI source is not found in the map style\n *\n * @example\n * Default initialization:\n * ```typescript\n * const poisModule = await POIsModule.get(map);\n * ```\n *\n * @example\n * With initial filter:\n * ```typescript\n * const poisModule = await POIsModule.get(map, {\n * visible: true,\n * filters: {\n * categories: {\n * show: 'only',\n * values: ['RESTAURANT', 'CAFE_PUB']\n * }\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: POIsModuleConfig): Promise<POIsModule> {\n await waitUntilMapIsReady(map);\n return new POIsModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: POIsModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const poiRuntimeSource = this.mapLibreMap.getSource(POI_SOURCE_ID);\n if (!poiRuntimeSource) {\n throw notInTheStyle(`init ${POIsModule.name} with source ID ${POI_SOURCE_ID}`);\n }\n const poi = new StyleSourceWithLayers(this.mapLibreMap, poiRuntimeSource, (layer) =>\n poiLayerIDs.includes(layer.id),\n );\n // TODO: check if the layers are present in the style, and if not, throw exception?\n const mainLayer = poi.sourceAndLayerIDs.layerIDs[0];\n if (this.mapLibreMap.getLayer(mainLayer)) {\n this.originalFilter = this.mapLibreMap.getFilter(mainLayer) as FilterSpecification;\n }\n return { poi };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: POIsModuleConfig | undefined) {\n if (config && !isNil(config.visible)) {\n this.setVisible(config.visible);\n } else if (!this._initializing && !this.isVisible()) {\n // applying default:\n this.setVisible(true);\n }\n\n this.filterCategories(config?.filters?.categories);\n return config;\n }\n\n /**\n * Checks if POI layers are currently visible.\n *\n * @returns `true` if at least one POI layer is visible, `false` if all are hidden.\n *\n * @example\n * ```typescript\n * if (pois.isVisible()) {\n * console.log('POIs are displayed');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.poi.isAnyLayerVisible();\n }\n\n /**\n * Sets the visibility of POI layers.\n *\n * @param visible - `true` to show POIs, `false` to hide them.\n *\n * @remarks\n * Changes are applied immediately if the map is ready.\n *\n * @example\n * ```typescript\n * pois.setVisible(false); // Hide all POIs\n * pois.setVisible(true); // Show all POIs\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = {\n ...this.config,\n visible,\n };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.poi.setLayersVisible(visible);\n }\n }\n\n /**\n * Filters POIs by category or category group.\n *\n * @param categoriesFilter - Filter configuration specifying which categories to show/hide.\n * Pass `undefined` to reset to default (show all).\n *\n * @remarks\n * **Filter Modes:**\n * - `only`: Show only the specified categories, hide all others\n * - `all_except`: Show all categories except the specified ones\n *\n * **Category Types:**\n * - Individual categories (e.g., 'RESTAURANT', 'HOTEL_MOTEL')\n * - Category groups (e.g., 'FOOD_DRINKS_GROUP', 'SHOPPING_GROUP')\n *\n * **Available Category Groups:**\n * - FOOD_DRINKS_GROUP\n * - SHOPPING_GROUP\n * - TRANSPORTATION_GROUP\n * - HEALTH_GROUP\n * - PARKING_GROUP\n * - HOLIDAY_TOURISM_GROUP\n * - EV_CHARGING_STATIONS_GROUP\n * - GAS_STATIONS_GROUP\n * - ACCOMMODATION_GROUP\n * - ENTERTAINMENT_GROUP\n * - SPORTS_LEISURE_GROUP\n * - EDUCATION_GROUP\n * - GOVERNMENT_GROUP\n *\n * @example\n * Show only restaurants:\n * ```typescript\n * pois.filterCategories({\n * show: 'only',\n * values: ['RESTAURANT']\n * });\n * ```\n *\n * @example\n * Hide parking:\n * ```typescript\n * pois.filterCategories({\n * show: 'all_except',\n * values: ['PARKING_GROUP']\n * });\n * ```\n *\n * @example\n * Reset filter (show all):\n * ```typescript\n * pois.filterCategories(undefined);\n * ```\n */\n filterCategories(categoriesFilter?: ValuesFilter<FilterablePOICategory> | undefined): void {\n if (categoriesFilter) {\n if (this.tomtomMap.mapReady) {\n const poiFilter = buildMappedValuesFilter(\n 'category',\n categoriesFilter.show,\n getStyleCategories(categoriesFilter.values),\n );\n\n this.mapLibreMap.setFilter('POI', getMergedAllFilter(poiFilter, this.originalFilter));\n }\n this.config = {\n ...this.config,\n filters: {\n categories: categoriesFilter,\n },\n };\n } else if (this.categoriesFilter) {\n // reset categories config to default\n this.config = {\n ...this.config,\n filters: {\n categories: {\n show: 'all_except',\n values: [],\n },\n },\n };\n if (this.tomtomMap.mapReady) {\n // Applies default:\n this.mapLibreMap.setFilter('POI', this.originalFilter);\n }\n }\n\n this.categoriesFilter = categoriesFilter;\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible POI features with properly typed properties.\n *\n * @example\n * ```typescript\n * const shown = poisModule.getShown();\n * console.log(`Visible POIs: ${shown.poi.length}`);\n * shown.poi.forEach(poi => {\n * console.log(poi.properties.name, poi.properties.category);\n * });\n * ```\n */\n getShown() {\n return {\n poi: this.mapLibreMap.queryRenderedFeatures({\n layers: this.sourcesWithLayers.poi.sourceAndLayerIDs.layerIDs,\n validate: false,\n }) as unknown as POIsModuleFeature[],\n };\n }\n\n /**\n * Gets the events interface for handling user interactions with POIs.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a POI\n * - `contextmenu`: User right-clicks on a POI\n * - `hover`: Mouse enters a POI\n * - `long-hover`: Mouse hovers over POI for extended time\n *\n * **Event Feature Properties:**\n * - `id`: Unique POI identifier\n * - `name`: POI name in native language\n * - `category`: POI category\n * - `iconID`: Icon sprite ID\n * - `group`: Category group\n * - `priority`: Importance level (lower = more important)\n *\n * @example\n * ```typescript\n * pois.events.on('click', (feature, lngLat) => {\n * console.log('POI:', feature.properties.name);\n * console.log('Category:', feature.properties.category);\n * console.log('ID:', feature.properties.id);\n * });\n *\n * pois.events.on('hover', (feature) => {\n * showTooltip(feature.properties.name);\n * });\n * ```\n */\n get events() {\n return new EventsModule<POIsModuleFeature>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.poi,\n this.config?.events,\n );\n }\n}\n","import type { LayerSpecification } from 'maplibre-gl';\nimport { poiLayerIDs } from '../pois';\nimport type { LayerSpecFilter } from '../shared';\nimport type { BaseMapLayerGroupName, BaseMapLayerGroups } from './types/baseMapModuleConfig';\n\ntype LayerGroupMapping = {\n layerIDMatches: string[];\n layerTypes: LayerSpecification['type'][];\n};\n\nconst layerGroupMappings: Record<BaseMapLayerGroupName, LayerGroupMapping> = {\n land: {\n layerIDMatches: ['lulc'],\n layerTypes: ['fill'],\n },\n borders: {\n layerIDMatches: ['borders'],\n layerTypes: ['line'],\n },\n water: {\n layerIDMatches: ['water'],\n layerTypes: ['fill', 'line'],\n },\n buildings2D: {\n layerIDMatches: ['building'],\n layerTypes: ['fill', 'line'],\n },\n buildings3D: {\n layerIDMatches: ['building'],\n layerTypes: ['fill-extrusion'],\n },\n houseNumbers: {\n layerIDMatches: ['house number'],\n layerTypes: ['symbol'],\n },\n roadLines: {\n layerIDMatches: ['road', 'tunnel', 'bridge', 'surface'],\n layerTypes: ['fill', 'line'],\n },\n roadLabels: {\n layerIDMatches: ['road', 'tunnel', 'bridge', 'surface'],\n layerTypes: ['symbol'],\n },\n roadShields: {\n layerIDMatches: ['shield'],\n layerTypes: ['symbol'],\n },\n placeLabels: {\n layerIDMatches: ['places'],\n layerTypes: ['symbol'],\n },\n smallerTownLabels: {\n layerIDMatches: ['town', 'village', 'neighbourhood'],\n layerTypes: ['symbol'],\n },\n cityLabels: {\n layerIDMatches: ['city', 'capital'],\n layerTypes: ['symbol'],\n },\n capitalLabels: {\n layerIDMatches: ['capital'],\n layerTypes: ['symbol'],\n },\n stateLabels: {\n layerIDMatches: ['state'],\n layerTypes: ['symbol'],\n },\n countryLabels: {\n layerIDMatches: ['places - country'],\n layerTypes: ['symbol'],\n },\n};\n\nconst isMatching = (group: BaseMapLayerGroupName, layer: LayerSpecification) => {\n const mapping = layerGroupMappings[group];\n return (\n mapping.layerIDMatches.some((part) => layer.id.toLowerCase().includes(part)) &&\n mapping.layerTypes.includes(layer.type)\n );\n};\n\n/**\n * @ignore\n */\nexport const buildLayerGroupFilter = (layerGroups: BaseMapLayerGroups): LayerSpecFilter => {\n const mode = layerGroups.mode;\n const groups = layerGroups.names;\n if (mode === 'include') {\n return (layer) => groups.some((group) => isMatching(group, layer));\n }\n if (mode === 'exclude') {\n return (layer) => !groups.some((group) => isMatching(group, layer));\n }\n // No filtering if we don't recognize the mode:\n console.error('Unrecognized layer group mode:', mode);\n return () => true;\n};\n\n/**\n * @ignore\n */\nexport const filterLayerByGroups = (layer: LayerSpecification, layerGroups?: BaseMapLayerGroups): boolean => {\n const mode = layerGroups?.mode;\n const groups = layerGroups?.names;\n if (mode && groups?.length) {\n if (mode === 'include') {\n return groups.some((group) => isMatching(group, layer));\n }\n if (mode === 'exclude') {\n return !groups.some((group) => isMatching(group, layer));\n }\n }\n return true;\n};\n\n/**\n * @ignore\n */\nexport const buildBaseMapLayerGroupFilter =\n (layerGroupsFilter?: BaseMapLayerGroups): LayerSpecFilter =>\n (layer: LayerSpecification): boolean =>\n (!layerGroupsFilter || filterLayerByGroups(layer, layerGroupsFilter)) && !poiLayerIDs.includes(layer.id);\n","import { isNil } from 'lodash-es';\n\nimport { AbstractMapModule, BASE_MAP_SOURCE_ID, EventsModule, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { waitUntilMapIsReady } from '../shared/mapUtils';\nimport { TomTomMap } from '../TomTomMap';\nimport { buildBaseMapLayerGroupFilter, buildLayerGroupFilter } from './layerGroups';\nimport type { BaseMapLayerGroups, BaseMapModuleConfig, BaseMapModuleInitConfig } from './types/baseMapModuleConfig';\n\ntype BaseSourceAndLayers = {\n vectorTiles: StyleSourceWithLayers;\n};\n\n/**\n * Base Map Module for controlling standard map layers and their visibility.\n *\n * This module manages the fundamental map layers including background, water, land, roads,\n * buildings, labels, and other vector tile layers from the base map style.\n *\n * @remarks\n * **Managed Layers:**\n * - Background and terrain\n * - Water bodies and coastlines\n * - Country and administrative borders\n * - Buildings (2D and 3D)\n * - Road lines, labels, and shields\n * - Place labels at various zoom levels\n * - House numbers\n *\n * **Does NOT Include:**\n * - Traffic flow/incidents (use {@link TrafficFlowModule} or {@link TrafficIncidentsModule})\n * - Points of Interest/POIs (use {@link POIsModule})\n * - Hillshade/terrain shading (use {@link HillshadeModule})\n *\n * **Use Cases:**\n * - Toggle base map visibility on/off\n * - Show only specific layer groups (e.g., roads only)\n * - Create custom map appearances by hiding certain elements\n * - Build overlay maps with selective base layers\n *\n * @example\n * Basic usage:\n * ```typescript\n * // Get module with default configuration\n * const baseMap = await BaseMapModule.get(map);\n *\n * // Toggle visibility\n * baseMap.setVisible(false); // Hide all base layers\n * baseMap.setVisible(true); // Show all base layers\n *\n * // Check current state\n * if (baseMap.isVisible()) {\n * console.log('Base map is visible');\n * }\n * ```\n *\n * @example\n * Working with layer groups:\n * ```typescript\n * // Show only roads and borders\n * const baseMap = await BaseMapModule.get(map, {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'borders']\n * }\n * });\n *\n * // Hide buildings and labels\n * baseMap.setVisible(false, {\n * layerGroups: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D', 'placeLabels']\n * }\n * });\n *\n * // Show only water and land\n * baseMap.setVisible(true, {\n * layerGroups: {\n * mode: 'include',\n * names: ['water', 'land']\n * }\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map);\n *\n * // Listen for clicks on base map features\n * baseMap.events.on('click', (feature, lngLat) => {\n * console.log('Clicked base map feature:', feature);\n * });\n *\n * // Remove event listeners\n * baseMap.events.off('click');\n * ```\n *\n * @see [Base Map Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/base-map)\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n *\n * @group Base Map\n */\nexport class BaseMapModule extends AbstractMapModule<BaseSourceAndLayers, BaseMapModuleConfig> {\n /**\n * Asynchronously retrieves a BaseMapModule instance for the given map.\n *\n * This is the recommended way to create a BaseMapModule. It ensures the map\n * is fully loaded before initializing the module.\n *\n * @param tomtomMap - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for module initialization.\n *\n * @returns A promise that resolves to the initialized BaseMapModule.\n *\n * @remarks\n * **Initialization:**\n * - Waits for map to be ready before creating module\n * - Validates that required sources exist in the map style\n * - Applies initial configuration if provided\n *\n * **Configuration Options:**\n * - `visible`: Initial visibility state\n * - `layerGroupsFilter`: Which layer groups to include/exclude\n * - `layerGroupsVisibility`: Fine-grained visibility per group\n *\n * @throws Error if the base map source is not found in the style\n *\n * @example\n * Default initialization:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map);\n * ```\n *\n * @example\n * With configuration:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map, {\n * visible: true,\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings3D', 'houseNumbers']\n * }\n * });\n * ```\n *\n * @example\n * Show only specific groups:\n * ```typescript\n * const baseMap = await BaseMapModule.get(map, {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land', 'borders']\n * },\n * visible: true\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: BaseMapModuleInitConfig): Promise<BaseMapModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new BaseMapModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: BaseMapModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config: BaseMapModuleInitConfig | undefined) {\n const source = this.mapLibreMap.getSource(BASE_MAP_SOURCE_ID);\n if (!source) {\n throw notInTheStyle(`init ${BaseMapModule.name} with source ID ${BASE_MAP_SOURCE_ID}`);\n }\n\n return {\n vectorTiles: new StyleSourceWithLayers(\n this.mapLibreMap,\n source,\n buildBaseMapLayerGroupFilter(config?.layerGroupsFilter),\n ),\n };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: BaseMapModuleConfig | undefined) {\n if (config && !isNil(config.visible)) {\n this.setVisible(config.visible);\n } else if (!this._initializing && !this.isVisible()) {\n // applying default:\n this.setVisible(true);\n }\n\n if (config?.layerGroupsVisibility) {\n this.setVisible(config.layerGroupsVisibility.visible, { layerGroups: config.layerGroupsVisibility });\n }\n\n // We merge the given config with the previous one to ensure init config parameters are kept:\n // (the init config can have more parameters than the runtime one)\n return this.config || config ? { ...this.config, ...config } : undefined;\n }\n\n /**\n * Checks if any base map layers are currently visible.\n *\n * @returns `true` if at least one base map layer is visible, `false` if all are hidden.\n *\n * @remarks\n * This checks the actual visibility state of layers in the map, not just the\n * module's configuration setting.\n *\n * @example\n * ```typescript\n * if (baseMap.isVisible()) {\n * console.log('Base map is rendered');\n * } else {\n * console.log('Base map is hidden');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.vectorTiles.isAnyLayerVisible();\n }\n\n /**\n * Sets the visibility of base map layers.\n *\n * @param visible - `true` to show layers, `false` to hide them.\n * @param options - Optional settings for fine-grained control.\n * @param options.layerGroups - Target specific layer groups instead of all layers.\n *\n * @remarks\n * **Behavior:**\n * - Without `options.layerGroups`: Affects all base map layers\n * - With `options.layerGroups`: Affects only specified layer groups\n * - Changes are applied immediately if map is ready\n *\n * **Layer Groups:**\n * Available groups: `land`, `water`, `borders`, `buildings2D`, `buildings3D`,\n * `houseNumbers`, `roadLines`, `roadLabels`, `roadShields`, `placeLabels`,\n * `smallerTownLabels`, `cityLabels`, `capitalLabels`, `stateLabels`, `countryLabels`\n *\n * @example\n * Show/hide all layers:\n * ```typescript\n * baseMap.setVisible(false); // Hide everything\n * baseMap.setVisible(true); // Show everything\n * ```\n *\n * @example\n * Control specific groups:\n * ```typescript\n * // Hide only buildings\n * baseMap.setVisible(false, {\n * layerGroups: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D']\n * }\n * });\n *\n * // Show everything except labels\n * baseMap.setVisible(true, {\n * layerGroups: {\n * mode: 'exclude',\n * names: ['placeLabels', 'cityLabels', 'countryLabels']\n * }\n * });\n * ```\n *\n * @example\n * Toggle visibility:\n * ```typescript\n * const isVisible = baseMap.isVisible();\n * baseMap.setVisible(!isVisible); // Toggle\n * ```\n */\n setVisible(visible: boolean, options?: { layerGroups?: BaseMapLayerGroups }): void {\n if (!options?.layerGroups) {\n // We remove the layer groups visibility from the config if it was there:\n delete this.config?.layerGroupsVisibility;\n this.config = { ...this.config, visible };\n } else {\n this.config = { ...this.config, layerGroupsVisibility: { ...options.layerGroups, visible } };\n }\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.vectorTiles.setLayersVisible(\n visible,\n options?.layerGroups && buildLayerGroupFilter(options.layerGroups),\n );\n }\n }\n\n /**\n * Gets the events interface for this module to handle user interactions.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a base map feature\n * - `contextmenu`: User right-clicks on a feature\n * - `hover`: Mouse enters a feature\n * - `long-hover`: Mouse hovers over a feature for extended time\n *\n * **Event Handler Signature:**\n * ```typescript\n * (feature: MapGeoJSONFeature, lngLat: LngLat, allFeatures: MapGeoJSONFeature[]) => void\n * ```\n *\n * @example\n * Register click handler:\n * ```typescript\n * baseMap.events.on('click', (feature, lngLat) => {\n * console.log('Clicked on:', feature.properties);\n * console.log('At coordinates:', lngLat);\n * });\n * ```\n *\n * @example\n * Multiple event types:\n * ```typescript\n * // Show tooltip on hover\n * baseMap.events.on('hover', (feature) => {\n * showTooltip(feature.properties.name);\n * });\n *\n * // Handle clicks\n * baseMap.events.on('click', (feature) => {\n * selectFeature(feature.id);\n * });\n *\n * // Clean up\n * baseMap.events.off('hover');\n * baseMap.events.off('click');\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.vectorTiles, this.config?.events);\n }\n}\n","import type { MapModuleCommonConfig } from '../../shared';\n\n/**\n * Available base map layer group identifiers.\n *\n * @remarks\n * Use these names with {@link BaseMapModule} to control layer visibility.\n *\n * @see {@link BaseMapLayerGroupName}\n * @see {@link BaseMapModuleInitConfig.layerGroupsFilter}\n *\n * @group Base Map\n */\nexport const baseMapLayerGroupNames = [\n 'land',\n 'water',\n 'borders',\n 'buildings2D',\n 'buildings3D',\n 'houseNumbers',\n 'roadLines',\n 'roadLabels',\n 'roadShields',\n 'placeLabels',\n 'smallerTownLabels',\n 'cityLabels',\n 'capitalLabels',\n 'stateLabels',\n 'countryLabels',\n] as const;\n\n/**\n * Name of a base map layer group.\n *\n * Identifies specific categories of base map layers that can be controlled together.\n *\n * @remarks\n * **Available Layer Groups:**\n * - `land` - Land areas and terrain\n * - `water` - Water bodies (oceans, lakes, rivers)\n * - `borders` - Country and administrative boundaries\n * - `buildings2D` - 2D building footprints\n * - `buildings3D` - 3D building models\n * - `houseNumbers` - House number labels\n * - `roadLines` - Road line geometries\n * - `roadLabels` - Street name labels\n * - `roadShields` - Highway shields (e.g., I-95, A1)\n * - `placeLabels` - General place labels\n * - `smallerTownLabels` - Small town/village labels\n * - `cityLabels` - City labels\n * - `capitalLabels` - Capital city labels\n * - `stateLabels` - State/province labels\n * - `countryLabels` - Country name labels\n *\n * @example\n * ```typescript\n * const group: BaseMapLayerGroupName = 'roadLines';\n * const labels: BaseMapLayerGroupName[] = ['cityLabels', 'countryLabels'];\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroupName = (typeof baseMapLayerGroupNames)[number];\n\n/**\n * Layer group visibility configuration with explicit visible state.\n *\n * Extends {@link BaseMapLayerGroups} to include a visibility flag, allowing\n * you to show or hide specific groups of base map layers.\n *\n * @example\n * ```typescript\n * // Hide all buildings\n * const config: BaseMapLayerGroupsVisibility = {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * };\n *\n * // Show only roads\n * const roadsOnly: BaseMapLayerGroupsVisibility = {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels'],\n * visible: true\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroupsVisibility = BaseMapLayerGroups & { visible: boolean };\n\n/**\n * Layer group filter for selective base map display.\n *\n * Defines which layer groups to include or exclude from the base map module.\n * Can be expressed as explicit inclusions (show only these) or exclusions\n * (show all except these).\n *\n * @remarks\n * **Filter Modes:**\n * - `include`: Only the specified groups are shown, all others are hidden\n * - `exclude`: All groups are shown except the specified ones\n *\n * **Common Use Cases:**\n * - Show only roads and labels (minimal map)\n * - Hide buildings for cleaner appearance\n * - Show only water and land (base terrain)\n * - Remove labels for overlay maps\n *\n * @example\n * ```typescript\n * // Show only roads and borders\n * const roadsOnly: BaseMapLayerGroups = {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'borders']\n * };\n *\n * // Show everything except buildings\n * const noBuildings: BaseMapLayerGroups = {\n * mode: 'exclude',\n * names: ['buildings2D', 'buildings3D']\n * };\n *\n * // Show only terrain (no labels, no roads)\n * const terrainOnly: BaseMapLayerGroups = {\n * mode: 'include',\n * names: ['land', 'water']\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapLayerGroups = {\n /**\n * Filter mode determining whether groups are included or excluded.\n *\n * @remarks\n * - `include`: Only the specified groups are considered, all others are ignored\n * - `exclude`: All base map groups except the specified ones are considered\n *\n * @example\n * ```typescript\n * mode: 'include' // Whitelist approach\n * mode: 'exclude' // Blacklist approach\n * ```\n */\n mode: 'include' | 'exclude';\n\n /**\n * Names of the layer groups to include or exclude.\n *\n * @remarks\n * The meaning depends on the `mode`:\n * - In `include` mode: Only these groups will be shown\n * - In `exclude` mode: These groups will be hidden, all others shown\n *\n * @example\n * ```typescript\n * // Show only these\n * names: ['roadLines', 'roadLabels', 'water']\n *\n * // Hide these\n * names: ['buildings2D', 'buildings3D', 'houseNumbers']\n * ```\n */\n names: BaseMapLayerGroupName[];\n};\n\n/**\n * Configuration for the BaseMapModule (initialization or runtime).\n *\n * Controls visibility and behavior of base map layer groups. Can be used both\n * during module initialization and for runtime updates.\n *\n * @remarks\n * This configuration allows fine-grained control over which base map elements\n * are displayed, enabling you to create custom map appearances for different\n * use cases.\n *\n * @example\n * ```typescript\n * // Hide specific layer groups\n * const config: BaseMapModuleConfig = {\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * }\n * };\n *\n * // Show only certain groups\n * const minimalConfig: BaseMapModuleConfig = {\n * visible: true,\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['roadLines', 'water', 'land'],\n * visible: true\n * }\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapModuleConfig = MapModuleCommonConfig & {\n /**\n * Controls the visibility of all layers associated with this module.\n *\n * @default true\n */\n visible?: boolean;\n\n /**\n * Optional visibility configuration for specific layer groups.\n *\n * @remarks\n * **Important:** The layer groups specified here must be included in the\n * module (not excluded by `layerGroupsFilter` during initialization).\n *\n * Use this to control visibility of layer groups at runtime without\n * reinitializing the module.\n *\n * @example\n * ```typescript\n * // Hide all building layers\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['buildings2D', 'buildings3D'],\n * visible: false\n * }\n *\n * // Show only labels\n * layerGroupsVisibility: {\n * mode: 'include',\n * names: ['placeLabels', 'cityLabels', 'countryLabels'],\n * visible: true\n * }\n * ```\n */\n layerGroupsVisibility?: BaseMapLayerGroupsVisibility;\n};\n\n/**\n * Initialization configuration for the BaseMapModule.\n *\n * Extends {@link BaseMapModuleConfig} with additional options available only\n * during module initialization.\n *\n * @remarks\n * **Initialization vs Runtime:**\n * - `layerGroupsFilter`: Only available at init - determines which groups to load\n * - `layerGroupsVisibility`: Available at init and runtime - controls visibility\n *\n * **Use Cases:**\n * - Create minimal maps with only essential layers\n * - Build custom map styles by excluding certain features\n * - Optimize performance by not loading unnecessary layers\n *\n * @example\n * ```typescript\n * // Initialize with only roads and water\n * const initConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['roadLines', 'roadLabels', 'water', 'land']\n * },\n * visible: true\n * };\n *\n * // Exclude buildings from initialization\n * const noBuildingsConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings2D', 'buildings3D', 'houseNumbers']\n * }\n * };\n *\n * // Minimal map for data overlay\n * const overlayBaseConfig: BaseMapModuleInitConfig = {\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land', 'borders']\n * },\n * visible: true\n * };\n * ```\n *\n * @group Base Map\n */\nexport type BaseMapModuleInitConfig = BaseMapModuleConfig & {\n /**\n * Layer groups to include/exclude during module initialization.\n *\n * @remarks\n * **One-time configuration:** This can only be set during initialization.\n * Once the module is created, you cannot change which groups are loaded,\n * only their visibility via `layerGroupsVisibility`.\n *\n * @example\n * ```typescript\n * // Load only essential layers\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['land', 'water', 'roadLines', 'borders']\n * }\n *\n * // Load everything except 3D buildings\n * layerGroupsFilter: {\n * mode: 'exclude',\n * names: ['buildings3D']\n * }\n *\n * // Minimal overlay map\n * layerGroupsFilter: {\n * mode: 'include',\n * names: ['water', 'land']\n * }\n * ```\n */\n layerGroupsFilter?: BaseMapLayerGroups;\n};\n","// ─── Geometry (utility) ──────────────────────────────────────────────────────\n\n/** The world ring produced by turf.mask */\nexport const TURF_MASK_WORLD_RING = [\n [180, 90],\n [-180, 90],\n [-180, -90],\n [180, -90],\n [180, 90],\n];\n\n// ─── Default geometry styling ─────────────────────────────────────────────────\n\n/** Default fill/line color for geometries. */\nexport const DEFAULT_COLOR = '#0A3653';\n\n/** Default fill opacity. */\nexport const DEFAULT_FILL_OPACITY = 0.15;\n\n/** Default line color. */\nexport const DEFAULT_LINE_OPACITY = 1;\n\n/** Default line width in pixels. */\nexport const DEFAULT_LINE_WIDTH = 2;\n\n// ─── Themed geometry styling (outline / filled / inverted) ───────────────────\n\n/** Fill opacity for the outline theme (transparent fill). */\nexport const OUTLINE_THEME_FILL_OPACITY = 0;\n\n/** Line color for the outline theme. */\nexport const OUTLINE_THEME_LINE_COLOR = '#555555';\n\n/** Line opacity for the outline theme. */\nexport const OUTLINE_THEME_LINE_OPACITY = 0.9;\n\n/** Line width for the outline theme. */\nexport const OUTLINE_THEME_LINE_WIDTH = 5;\n\n/** Fill opacity for the filled/inverted theme. */\nexport const FILLED_THEME_FILL_OPACITY = 0.6;\n\n/** Line color for the filled/inverted theme. */\nexport const FILLED_THEME_LINE_COLOR = 'grey';\n\n/** Line opacity for the filled/inverted theme. */\nexport const FILLED_THEME_LINE_OPACITY = 1;\n\n/** Line width for the filled/inverted theme. */\nexport const FILLED_THEME_LINE_WIDTH = 1;\n\n// ─── Label styling ────────────────────────────────────────────────────────────\n\n/** Text color shared by title and border labels. */\nexport const TITLE_COLOR = '#333333';\n\n/** Text halo color shared by title and border labels. */\nexport const TITLE_HALO_COLOR = '#FFFFFF';\n\n/** Text size for geometry title (point) labels. */\nexport const TITLE_SIZE = 12;\n\n/** Text padding for geometry title (point) labels. */\nexport const TITLE_PADDING = 5;\n\n/** Minimum zoom level for border labels. */\nexport const BORDER_LABEL_MIN_ZOOM = 3;\n\n/** Text size for border labels. */\nexport const BORDER_LABEL_TEXT_SIZE = 15;\n\n/** Symbol spacing for border labels, in pixels. */\nexport const BORDER_LABEL_SYMBOL_SPACING = 200;\n\n/** Text halo width for border labels, in pixels. */\nexport const BORDER_LABEL_TEXT_HALO_WIDTH = 2;\n","import { isNil } from 'lodash-es';\nimport type { FillLayerSpecification, LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate, SymbolLayerSpecWithoutSource } from '../../shared';\nimport { MAP_BOLD_FONT } from '../../shared/layers/commonLayerProps';\nimport type { GeometriesModuleConfig } from '../types/geometriesModuleConfig';\nimport {\n BORDER_LABEL_MIN_ZOOM,\n BORDER_LABEL_SYMBOL_SPACING,\n BORDER_LABEL_TEXT_HALO_WIDTH,\n BORDER_LABEL_TEXT_SIZE,\n DEFAULT_COLOR,\n DEFAULT_FILL_OPACITY,\n DEFAULT_LINE_OPACITY,\n DEFAULT_LINE_WIDTH,\n OUTLINE_THEME_FILL_OPACITY,\n OUTLINE_THEME_LINE_COLOR,\n OUTLINE_THEME_LINE_OPACITY,\n OUTLINE_THEME_LINE_WIDTH,\n TITLE_COLOR,\n TITLE_HALO_COLOR,\n TITLE_PADDING,\n TITLE_SIZE,\n} from './constants';\n\n/**\n * @ignore\n */\nexport const geometryFillSpec: LayerSpecTemplate<FillLayerSpecification> = {\n type: 'fill',\n paint: {\n 'fill-color': ['coalesce', ['get', 'color'], DEFAULT_COLOR],\n 'fill-opacity': ['case', ['==', ['get', 'theme'], 'outline'], OUTLINE_THEME_FILL_OPACITY, DEFAULT_FILL_OPACITY],\n 'fill-antialias': false,\n },\n};\n\n/**\n * @ignore\n */\nexport const geometryOutlineSpec: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n paint: {\n 'line-color': [\n 'case',\n ['==', ['get', 'theme'], 'outline'],\n ['coalesce', ['get', 'color'], OUTLINE_THEME_LINE_COLOR],\n ['coalesce', ['get', 'color'], DEFAULT_COLOR],\n ],\n 'line-opacity': ['case', ['==', ['get', 'theme'], 'outline'], OUTLINE_THEME_LINE_OPACITY, DEFAULT_LINE_OPACITY],\n 'line-width': ['case', ['==', ['get', 'theme'], 'outline'], OUTLINE_THEME_LINE_WIDTH, DEFAULT_LINE_WIDTH],\n },\n};\n\n/**\n * Builds Geometry layer specifications for fill and outline layers.\n * @ignore\n */\nexport const buildGeometryLayerSpecs = (\n fillLayerId: string,\n outlineLayerId: string,\n config?: GeometriesModuleConfig,\n): [SymbolLayerSpecWithoutSource, SymbolLayerSpecWithoutSource] => {\n const colorConfig = config?.colorConfig;\n const lineConfig = config?.lineConfig;\n\n const fillLayerSpec = {\n ...geometryFillSpec,\n id: fillLayerId,\n paint: {\n ...geometryFillSpec.paint,\n ...(!isNil(colorConfig?.fillOpacity) && { 'fill-opacity': colorConfig?.fillOpacity }),\n ...(colorConfig?.fillColor && { 'fill-color': ['get', 'color'] }),\n },\n } as unknown as SymbolLayerSpecWithoutSource;\n\n const outlineLayerSpec = {\n ...geometryOutlineSpec,\n id: outlineLayerId,\n paint: {\n ...geometryOutlineSpec.paint,\n ...(!isNil(lineConfig?.lineColor) && { 'line-color': lineConfig?.lineColor }),\n ...(!isNil(lineConfig?.lineWidth) && { 'line-width': lineConfig?.lineWidth }),\n ...(!isNil(lineConfig?.lineOpacity) && { 'line-opacity': lineConfig?.lineOpacity }),\n },\n } as unknown as SymbolLayerSpecWithoutSource;\n\n return [fillLayerSpec, outlineLayerSpec];\n};\n\n/**\n * Build geometry layer specification for title.\n * @ignore\n */\nexport const buildGeometryTitleLayerSpec = (\n layerId: string,\n config?: GeometriesModuleConfig,\n): Omit<SymbolLayerSpecification, 'source'> => {\n const textConfig = config?.textConfig;\n\n return {\n type: 'symbol',\n id: layerId,\n layout: {\n 'text-field': ['get', 'title'],\n ...(textConfig?.textField && { 'text-field': textConfig.textField }),\n 'text-padding': TITLE_PADDING,\n 'text-size': TITLE_SIZE,\n 'text-font': [MAP_BOLD_FONT],\n 'symbol-placement': 'point',\n },\n paint: {\n 'text-color': TITLE_COLOR,\n 'text-halo-color': TITLE_HALO_COLOR,\n 'text-halo-width': ['interpolate', ['linear'], ['zoom'], 6, 1, 10, 1.5],\n 'text-translate-anchor': 'viewport',\n },\n };\n};\n\n/**\n * Build geometry layer specification for line labels.\n * Adds labels along the polygon border.\n * @ignore\n */\nexport const buildGeometryLineLabelLayerSpec = (\n layerId: string,\n config?: GeometriesModuleConfig,\n): Omit<SymbolLayerSpecification, 'source'> => {\n const lineLabelConfig = config?.lineLabelConfig;\n const minzoom = lineLabelConfig?.minZoom ?? BORDER_LABEL_MIN_ZOOM;\n\n return {\n type: 'symbol',\n id: layerId,\n minzoom,\n layout: {\n 'text-field': ['get', 'title'],\n 'symbol-placement': 'line',\n 'text-size': lineLabelConfig?.textSize ?? BORDER_LABEL_TEXT_SIZE,\n 'text-font': [MAP_BOLD_FONT],\n 'symbol-spacing': lineLabelConfig?.symbolSpacing ?? BORDER_LABEL_SYMBOL_SPACING,\n 'text-keep-upright': true,\n 'text-offset': [0, 1],\n },\n paint: {\n 'text-color': lineLabelConfig?.textColor ?? TITLE_COLOR,\n 'text-halo-color': lineLabelConfig?.textHaloColor ?? TITLE_HALO_COLOR,\n 'text-halo-width': lineLabelConfig?.textHaloWidth ?? BORDER_LABEL_TEXT_HALO_WIDTH,\n },\n };\n};\n","export const colorPalettes = {\n warm: [\n '#793F0D',\n '#AC703D',\n '#C38E63',\n '#E49969',\n '#E5AE86',\n '#EEC5A9',\n '#6E7649',\n '#9D9754',\n '#C7C397',\n '#B4A851',\n '#DFD27C',\n '#E7E3B5',\n '#846D74',\n '#B7A6AD',\n '#D3C9CE',\n ],\n browns: [\n '#edc4b3',\n '#e6b8a2',\n '#deab90',\n '#d69f7e',\n '#cd9777',\n '#c38e70',\n '#b07d62',\n '#9d6b53',\n '#8a5a44',\n '#774936',\n ],\n cold: ['#344464', '#548ca4', '#549cac', '#2c445c', '#a4ccd4', '#acbccc', '#b4c4d4', '#acd4cc', '#5c8ca4'],\n fadedBlues: [\n '#152033',\n '#1d2d44',\n '#2e455d',\n '#3e5c76',\n '#4c6884',\n '#597491',\n '#67809e',\n '#6e86a5',\n '#7b91ad',\n '#879bb4',\n ],\n blues: ['#03045e', '#023e8a', '#0077b6', '#0096c7', '#00b4d8', '#48cae4', '#90e0ef', '#ade8f4', '#caf0f8'],\n greens: ['#004b23', '#006400', '#007200', '#008000', '#38b000', '#70e000', '#9ef01a', '#ccff33'],\n fadedGreenToBlue: [\n '#d9ed92',\n '#b5e48c',\n '#99d98c',\n '#76c893',\n '#52b69a',\n '#34a0a4',\n '#168aad',\n '#1a759f',\n '#1e6091',\n '#184e77',\n ],\n blueToRed: [\n '#033270',\n '#1368aa',\n '#4091c9',\n '#9dcee2',\n '#fedfd4',\n '#f29479',\n '#f26a4f',\n '#ef3c2d',\n '#cb1b16',\n '#65010c',\n ],\n greenToYellow: [\n '#007f5f',\n '#2b9348',\n '#55a630',\n '#80b918',\n '#aacc00',\n '#bfd200',\n '#d4d700',\n '#dddf00',\n '#eeef20',\n '#ffff3f',\n ],\n pastel: [\n '#fec5bb',\n '#fcd5ce',\n '#fae1dd',\n '#f8edeb',\n '#e8e8e4',\n '#d8e2dc',\n '#ece4db',\n '#ffe5d9',\n '#ffd7ba',\n '#fec89a',\n ],\n retro: [\n '#f94144',\n '#f3722c',\n '#f8961e',\n '#f9844a',\n '#f9c74f',\n '#90be6d',\n '#43aa8b',\n '#4d908e',\n '#577590',\n '#277da1',\n ],\n contrastRetro: [\n '#001219',\n '#005f73',\n '#0a9396',\n '#94d2bd',\n '#e9d8a6',\n '#ee9b00',\n '#ca6702',\n '#bb3e03',\n '#ae2012',\n '#9b2226',\n ],\n fadedRainbow: ['#ffadad', '#ffd6a5', '#fdffb6', '#caffbf', '#9bf6ff', '#a0c4ff', '#bdb2ff', '#ffc6ff', '#fffffc'],\n pastelRainbow: [\n '#54478c',\n '#2c699a',\n '#048ba8',\n '#0db39e',\n '#16db93',\n '#83e377',\n '#b9e769',\n '#efea5a',\n '#f1c453',\n '#f29e4c',\n ],\n};\n\nexport type ColorPaletteOptions = keyof typeof colorPalettes;\nexport const colorPaletteIDs = Object.keys(colorPalettes) as ColorPaletteOptions[];\n","import {\n bboxCenter,\n bboxFromCoordsArray,\n generateId,\n type PolygonFeature,\n PolygonFeatures,\n} from '@tomtom-org/maps-sdk/core';\nimport { mask } from '@turf/turf';\nimport type { Feature, FeatureCollection, GeoJsonProperties, MultiPolygon, Point, Polygon, Position } from 'geojson';\nimport type { DataDrivenPropertyValueSpecification } from 'maplibre-gl';\nimport { type ColorPaletteOptions, colorPalettes } from './layers/colorPalettes';\nimport type { GeometriesModuleConfig } from './types/geometriesModuleConfig';\nimport type { DisplayGeometryProps, ExtraGeometryDisplayProps } from './types/geometryDisplayProps';\nimport type { GeometryTheme } from './types/geometryTheme';\n\n/**\n * Build geometry Title. The type can be a string or a Maplibre expression.\n * @param feature - Geometry\n * @param config - Geometry module configuration\n * @returns\n */\nconst buildTitle = (\n feature: Feature<Polygon | MultiPolygon, DisplayGeometryProps | GeoJsonProperties>,\n config: GeometriesModuleConfig,\n): DataDrivenPropertyValueSpecification<string> | string | undefined => {\n if (config.textConfig?.textField) {\n return config.textConfig.textField;\n }\n return feature.properties?.address?.freeformAddress;\n};\n\n/**\n * Builds a geometry color string or MapLibre expression.\n * @param config - Geometry module configuration\n * @param index - Number to use as index to pick color from palette option\n */\nconst buildColor = (\n config: GeometriesModuleConfig,\n index: number,\n): DataDrivenPropertyValueSpecification<string> | string | undefined => {\n const color = config?.colorConfig?.fillColor;\n if (typeof color === 'string' && colorPalettes[color as ColorPaletteOptions]) {\n const palette = colorPalettes[color as ColorPaletteOptions];\n return palette[index % palette.length];\n }\n return color;\n};\n\n/** Finds the longest coordinate array in a MultiPolygon for title placement. */\nconst getLongestArray = (coordinates: Position[][][]) =>\n coordinates.flat().reduce((result, coord) => (coord.length > result.length ? coord : result), []);\n\n/**\n * Converts a polygon into a world-minus-polygon donut for the `'inverted'` theme.\n *\n * Uses `turf.mask` to punch the polygon as a hole in a world bounding box.\n *\n * @remarks\n * Called automatically when a feature or config has `theme: 'inverted'`.\n *\n * @group Geometries\n */\nexport const invertFeature = (feature: PolygonFeature): PolygonFeature => {\n const masked = mask(feature);\n return {\n ...feature,\n geometry: masked.geometry,\n } as PolygonFeature;\n};\n\ntype InputFeatureProps = {\n theme?: GeometryTheme;\n title?: string;\n color?: string;\n id?: string;\n};\n/**\n * Prepares polygon features for display by applying theme, colors, and titles.\n *\n * @param geometry - Polygon features to prepare.\n * @param config - Module configuration for styling.\n * @returns Processed features ready for MapLibre rendering.\n * @ignore\n */\nexport const prepareGeometryForDisplay = (\n geometry: PolygonFeatures<GeoJsonProperties | DisplayGeometryProps>,\n config: GeometriesModuleConfig = {},\n): PolygonFeatures<ExtraGeometryDisplayProps> => ({\n ...geometry,\n features: geometry.features.map((feature, index) => {\n const props = (feature.properties ?? {}) as InputFeatureProps;\n const effectiveTheme = props.theme ?? config.theme;\n const processedFeature = effectiveTheme === 'inverted' ? invertFeature(feature as PolygonFeature) : feature;\n const processedProps = (processedFeature.properties ?? {}) as InputFeatureProps;\n // buildTitle/buildColor may return MapLibre expressions; cast since output type expects string\n const title = (processedProps.title ?? buildTitle(processedFeature, config)) as string | undefined;\n const color = (processedProps.color ?? buildColor(config, index)) as string | undefined;\n return {\n ...processedFeature,\n properties: {\n ...processedFeature.properties,\n ...(effectiveTheme && { theme: effectiveTheme }),\n ...(title && { title }),\n ...(color && { color }),\n id: processedProps.id ?? generateId(),\n },\n };\n }),\n});\n\n/**\n * Create a Feature<Point> with coordinates where title will be placed.\n * If feature properties contains a coordinates value, it will use it.\n * In case there is not coordinates value, it will get the biggest Polygon inside a feature and calculate\n * the bounding box for those coordinates and finally calculate the bounding box center to place the title.\n * @param geometries\n * @returns\n * @ignore\n */\nexport const prepareTitleForDisplay = (geometries: PolygonFeatures): FeatureCollection<Point> => {\n const features = geometries.features.map((feature) => {\n let coordinates: Position[] | Position | null;\n\n if (feature.properties?.placeCoordinates) {\n coordinates = feature.properties?.placeCoordinates;\n } else if (feature.geometry.type === 'MultiPolygon') {\n const biggestPolygon = getLongestArray(feature.geometry.coordinates);\n const bbox = bboxFromCoordsArray(biggestPolygon);\n coordinates = (bbox && bboxCenter(bbox)) || null;\n } else {\n coordinates = feature.geometry.coordinates.flat();\n }\n\n const id = feature.id ?? feature.properties?.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...feature.properties,\n id, // we need id in properties due to promoteId feature\n },\n } as Feature<Point>;\n });\n\n return { type: 'FeatureCollection', bbox: geometries.bbox, features };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { FeatureCollection, Point } from 'geojson';\nimport type { SymbolLayerSpecification } from 'maplibre-gl';\nimport type { SymbolLayerSpecWithoutSource, ToBeAddedLayerSpec } from '../shared';\nimport { AbstractMapModule, EventsModule, GeoJSONSourceWithLayers, mapStyleLayerIDs } from '../shared';\nimport { changeLayerProps, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport {\n buildGeometryLayerSpecs,\n buildGeometryLineLabelLayerSpec,\n buildGeometryTitleLayerSpec,\n} from './layers/geometryLayers';\nimport { prepareGeometryForDisplay, prepareTitleForDisplay } from './prepareGeometryForDisplay';\nimport type {\n GeometriesModuleConfig,\n GeometryBeforeLayerConfig,\n GeometryTextConfig,\n} from './types/geometriesModuleConfig';\n\n/**\n * IDs of sources and layers from a geometry module.\n */\ntype GeometrySourcesWithLayers = {\n geometry: GeoJSONSourceWithLayers<PolygonFeatures>;\n geometryLabel: GeoJSONSourceWithLayers<FeatureCollection<Point>>;\n};\n\n/**\n * Geometries Module for displaying polygon areas with custom styling on the map.\n *\n * This module enables visualization of geographic areas (polygons) with customizable\n * colors, borders, and labels. Ideal for displaying search results, administrative\n * boundaries, service areas, or any polygon-based geographic data.\n *\n * @remarks\n * **Features:**\n * - Display single or multiple polygon geometries\n * - Customizable fill colors and opacity\n * - Configurable borders (color, width, opacity)\n * - Optional text labels for geometries\n * - Support for data-driven styling via MapLibre expressions\n * - Layer ordering control\n * - Event handling for user interactions\n *\n * **Data Format:**\n * - Accepts GeoJSON Polygon and MultiPolygon features\n * - Supports FeatureCollection for multiple geometries\n * - Compatible with TomTom Search API geometry results\n *\n * **Styling:**\n * - Use predefined color palettes or custom colors\n * - Apply MapLibre expressions for dynamic styling\n * - Per-feature styling via feature properties\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { GeometriesModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Initialize module\n * const geometriesModule = await GeometriesModule.get(map);\n *\n * // Display a polygon\n * await geometriesModule.show({\n * type: 'Feature',\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.88, 52.37], [4.89, 52.37], [4.89, 52.38], [4.88, 52.38], [4.88, 52.37]]]\n * },\n * properties: {\n * title: 'Area of Interest'\n * }\n * });\n * ```\n *\n * @example\n * Custom styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * fillColor: '#FF5733',\n * fillOpacity: 0.3\n * },\n * lineConfig: {\n * lineColor: '#C70039',\n * lineWidth: 3\n * },\n * textConfig: {\n * textField: ['get', 'name']\n * }\n * });\n *\n * await geometriesModule.show(polygonFeatures);\n * ```\n *\n * @example\n * Multiple geometries with different colors:\n * ```typescript\n * await geometriesModule.show({\n * type: 'FeatureCollection',\n * features: [\n * {\n * type: 'Feature',\n * geometry: { type: 'Polygon', coordinates: [...] },\n * properties: { color: '#FF0000', title: 'Red Zone' }\n * },\n * {\n * type: 'Feature',\n * geometry: { type: 'Polygon', coordinates: [...] },\n * properties: { color: '#00FF00', title: 'Green Zone' }\n * }\n * ]\n * });\n * ```\n *\n * @example\n * Event handling:\n * ```typescript\n * geometriesModule.events.on('click', (feature, lngLat) => {\n * console.log('Clicked geometry:', feature.properties.title);\n * console.log('At coordinates:', lngLat);\n * });\n *\n * geometriesModule.events.on('hover', (feature) => {\n * showTooltip(feature.properties.title);\n * });\n * ```\n *\n * @see [Geometries Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/geometries)\n *\n * @group Geometries\n */\nexport class GeometriesModule extends AbstractMapModule<GeometrySourcesWithLayers, GeometriesModuleConfig> {\n private static lastInstanceIndex = -1;\n\n private titleLayerSpecs!: SymbolLayerSpecWithoutSource;\n private geometryFillLayerSpecs!: SymbolLayerSpecWithoutSource;\n private geometryOutlineLayerSpecs!: SymbolLayerSpecWithoutSource;\n private lineLabelLayerSpecs: SymbolLayerSpecWithoutSource | null = null;\n\n private sourceID!: string;\n private fillLayerID!: string;\n private outlineLayerID!: string;\n private lineLabelLayerID!: string;\n\n private titleSourceID!: string;\n private titleLayerID!: string;\n\n // Cached for restoreDataAndConfigImpl on style change.\n private lastRawInput: PolygonFeatures | null = null;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n *\n * @remarks\n * **Configuration Options:**\n * - `colorConfig`: Fill color and opacity settings\n * - `lineConfig`: Border/outline styling\n * - `textConfig`: Label display configuration\n * - `beforeLayerConfig`: Layer ordering (place above/below other layers)\n *\n * **Multiple Instances:**\n * You can create multiple GeometriesModule instances on the same map,\n * each managing different sets of geometries with different styles.\n *\n * @example\n * Default initialization:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map);\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * fillColor: 'blue',\n * fillOpacity: 0.25\n * },\n * lineConfig: {\n * lineColor: 'darkblue',\n * lineWidth: 2,\n * lineOpacity: 0.8\n * },\n * textConfig: {\n * textField: ['get', 'title']\n * },\n * beforeLayerConfig: 'top'\n * });\n * ```\n *\n * @example\n * Data-driven styling:\n * ```typescript\n * const geometriesModule = await GeometriesModule.get(map, {\n * colorConfig: {\n * // Color based on feature properties\n * fillColor: [\n * 'match',\n * ['get', 'type'],\n * 'residential', '#FFEB3B',\n * 'commercial', '#2196F3',\n * 'industrial', '#9E9E9E',\n * '#E0E0E0' // default\n * ],\n * fillOpacity: 0.4\n * }\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: GeometriesModuleConfig): Promise<GeometriesModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new GeometriesModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: GeometriesModuleConfig) {\n super('geojson', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: GeometriesModuleConfig, restore?: boolean): GeometrySourcesWithLayers {\n if (!restore) {\n GeometriesModule.lastInstanceIndex++;\n this.sourceID = `geometry-${GeometriesModule.lastInstanceIndex}`;\n this.titleSourceID = `geometryTitle-${GeometriesModule.lastInstanceIndex}`;\n const layerIdPrefix = `geometry-${GeometriesModule.lastInstanceIndex}`;\n this.fillLayerID = `${layerIdPrefix}_Fill`;\n this.outlineLayerID = `${layerIdPrefix}_Outline`;\n this.lineLabelLayerID = `${layerIdPrefix}_LineLabel`;\n this.titleLayerID = `${layerIdPrefix}_Title`;\n }\n\n const [geometryFillSpec, geometryOutlineSpec] = buildGeometryLayerSpecs(\n this.fillLayerID,\n this.outlineLayerID,\n config,\n );\n const titleLayerSpec = buildGeometryTitleLayerSpec(this.titleLayerID, config);\n this.titleLayerSpecs = titleLayerSpec;\n this.geometryFillLayerSpecs = geometryFillSpec;\n this.geometryOutlineLayerSpecs = geometryOutlineSpec;\n\n const lineLabelSpec =\n config?.lineLabelConfig === undefined\n ? null\n : buildGeometryLineLabelLayerSpec(this.lineLabelLayerID, config);\n this.lineLabelLayerSpecs = lineLabelSpec as SymbolLayerSpecWithoutSource | null;\n\n return {\n geometry: new GeoJSONSourceWithLayers(this.mapLibreMap, this.sourceID, [\n { ...geometryFillSpec },\n { ...geometryOutlineSpec },\n ...(lineLabelSpec ? [lineLabelSpec as ToBeAddedLayerSpec<SymbolLayerSpecification>] : []),\n ]),\n geometryLabel: new GeoJSONSourceWithLayers(this.mapLibreMap, this.titleSourceID, [\n titleLayerSpec as ToBeAddedLayerSpec<SymbolLayerSpecification>,\n ]),\n };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: GeometriesModuleConfig | undefined) {\n if (config?.textConfig || config?.colorConfig || config?.lineConfig) {\n this.updateLayerAndData(config);\n }\n if (config?.beforeLayerConfig) {\n this.moveBeforeLayer(config.beforeLayerConfig);\n }\n return config;\n }\n\n private moveBeforeLayerID(beforeLayerId?: string) {\n for (const layer of this.sourcesWithLayers.geometry.sourceAndLayerIDs.layerIDs) {\n this.mapLibreMap.moveLayer(layer, beforeLayerId);\n }\n }\n\n /**\n * Positions the geometry layers relative to other map layers.\n *\n * @param layerConfig - Layer positioning configuration.\n * Can be `'top'` to place above all layers, or a specific layer ID.\n *\n * @remarks\n * **Use Cases:**\n * - Place geometries above base map but below labels\n * - Ensure geometries appear above/below specific features\n * - Control visual hierarchy of multiple data layers\n *\n * **Available Layer IDs:**\n * Use predefined layer IDs from `mapStyleLayerIDs` or custom layer IDs.\n *\n * @example\n * ```typescript\n * import { mapStyleLayerIDs } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Place below labels\n * geometries.moveBeforeLayer(mapStyleLayerIDs.lowestLabel);\n *\n * // Place on top\n * geometries.moveBeforeLayer('top');\n * ```\n */\n moveBeforeLayer(layerConfig: GeometryBeforeLayerConfig) {\n this.config = { ...this.config, beforeLayerConfig: layerConfig };\n this.moveBeforeLayerID(layerConfig === 'top' ? this.titleLayerID : mapStyleLayerIDs[layerConfig]);\n }\n\n /**\n * Updates the text/label configuration for displayed geometries.\n *\n * @param textConfig - New text configuration settings.\n *\n * @remarks\n * **Configuration:**\n * - `textField`: MapLibre expression for label text content\n * - Supports dynamic text based on feature properties\n * - Changes apply to currently shown and future geometries\n *\n * @example\n * ```typescript\n * // Show feature property as label\n * geometries.applyTextConfig({\n * textField: ['get', 'name']\n * });\n *\n * // Conditional labels\n * geometries.applyTextConfig({\n * textField: [\n * 'case',\n * ['has', 'label'],\n * ['get', 'label'],\n * ['get', 'title']\n * ]\n * });\n * ```\n */\n applyTextConfig(textConfig: GeometryTextConfig) {\n const config = { ...this.config, textConfig };\n this.updateLayerAndData(config);\n // TODO: is this consistent with _applyConfig?\n this.sourcesWithLayers.geometryLabel.show(\n prepareTitleForDisplay(this.sourcesWithLayers.geometry.shownFeatures),\n );\n this.config = config;\n }\n\n private updateLayerAndData(config: GeometriesModuleConfig) {\n const [geometryFillSpec, geometryOutlineSpec] = buildGeometryLayerSpecs(\n this.fillLayerID,\n this.outlineLayerID,\n config,\n );\n const newTitleLayerSpecs = buildGeometryTitleLayerSpec(this.titleLayerID, config);\n\n changeLayerProps(geometryFillSpec, this.geometryFillLayerSpecs, this.mapLibreMap);\n changeLayerProps(geometryOutlineSpec, this.geometryOutlineLayerSpecs, this.mapLibreMap);\n changeLayerProps(newTitleLayerSpecs, this.titleLayerSpecs, this.mapLibreMap);\n\n this.geometryFillLayerSpecs = geometryFillSpec;\n this.geometryOutlineLayerSpecs = geometryOutlineSpec;\n this.titleLayerSpecs = newTitleLayerSpecs;\n\n if (this.lineLabelLayerSpecs) {\n const newLineLabelSpec = buildGeometryLineLabelLayerSpec(this.lineLabelLayerID, config);\n changeLayerProps(\n newLineLabelSpec as SymbolLayerSpecWithoutSource,\n this.lineLabelLayerSpecs,\n this.mapLibreMap,\n );\n this.lineLabelLayerSpecs = newLineLabelSpec as SymbolLayerSpecWithoutSource;\n }\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previousInput = this.lastRawInput;\n this.initSourcesWithLayers(this.config, true);\n this.config && this._applyConfig(this.config);\n if (previousInput) {\n // Fire-and-forget: base class requires sync, but show() is async.\n void this.show(previousInput);\n }\n }\n\n /**\n * Displays the given polygon geometries on the map.\n *\n * @param geometries - Polygon features to display. Can be a single Feature,\n * array of Features, or a FeatureCollection.\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown geometries\n * - Applies current module styling configuration\n * - Waits for module to be ready before displaying\n * - Automatically handles both Polygon and MultiPolygon types\n *\n * **Feature Properties:**\n * - `title`: Used for labels if text config is set\n * - `color`: Override fill color per feature\n * - Custom properties accessible in styling expressions\n *\n * @example\n * Single polygon:\n * ```typescript\n * await geometries.show({\n * type: 'Feature',\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.88, 52.37], [4.89, 52.37], [4.89, 52.38], [4.88, 52.37]]]\n * },\n * properties: {\n * title: 'Amsterdam Center',\n * color: '#FF5733'\n * }\n * });\n * ```\n *\n * @example\n * Multiple polygons:\n * ```typescript\n * await geometries.show({\n * type: 'FeatureCollection',\n * features: [\n * { type: 'Feature', geometry: {...}, properties: {...} },\n * { type: 'Feature', geometry: {...}, properties: {...} }\n * ]\n * });\n * ```\n *\n * @example\n * From search API response:\n * ```typescript\n * import { search } from '@tomtom-international/maps-sdk-js/services';\n *\n * const result = await search.geometrySearch({\n * query: 'Amsterdam',\n * geometryList: [{ type: 'CIRCLE', position: [52.37, 4.89], radius: 5000 }]\n * });\n *\n * if (result.results[0].dataSources?.geometry) {\n * await geometries.show(result.results[0].dataSources.geometry);\n * }\n * ```\n */\n async show(geometries: PolygonFeatures) {\n await this.waitUntilModuleReady();\n this.lastRawInput = geometries;\n const { transformFeaturesForDisplay } = this.config ?? {};\n const transformed = transformFeaturesForDisplay ? transformFeaturesForDisplay(geometries) : geometries;\n const geometry = this.sourcesWithLayers.geometry;\n geometry.show(prepareGeometryForDisplay(transformed, this.config));\n this.sourcesWithLayers.geometryLabel.show(prepareTitleForDisplay(geometry.shownFeatures));\n }\n\n /**\n * Removes all geometries from the map.\n *\n * @remarks\n * - Clears both geometry layers and labels\n * - Does not reset styling configuration\n * - Module remains initialized and ready for new data\n *\n * @example\n * ```typescript\n * // Clear displayed geometries\n * await geometries.clear();\n *\n * // Show new geometries\n * await geometries.show(newGeometries);\n * ```\n */\n async clear() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.geometry.clear();\n }\n\n /**\n * Returns the currently shown geometries.\n *\n * @returns The geometries currently displayed on the map.\n *\n * @remarks\n * Returns the exact data that was passed to the `show()` method.\n *\n * @example\n * ```typescript\n * const shown = geometriesModule.getShown();\n * console.log(`Geometries: ${shown.geometry.features.length}`);\n * ```\n */\n getShown() {\n return {\n geometry: this.sourcesWithLayers.geometry.shownFeatures,\n geometryLabel: this.sourcesWithLayers.geometryLabel.shownFeatures,\n };\n }\n\n /**\n * Gets the events interface for handling user interactions with geometries.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on a geometry\n * - `contextmenu`: User right-clicks on a geometry\n * - `hover`: Mouse enters a geometry\n * - `long-hover`: Mouse hovers over geometry for extended time\n *\n * **Event Features:**\n * - Receive the original feature data passed to `show()`\n * - Access feature properties and geometry\n * - Get click/hover coordinates\n *\n * @example\n * Basic event handling:\n * ```typescript\n * geometries.events.on('click', (feature, lngLat) => {\n * console.log('Clicked:', feature.properties);\n * console.log('Location:', lngLat);\n * });\n * ```\n *\n * @example\n * Multiple handlers:\n * ```typescript\n * // Highlight on hover\n * geometries.events.on('hover', (feature) => {\n * highlightGeometry(feature.id);\n * });\n *\n * // Show details on click\n * geometries.events.on('click', (feature) => {\n * showDetailPanel(feature.properties);\n * });\n *\n * // Context menu\n * geometries.events.on('contextmenu', (feature, lngLat) => {\n * showContextMenu(lngLat, feature);\n * });\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.geometry, this.config?.events);\n }\n}\n","import { type PolygonFeature, type PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { difference, featureCollection, mask } from '@turf/turf';\nimport type { GeometryTheme } from './types/geometryTheme';\n\n/** Custom label generator for reachable range features. */\nexport type ReachableRangeLabelFn = (feature: PolygonFeature, index: number) => string;\n\n/** Unit suffix for each budget type, used for auto-generated labels. */\nconst BUDGET_UNITS: Readonly<Record<string, string>> = {\n timeMinutes: 'min',\n distanceKM: 'km',\n remainingChargeCPT: '% remaining',\n spentChargePCT: '% spent',\n spentFuelLiters: 'L',\n};\n\n/**\n * Transforms range polygons into donut polygons for the 'inverted' theme.\n * Each donut fills the zone between two consecutive budget boundaries.\n */\nconst buildDonutFeatures = (features: PolygonFeature[]): PolygonFeature[] => {\n const donuts: PolygonFeature[] = features.map((feature, i) => {\n // Outermost band: world minus the largest range polygon.\n // Inner bands: previous (larger) range minus current (smaller) range.\n const donutGeo = i === 0 ? mask(feature) : difference(featureCollection([features[i - 1], feature]));\n return {\n ...feature,\n geometry: (donutGeo ?? feature).geometry,\n } as PolygonFeature;\n });\n\n return [...donuts, ...features.slice(-1)];\n};\n\n/**\n * Attaches labels and applies theme-specific geometry to reachable range polygon features.\n *\n * Features must be ordered **largest budget first** (e.g. 30 min, 20 min, 10 min).\n * For the `inverted` theme, polygons are converted to donuts and one extra innermost feature is appended.\n *\n * @param features Polygon features from `calculateReachableRange`, ordered largest budget first.\n * @param labels Label per feature in matching order (e.g. `['30 min', '20 min', '10 min']`).\n * @param theme Visual theme. Defaults to `'filled'`.\n *\n * @example\n * ```typescript\n * const features = [range30min, range20min, range10min];\n * const labels = ['30 min', '20 min', '10 min'];\n *\n * module.show({\n * type: 'FeatureCollection',\n * features: buildReachableRangeFeatures(features, labels, 'inverted'),\n * });\n * ```\n *\n * @group Geometries\n */\nexport const buildReachableRangeFeatures = (\n features: PolygonFeature[],\n labels: string[],\n theme: GeometryTheme = 'filled',\n): PolygonFeature[] => {\n const labelled = features.map((f, i) => ({\n ...f,\n properties: { ...f.properties, title: labels[i], theme },\n }));\n if (theme === 'inverted') {\n // buildDonutFeatures handles its own geometry transformation for multi-range donuts.\n // Set theme to 'filled' afterward to prevent prepareGeometryForDisplay from\n // applying invertFeature on already-transformed geometry.\n return buildDonutFeatures(labelled).map((f) => ({\n ...f,\n properties: { ...f.properties, theme: 'filled' as GeometryTheme },\n }));\n }\n return labelled;\n};\n\n/**\n * Prepares the output of `calculateReachableRanges` for display with {@link GeometriesModule}.\n *\n * Derives labels from each feature's `budget` property by default, applies theme-specific\n * geometry, and preserves the bounding box. Pass the returned value directly to `module.show()`.\n *\n * Note: when using {@link reachableRangeGeometryConfig} this is called automatically — you only\n * need this function for explicit control over labels or theme.\n *\n * Features must be ordered **largest budget first** (e.g. 30 min, 20 min, 10 min).\n *\n * @param result Raw output of `calculateReachableRanges`.\n * @param theme Visual theme. Defaults to `'filled'`.\n * @param label Custom label generator; when omitted, labels derived from `budget` property.\n *\n * @example\n * ```typescript\n * // explicit control:\n * module.show(prepareReachableRangesForDisplay(result, 'inverted'));\n * module.show(prepareReachableRangesForDisplay(result, 'filled', (f) => myLabel(f)));\n * ```\n *\n * @group Geometries\n */\nexport const prepareReachableRangesForDisplay = (\n result: PolygonFeatures,\n theme: GeometryTheme = 'filled',\n label?: ReachableRangeLabelFn,\n): PolygonFeatures => {\n const features = result.features as PolygonFeature[];\n const labels = features.map((f, i) => {\n if (label) {\n return label(f, i);\n }\n const budget = f.properties?.['budget'] as { type?: string; value?: number } | undefined;\n if (budget?.value !== undefined && budget?.type) {\n return `${budget.value} ${BUDGET_UNITS[budget.type] ?? budget.type}`;\n }\n return String(i + 1);\n });\n\n return {\n type: 'FeatureCollection',\n ...(result.bbox && { bbox: result.bbox }),\n features: buildReachableRangeFeatures(features, labels, theme),\n };\n};\n","import type { ColorPaletteOptions } from './layers/colorPalettes';\nimport {\n FILLED_THEME_FILL_OPACITY,\n FILLED_THEME_LINE_COLOR,\n FILLED_THEME_LINE_OPACITY,\n FILLED_THEME_LINE_WIDTH,\n OUTLINE_THEME_FILL_OPACITY,\n OUTLINE_THEME_LINE_COLOR,\n OUTLINE_THEME_LINE_OPACITY,\n OUTLINE_THEME_LINE_WIDTH,\n} from './layers/constants';\nimport { prepareReachableRangesForDisplay, type ReachableRangeLabelFn } from './prepareReachableRangesForDisplay';\nimport type { GeometriesModuleConfig } from './types/geometriesModuleConfig';\nimport type { GeometryTheme } from './types/geometryTheme';\n\n/**\n * Config for themed polygon display with palette-based coloring.\n *\n * Includes data-driven expressions so each feature's `theme` property controls its\n * styling, line labels config, and positions layers below map labels.\n *\n * For reachable ranges, use {@link reachableRangeGeometryConfig} which extends this\n * with auto-generated labels.\n *\n * @param palette Color palette for polygon fills. Defaults to `'fadedRainbow'`.\n *\n * @group Geometries\n */\nexport const themedGeometryConfig = (palette: ColorPaletteOptions = 'fadedRainbow'): GeometriesModuleConfig => ({\n beforeLayerConfig: 'lowestLabel',\n lineConfig: {\n // outline theme: thick colored line; filled/inverted: thin grey line\n lineWidth: ['case', ['==', ['get', 'theme'], 'outline'], OUTLINE_THEME_LINE_WIDTH, FILLED_THEME_LINE_WIDTH],\n lineColor: [\n 'case',\n ['==', ['get', 'theme'], 'outline'],\n ['coalesce', ['get', 'color'], OUTLINE_THEME_LINE_COLOR],\n FILLED_THEME_LINE_COLOR,\n ],\n lineOpacity: [\n 'case',\n ['==', ['get', 'theme'], 'outline'],\n OUTLINE_THEME_LINE_OPACITY,\n FILLED_THEME_LINE_OPACITY,\n ],\n },\n colorConfig: {\n fillColor: palette,\n // outline theme: transparent fill; filled/inverted: semi-transparent fill\n fillOpacity: [\n 'case',\n ['==', ['get', 'theme'], 'outline'],\n OUTLINE_THEME_FILL_OPACITY,\n FILLED_THEME_FILL_OPACITY,\n ],\n },\n lineLabelConfig: {},\n});\n\n/**\n * Returns a {@link GeometriesModuleConfig} for reachable ranges.\n *\n * Builds on {@link themedGeometryConfig} and overrides `transformFeaturesForDisplay` so the\n * raw output of `calculateReachableRanges` can be passed directly to {@link GeometriesModule.show}\n * — labels are derived from each feature's `budget` property.\n *\n * @param palette Color palette. Defaults to `'fadedRainbow'`.\n * @param theme Visual theme. Defaults to `'filled'`.\n * @param label Custom label generator; when omitted, labels are derived from `budget` property.\n *\n * @example\n * ```typescript\n * const module = await GeometriesModule.get(map, reachableRangeGeometryConfig());\n * const result = await calculateReachableRanges([...]);\n * module.show(result); // labels and theme applied automatically\n * ```\n *\n * @group Geometries\n */\nexport const reachableRangeGeometryConfig = (\n palette: ColorPaletteOptions = 'fadedRainbow',\n theme?: GeometryTheme,\n label?: ReachableRangeLabelFn,\n): GeometriesModuleConfig => ({\n ...themedGeometryConfig(palette),\n // Derives budget labels (e.g. '30 min') and applies theme-specific geometry transformations.\n transformFeaturesForDisplay: (result) => prepareReachableRangesForDisplay(result, theme, label),\n});\n","/**\n * Visual theme for polygon display with {@link GeometriesModule}.\n *\n * - `'filled'` — Colored fill with thin border (default)\n * - `'outline'` — Transparent fill with thick colored border\n * - `'inverted'` — Colors the area **outside** the polygon (donut geometry)\n *\n * Set via {@link GeometriesModuleConfig.theme} at config level, or per-feature in\n * `properties.theme`.\n *\n * @group Geometries\n */\nexport type GeometryTheme = 'filled' | 'outline' | 'inverted';\n\n/**\n * All valid {@link GeometryTheme} values.\n *\n * @group Geometries\n */\nexport const geometryThemes = ['filled', 'outline', 'inverted'] as const;\n","import { AbstractMapModule, EventsModule, HILLSHADE_SOURCE_ID, StyleSourceWithLayers } from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport type { HillshadeModuleConfig } from '.';\n\n/**\n * IDs of sources and layers for hillshade module.\n */\ntype HillshadeSourcesWithLayers = {\n hillshade: StyleSourceWithLayers;\n};\n\n/**\n * Map module for displaying terrain shading (hillshade).\n *\n * The HillshadeModule adds realistic terrain depth perception to the map by rendering\n * shadow and highlight effects based on elevation data. This enhances the 3D appearance\n * of mountainous and hilly terrain.\n *\n * @remarks\n * **Features:**\n * - Realistic terrain shading\n * - Uses vector tile elevation data\n * - Lightweight performance impact\n * - Seamlessly integrates with other map layers\n * - Toggle visibility on/off\n *\n * **Common Use Cases:**\n * - Outdoor recreation maps (hiking, skiing)\n * - Geographic/topographic applications\n * - Environmental visualization\n * - Landscape planning tools\n *\n * @example\n * ```typescript\n * // Create the module with hillshade visible\n * const hillshade = await HillshadeModule.getInstance(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * hillshade.setVisible(false);\n *\n * // Listen to events\n * hillshade.events.on('click', (feature, lngLat) => {\n * console.log('Clicked hillshade at:', lngLat);\n * });\n * ```\n *\n * @see [Hillshade Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/hillshade)\n *\n * @group Hillshade\n */\nexport class HillshadeModule extends AbstractMapModule<HillshadeSourcesWithLayers, HillshadeModuleConfig> {\n /**\n * Retrieves a HillshadeModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization and visibility.\n *\n * @returns A promise that resolves to the initialized HillshadeModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `ensureAddedToStyle`: Auto-add hillshade to style if missing\n *\n * **Style Requirement:**\n * - Hillshade must be included in the map style or added via `ensureAddedToStyle`\n * - Some styles may not support hillshade (e.g., satellite)\n *\n * @throws Error if hillshade source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map);\n * ```\n *\n * @example\n * Auto-add to style if missing:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map, {\n * visible: true\n * });\n * ```\n *\n * @example\n * Start hidden:\n * ```typescript\n * const hillshadeModule = await HillshadeModule.get(map, {\n * visible: false\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: HillshadeModuleConfig): Promise<HillshadeModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, HILLSHADE_SOURCE_ID, 'hillshade');\n return new HillshadeModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: HillshadeModuleConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const hillshadeSource = this.mapLibreMap.getSource(HILLSHADE_SOURCE_ID);\n if (!hillshadeSource) {\n throw notInTheStyle(`init ${HillshadeModule.name} with source ID ${HILLSHADE_SOURCE_ID}`);\n }\n return { hillshade: new StyleSourceWithLayers(this.mapLibreMap, hillshadeSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: HillshadeModuleConfig | undefined) {\n this.setVisible(config?.visible ?? false);\n return config;\n }\n\n /**\n * Sets the visibility of the hillshade layer.\n *\n * @param visible - `true` to show hillshade, `false` to hide it.\n *\n * @remarks\n * Changes are applied immediately if the map is ready.\n *\n * @example\n * ```typescript\n * hillshade.setVisible(true); // Show terrain shading\n * hillshade.setVisible(false); // Hide terrain shading\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = {\n ...this.config,\n visible,\n };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.hillshade.setLayersVisible(visible);\n }\n }\n\n /**\n * Checks if the hillshade layer is currently visible.\n *\n * @returns `true` if visible, `false` if hidden.\n *\n * @example\n * ```typescript\n * if (hillshade.isVisible()) {\n * console.log('Terrain shading is active');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.hillshade.isAnyLayerVisible();\n }\n\n /**\n * Gets the events interface for handling user interactions with hillshade.\n *\n * @returns An EventsModule instance for registering event handlers.\n *\n * @remarks\n * **Supported Events:**\n * - `click`: User clicks on the hillshade layer\n * - `contextmenu`: User right-clicks\n * - `hover`: Mouse enters hillshade area\n * - `long-hover`: Extended hover\n *\n * @example\n * ```typescript\n * hillshade.events.on('click', (feature, lngLat) => {\n * console.log('Clicked terrain at:', lngLat);\n * });\n * ```\n */\n get events() {\n return new EventsModule(this.tomtomMap._eventsProxy, this.sourcesWithLayers.hillshade, this.config?.events);\n }\n}\n","import type { GlobalConfig } from '@tomtom-org/maps-sdk/core';\nimport type { MapOptions, StyleSpecification } from 'maplibre-gl';\nimport type { MapEventsConfig } from './mapEventsConfig';\n\n/**\n * Array of all available standard style identifiers.\n *\n * This constant provides the complete list of TomTom-hosted standard map styles\n * that can be used when initializing a map. It serves as the source of truth for\n * valid style IDs and is used to derive the {@link StandardStyleID} type.\n *\n * @remarks\n * Use this array when you need to:\n * - Iterate over all available styles (e.g., building a style picker UI)\n * - Validate if a style ID is a standard style\n * - Display available options to users\n *\n * The array is defined as `const` with `as const` assertion to ensure type safety\n * and prevent modifications at runtime.\n *\n * @example\n * ```typescript\n * import { standardStyleIDs } from '@tomtom-org/maps-sdk/map';\n *\n * // Build a dropdown menu with all standard styles\n * const styleSelector = document.getElementById('style-selector');\n * standardStyleIDs.forEach((styleId) => {\n * const option = new Option(styleId);\n * styleSelector.add(option);\n * });\n *\n * // Check if a style ID is valid\n * const isValidStyle = (id: string): boolean => {\n * return standardStyleIDs.includes(id as any);\n * };\n * ```\n *\n * @see {@link StandardStyleID} - The type derived from this array\n *\n * @group Map Style\n */\nexport const standardStyleIDs = [\n 'standardLight',\n 'standardDark',\n // TODO: driving styles not supported in Orbis for now\n 'drivingLight',\n 'drivingDark',\n 'monoLight',\n 'monoDark',\n 'satellite',\n] as const;\n\n/**\n * Identifier for a TomTom-hosted standard map style.\n *\n * Standard styles are officially maintained by TomTom and provide consistent,\n * professionally designed map appearances.\n *\n * @remarks\n * Available styles:\n * - `standardLight`: Default light theme with full detail\n * - `standardDark`: Dark theme for low-light environments\n * - `drivingLight`: Optimized for in-car navigation (light)\n * - `drivingDark`: Optimized for in-car navigation (dark)\n * - `monoLight`: Minimalist monochrome light theme\n * - `monoDark`: Minimalist monochrome dark theme\n * - `satellite`: Satellite imagery basemap\n *\n * @example\n * ```typescript\n * const styleId: StandardStyleID = 'standardLight';\n * ```\n *\n * @group Map Style\n */\nexport type StandardStyleID = (typeof standardStyleIDs)[number];\n\n/**\n * Configuration for a TomTom standard map style.\n *\n * Provides options to customize which modules are included and which style version to use.\n *\n * @example\n * ```typescript\n * // Standard style with all default modules\n * const style: StandardStyle = {\n * id: 'standardLight'\n * };\n *\n * // Exclude traffic modules\n * const styleNoTraffic: StandardStyle = {\n * id: 'standardLight',\n * include: ['hillshade'] // Only include hillshade, exclude traffic\n * };\n *\n * // Use specific style version\n * const versionedStyle: StandardStyle = {\n * id: 'standardDark',\n * version: '1.0.0'\n * };\n * ```\n *\n * @group Map Style\n */\nexport type StandardStyle = {\n /**\n * Standard style identifier.\n *\n * Determines the visual appearance of the map.\n */\n id?: StandardStyleID;\n /**\n * Modules to include when loading the style.\n * * Use this to selectively enable only needed modules for better performance.\n *\n * If not specified, all available modules are included by default.\n * If an empty array is provided, no optional modules will be included.\n *\n * @default All available modules\n *\n * @remarks\n * Available modules:\n * - `trafficIncidents`: Real-time traffic incidents (accidents, closures)\n * - `trafficFlow`: Real-time traffic flow visualization\n * - `hillshade`: Terrain elevation shading\n *\n * @example\n * ```typescript\n * // Include only traffic modules\n * include: ['trafficIncidents', 'trafficFlow']\n *\n * // Include only hillshade\n * include: ['hillshade']\n *\n * // Include no optional modules\n * include: []\n * ```\n */\n include?: StyleModule[];\n /**\n * Style version to load.\n *\n * Allows pinning to a specific style version for consistency.\n * If not specified, uses the latest SDK-supported version.\n *\n * @default Latest SDK-supported version\n *\n * @example\n * ```typescript\n * version: '1.0.0'\n * ```\n */\n version?: string;\n};\n\n/**\n * Configuration for a custom map style.\n *\n * Allows using your own map style either via URL or direct JSON specification.\n *\n * @remarks\n * Use custom styles for:\n * - Branded map appearances\n * - Specialized use cases (indoor maps, thematic maps)\n * - Integration with custom tile servers\n *\n * @example\n * ```typescript\n * // Load from URL\n * const urlStyle: CustomStyle = {\n * url: 'https://example.com/my-custom-style.json'\n * };\n *\n * // Direct JSON specification\n * const jsonStyle: CustomStyle = {\n * json: {\n * version: 8,\n * sources: { ... },\n * layers: [ ... ]\n * }\n * };\n * ```\n *\n * @group Map Style\n */\nexport type CustomStyle = {\n /**\n * URL to a MapLibre/Mapbox style JSON.\n *\n * The URL should not include the API key - it will be automatically added.\n * Mutually exclusive with the `json` property.\n *\n * @example\n * ```typescript\n * url: 'https://api.tomtom.com/style/1/style/my-custom-style'\n * ```\n */\n url?: string;\n /**\n * Direct style specification as JSON.\n *\n * Provide the complete MapLibre Style Specification object.\n * Mutually exclusive with the `url` property.\n *\n * @see [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/)\n *\n * @example\n * ```typescript\n * json: {\n * version: 8,\n * sources: {\n * 'my-source': { type: 'vector', url: '...' }\n * },\n * layers: [\n * { id: 'background', type: 'background', paint: { 'background-color': '#f0f0f0' } }\n * ]\n * }\n * ```\n */\n json?: StyleSpecification;\n};\n\n/**\n * Array of all available style modules.\n *\n * @group Map Style\n */\nexport const styleModules = ['trafficIncidents', 'trafficFlow', 'hillshade'] as const;\n\n/**\n * Optional map modules that can be included with a style.\n *\n * @remarks\n * - `trafficIncidents`: Shows real-time traffic incidents on the map\n * - `trafficFlow`: Shows real-time traffic flow with color-coded speeds\n * - `hillshade`: Adds terrain elevation shading for topographic context\n *\n * @group Map Style\n */\nexport type StyleModule = (typeof styleModules)[number];\n\n/**\n * Map style specification for initialization.\n *\n * Defines which map style to load and how it should be configured.\n * Supports standard TomTom styles or custom styles.\n *\n * @remarks\n * Three input formats:\n * 1. **Simple ID**: Just pass a standard style ID string\n * 2. **Standard style object**: Use a TomTom style with custom configuration\n * 3. **Custom style object**: Load your own style from URL or JSON\n *\n * @example\n * ```typescript\n * // 1. Simple standard style\n * style: 'standardLight'\n *\n * // 2. Standard style with configuration\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * }\n *\n * // 3. Custom style from URL\n * style: {\n * type: 'custom',\n * url: 'https://example.com/style.json'\n * }\n *\n * // 4. Custom style from JSON\n * style: {\n * type: 'custom',\n * json: { version: 8, sources: {...}, layers: [...] }\n * }\n * ```\n *\n * @group Map Style\n */\nexport type StyleInput = StandardStyleID | (StandardStyle & { type: 'standard' }) | (CustomStyle & { type: 'custom' });\n\n/**\n * MapLibre-specific options for advanced map configuration.\n *\n * Extends MapLibre GL JS MapOptions, excluding style and attribution control\n * which are handled by the TomTom SDK.\n *\n * @remarks\n * Includes options for:\n * - Initial viewport (center, zoom, bearing, pitch)\n * - Interaction controls (zoom, rotation, drag)\n * - Rendering options (antialiasing, terrain)\n * - Localization and accessibility\n *\n * @see [MapLibre MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/)\n *\n * @group Map\n */\nexport type MapLibreOptions = Omit<MapOptions, 'style' | 'attributionControl'>;\n\n/**\n * Parameters for initializing a TomTom map instance.\n *\n * Combines global SDK configuration with map-specific settings like style, events, and MapLibre options.\n * All GlobalConfig properties (key, baseURL, etc.) are optional and will be merged from global configuration.\n * Only mapLibre.container is strictly required.\n *\n * @example\n * ```typescript\n * const mapParams: TomTomMapParams = {\n * key: 'your-api-key',\n * style: 'standardLight',\n * events: {\n * onClick: (event) => console.log('Map clicked', event)\n * },\n * mapLibre: {\n * container: 'map-container',\n * center: [4.9041, 52.3676],\n * zoom: 12\n * }\n * };\n * ```\n *\n * @group Map\n */\nexport type TomTomMapParams = Partial<GlobalConfig> & {\n /**\n * Map style to load.\n *\n * If not specified, defaults to 'standardLight'.\n *\n * @default 'standardLight'\n *\n * @example\n * ```typescript\n * // Use dark theme\n * style: 'standardDark'\n *\n * // Custom configuration\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow']\n * }\n * ```\n */\n style?: StyleInput;\n\n /**\n * Event handler configuration for map interactions.\n *\n * Define callbacks for various map events like clicks, hovers, and movements.\n *\n * @example\n * ```typescript\n * events: {\n * onClick: (event) => {\n * console.log('Clicked at', event.lngLat);\n * },\n * onMoveEnd: () => {\n * console.log('Map moved to', map.getCenter());\n * }\n * }\n * ```\n */\n events?: MapEventsConfig;\n\n /**\n * MapLibre-specific options for map configuration.\n *\n * Includes options for viewport settings, interaction controls, rendering options, and more.\n *\n * @example\n * ```typescript\n * mapLibre: {\n * container: 'map',\n * center: [4.9041, 52.3676],\n * zoom: 12,\n * pitch: 45,\n * bearing: -17.6,\n * antialias: true,\n * maxZoom: 18,\n * minZoom: 8\n * }\n * ```\n */\n mapLibre: MapLibreOptions;\n};\n\n/**\n * Complete TomTom map parameters after merging with global configuration.\n * This type represents the fully resolved configuration used internally by the SDK.\n *\n * @ignore\n */\nexport type InternalTomTomMapParams = GlobalConfig & TomTomMapParams;\n","import type { ExpressionSpecification } from 'maplibre-gl';\n\n/**\n * Main route line foreground color.\n * @ignore\n */\nexport const ROUTE_LINE_FOREGROUND_COLOR = '#36A8F0';\n\n/**\n * Main route outline color.\n * @ignore\n */\nexport const ROUTE_LINE_OUTLINE_COLOR = '#105287';\n\n/**\n * Deselected route line foreground color.\n * @ignore\n */\nexport const DESELECTED_FOREGROUND_COLOR = '#ABAFB3';\n\n/**\n * Deselected route line outline color.\n * @ignore\n */\nexport const DESELECTED_OUTLINE_COLOR = '#3C4956';\n\n/**\n * @ignore\n */\nexport const DESELECTED_SECONDARY_COLOR = '#727C85';\n\n/**\n * Main route line width based on zoom level.\n * @ignore\n */\nexport const ROUTE_LINE_FOREGROUND_WIDTH: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 1,\n 3,\n 5,\n 4,\n 10,\n 7,\n 18,\n 10,\n];\n\n/**\n * Used for showing/hiding layer depending on layer being part of selected route or not.\n *\n * @remarks\n * Add this to layers that depend on whether they are part of the selected route or not.\n *\n * @example:\n * filter: SELECTED_ROUTE_FILTER\n\n * @group Routing\n */\nexport const SELECTED_ROUTE_FILTER: ExpressionSpecification = ['==', ['get', 'routeState'], 'selected'];\n\n/**\n * Used for showing/hiding layer depending on layer being part of deselected route or not.\n *\n * @remarks\n * Add this to layers that depend on whether they are part of the deselected route or not.\n *\n * @example:\n * filter: DESELECTED_ROUTE_FILTER\n\n * @group Routing\n */\nexport const DESELECTED_ROUTE_FILTER: ExpressionSpecification = ['==', ['get', 'routeState'], 'deselected'];\n\n/**\n * @ignore\n */\nexport const MAJOR_DELAY_COLOR = '#AD0000';\n/**\n * @ignore\n */\nexport const MODERATE_DELAY_COLOR = '#FB2D09';\n/**\n * @ignore\n */\nexport const MINOR_DELAY_LABEL_COLOR = '#f58240';\n/**\n * @ignore\n */\nexport const UNKNOWN_DELAY_COLOR = '#000000';\n","import type {\n DataDrivenPropertyValueSpecification,\n FormattedSpecification,\n SymbolLayerSpecification,\n} from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { pinLayerBaseSpec } from '../../shared/layers/symbolLayers';\nimport { ChargingStopsConfig, ChargingStopTextConfig } from '../types/routeModuleConfig';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\nconst chargingStopTextField = (\n config: ChargingStopTextConfig | undefined,\n): DataDrivenPropertyValueSpecification<FormattedSpecification> => {\n if (config?.visible === false) {\n return '';\n }\n\n return (\n config?.title ?? [\n 'format',\n ['get', 'title'],\n '\\n',\n ['get', 'chargingPower'],\n ' • ',\n ['get', 'chargingDuration'],\n ]\n );\n};\n\n/**\n * @ignore\n * @see toDisplayChargingStops\n */\nexport const chargingStopSymbol = (\n config: ChargingStopsConfig | undefined,\n): LayerSpecTemplate<SymbolLayerSpecification> => {\n return {\n ...pinLayerBaseSpec,\n filter: SELECTED_ROUTE_FILTER,\n minzoom: 4,\n layout: {\n ...pinLayerBaseSpec.layout,\n 'text-field': chargingStopTextField(config?.text),\n },\n paint: {\n ...pinLayerBaseSpec.paint,\n },\n };\n};\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\nconst commonProps = {\n filter: SELECTED_ROUTE_FILTER,\n minzoom: 16,\n};\n\nconst commonLineProps: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonProps,\n type: 'line',\n layout: { 'line-cap': 'round' },\n};\n\n/**\n * @ignore\n */\nexport const instructionOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonLineProps,\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 16, 14, 22, 20],\n 'line-color': 'grey',\n },\n};\n\n/**\n * @ignore\n */\nexport const instructionLine: LayerSpecTemplate<LineLayerSpecification> = {\n ...commonLineProps,\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 16, 12, 22, 17],\n 'line-color': 'white',\n },\n};\n\n/**\n * @ignore\n */\nexport const INSTRUCTION_ARROW_IMAGE_ID = 'instruction-arrow';\n\n/**\n * @ignore\n */\nexport const instructionArrow: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...commonProps,\n type: 'symbol',\n layout: {\n 'icon-allow-overlap': true,\n 'icon-image': INSTRUCTION_ARROW_IMAGE_ID, // Will be updated with instance suffix in config\n 'icon-rotation-alignment': 'map',\n 'icon-rotate': ['get', 'lastPointBearingDegrees'],\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 16, 1, 22, 2],\n },\n};\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { ROUTE_LINE_FOREGROUND_WIDTH, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeFerriesLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n 'line-color': '#6dc4ed',\n },\n};\n\n/**\n * @ignore\n */\nexport const routeFerriesSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 6,\n // zoom where the map POI naturally appears:\n maxzoom: 16.5,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-image': 'poi-ferry_terminal',\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 6, 0.8, 16.5, 1],\n // helps smooth the transition from along-route to map-poi, which also has a label in it:\n 'icon-ignore-placement': true,\n },\n};\n","import type { ExpressionSpecification, LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport {\n DESELECTED_FOREGROUND_COLOR,\n DESELECTED_OUTLINE_COLOR,\n DESELECTED_ROUTE_FILTER,\n ROUTE_LINE_FOREGROUND_COLOR,\n ROUTE_LINE_FOREGROUND_WIDTH,\n ROUTE_LINE_OUTLINE_COLOR,\n SELECTED_ROUTE_FILTER,\n} from './shared';\n\n/**\n * @ignore\n */\nexport const routeLineBaseTemplate: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n 'line-sort-key': ['get', 'index'],\n },\n};\n\nconst outlineLineWidth: ExpressionSpecification = ['interpolate', ['linear'], ['zoom'], 1, 5, 5, 6, 10, 10, 18, 14];\n\n/**\n * @ignore\n */\nexport const routeDeselectedOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: DESELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': DESELECTED_OUTLINE_COLOR,\n 'line-width': outlineLineWidth,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeDeselectedLine: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: DESELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': DESELECTED_FOREGROUND_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeOutline: LayerSpecTemplate<LineLayerSpecification> = {\n ...routeLineBaseTemplate,\n filter: SELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': ROUTE_LINE_OUTLINE_COLOR,\n 'line-width': outlineLineWidth,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeMainLine = (props?: { color?: string }): LayerSpecTemplate<LineLayerSpecification> => ({\n ...routeLineBaseTemplate,\n filter: SELECTED_ROUTE_FILTER,\n paint: {\n 'line-color': props?.color ?? ROUTE_LINE_FOREGROUND_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n});\n\n/**\n * @ignore\n */\nexport const routeLineArrows: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n layout: {\n 'symbol-placement': 'line',\n 'icon-image': 'roads-arrow-white',\n // The current arrow icon seems to point backwards otherwise. Check with caution!\n 'icon-rotate': 180,\n },\n};\n\n/**\n * @ignore\n */\nexport const SELECTED_SUMMARY_POPUP_IMAGE_ID = 'selected-route-summary-popup';\n/**\n * @ignore\n */\nexport const DESELECTED_SUMMARY_POPUP_IMAGE_ID = 'deselected-route-summary-popup';\n","import type { LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeTollRoadsOutline: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n },\n paint: {\n 'line-width': ['interpolate', ['linear'], ['zoom'], 1, 9, 5, 11, 10, 15, 18, 20],\n 'line-color': '#BEBFFA',\n },\n};\n\n/**\n * @ignore\n */\nexport const routeTollRoadsSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 4,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-image': 'poi-toll_plaza',\n 'icon-size': ['interpolate', ['linear'], ['zoom'], 4, 0.8, 16.5, 1],\n },\n};\n","import type { ExpressionSpecification, LineLayerSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT } from '../../shared/layers/commonLayerProps';\nimport {\n MAJOR_DELAY_COLOR,\n MINOR_DELAY_LABEL_COLOR,\n MODERATE_DELAY_COLOR,\n SELECTED_ROUTE_FILTER,\n UNKNOWN_DELAY_COLOR,\n} from './shared';\n\nconst EXTRA_FOREGROUND_LINE_WIDTH: ExpressionSpecification = [\n 'interpolate',\n ['linear'],\n ['zoom'],\n 1,\n 2,\n 5,\n 3,\n 10,\n 4,\n 18,\n 6,\n];\n\n/**\n * @ignore\n */\nexport const routeIncidentsBGLine: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n layout: { 'line-cap': 'round' },\n paint: {\n 'line-width': EXTRA_FOREGROUND_LINE_WIDTH,\n 'line-color': [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'minor',\n '#FFC105',\n 'moderate',\n MODERATE_DELAY_COLOR,\n 'major',\n MAJOR_DELAY_COLOR,\n // other\n '#C7D2D8',\n ],\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsDashedLine: LayerSpecTemplate<LineLayerSpecification> = {\n type: 'line',\n filter: ['in', ['get', 'magnitudeOfDelay'], ['literal', ['unknown', 'indefinite']]],\n layout: { 'line-join': 'round' },\n paint: {\n 'line-width': EXTRA_FOREGROUND_LINE_WIDTH,\n 'line-color': [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'unknown',\n 'rgba(190, 39, 27, 1)',\n // other (undefined):\n 'rgba(137, 150, 168, 1)',\n ],\n 'line-dasharray': [1.5, 1],\n },\n};\n\n/**\n * @ignore\n */\nexport const magnitudeOfDelayTextColor: ExpressionSpecification = [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'minor',\n MINOR_DELAY_LABEL_COLOR,\n 'moderate',\n MODERATE_DELAY_COLOR,\n 'major',\n MAJOR_DELAY_COLOR,\n 'indefinite',\n '#666666',\n // other\n UNKNOWN_DELAY_COLOR,\n];\n\nconst routeIncidentsSymbolBase: LayerSpecTemplate<SymbolLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'symbol',\n minzoom: 6,\n layout: {\n 'symbol-placement': 'point',\n 'symbol-avoid-edges': true,\n 'icon-ignore-placement': true,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsJamSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...routeIncidentsSymbolBase,\n filter: ['all', ['has', 'jamIconID'], routeIncidentsSymbolBase.filter as ExpressionSpecification],\n layout: {\n ...routeIncidentsSymbolBase.layout,\n 'icon-image': ['get', 'jamIconID'],\n 'icon-anchor': 'bottom-left',\n 'text-anchor': 'bottom-left',\n // Jam symbols have delay labels in them:\n 'text-field': ['get', 'title'],\n 'text-font': [MAP_BOLD_FONT],\n 'text-offset': [3.9, -1.4],\n 'text-size': 13,\n },\n paint: {\n ...routeIncidentsSymbolBase.paint,\n 'text-color': magnitudeOfDelayTextColor,\n 'text-halo-color': '#FFFFFF',\n 'text-halo-width': 1,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeIncidentsCauseSymbol: LayerSpecTemplate<SymbolLayerSpecification> = {\n ...routeIncidentsSymbolBase,\n filter: ['all', ['has', 'causeIconID'], routeIncidentsSymbolBase.filter as ExpressionSpecification],\n layout: {\n ...routeIncidentsSymbolBase.layout,\n 'icon-image': ['get', 'causeIconID'],\n 'icon-anchor': 'bottom-right',\n // Cause symbols have no label in them.\n },\n paint: { ...routeIncidentsSymbolBase.paint },\n};\n","import type { LineLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { ROUTE_LINE_FOREGROUND_WIDTH, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const routeTunnelsLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n 'line-color': '#000000',\n 'line-opacity': 0.3,\n },\n};\n","import type { LineLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport {\n ROUTE_LINE_FOREGROUND_COLOR,\n ROUTE_LINE_FOREGROUND_WIDTH,\n ROUTE_LINE_OUTLINE_COLOR,\n SELECTED_ROUTE_FILTER,\n} from './shared';\n\n/**\n * @ignore\n */\nexport const routeVehicleRestrictedBackgroundLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n },\n paint: {\n 'line-color': ROUTE_LINE_OUTLINE_COLOR,\n 'line-width': ROUTE_LINE_FOREGROUND_WIDTH,\n },\n};\n\n/**\n * @ignore\n */\nexport const routeVehicleRestrictedDottedLine: LayerSpecTemplate<LineLayerSpecification> = {\n filter: SELECTED_ROUTE_FILTER,\n type: 'line',\n layout: {\n 'line-join': 'round',\n 'line-cap': 'round',\n },\n paint: {\n 'line-color': ROUTE_LINE_FOREGROUND_COLOR,\n 'line-width': ['interpolate', ['linear'], ['zoom'], 1, 2, 5, 3, 10, 5, 18, 7],\n 'line-dasharray': [0, 1.5],\n },\n};\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT, MAP_MEDIUM_FONT } from '../../shared/layers/commonLayerProps';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport { DESELECTED_SUMMARY_POPUP_IMAGE_ID, SELECTED_SUMMARY_POPUP_IMAGE_ID } from './routeMainLineLayers';\nimport { magnitudeOfDelayTextColor } from './routeTrafficSectionLayers';\nimport { DESELECTED_SECONDARY_COLOR, SELECTED_ROUTE_FILTER } from './shared';\n\n/**\n * @ignore\n */\nexport const TRAFFIC_CLEAR_IMAGE_ID = 'traffic-clear';\n/**\n * @ignore\n */\nexport const TRAFFIC_MAJOR_IMAGE_ID = 'traffic-major';\n/**\n * @ignore\n */\nexport const TRAFFIC_MODERATE_IMAGE_ID = 'traffic-moderate';\n/**\n * @ignore\n */\nexport const TRAFFIC_MINOR_IMAGE_ID = 'traffic-minor';\n\nconst hasFormattedTraffic: ExpressionSpecification = ['has', 'formattedTraffic'];\n\n/**\n * Builds the summary bubble symbol layer specification with instance-specific image IDs.\n * @param instanceIndex - Optional instance index for supporting multiple RoutingModule instances\n * @ignore\n */\nexport const buildSummaryBubbleSymbolPoint = (instanceIndex?: number): LayerSpecTemplate<SymbolLayerSpecification> => {\n const selectedImageID =\n instanceIndex !== undefined\n ? suffixNumber(SELECTED_SUMMARY_POPUP_IMAGE_ID, instanceIndex)\n : SELECTED_SUMMARY_POPUP_IMAGE_ID;\n const deselectedImageID =\n instanceIndex !== undefined\n ? suffixNumber(DESELECTED_SUMMARY_POPUP_IMAGE_ID, instanceIndex)\n : DESELECTED_SUMMARY_POPUP_IMAGE_ID;\n const trafficClearID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_CLEAR_IMAGE_ID, instanceIndex) : TRAFFIC_CLEAR_IMAGE_ID;\n const trafficMajorID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_MAJOR_IMAGE_ID, instanceIndex) : TRAFFIC_MAJOR_IMAGE_ID;\n const trafficModerateID =\n instanceIndex !== undefined\n ? suffixNumber(TRAFFIC_MODERATE_IMAGE_ID, instanceIndex)\n : TRAFFIC_MODERATE_IMAGE_ID;\n const trafficMinorID =\n instanceIndex !== undefined ? suffixNumber(TRAFFIC_MINOR_IMAGE_ID, instanceIndex) : TRAFFIC_MINOR_IMAGE_ID;\n\n return {\n type: 'symbol',\n layout: {\n 'icon-image': ['case', SELECTED_ROUTE_FILTER, selectedImageID, deselectedImageID],\n 'symbol-placement': 'point',\n 'icon-rotation-alignment': 'viewport',\n 'text-rotation-alignment': 'viewport',\n 'symbol-sort-key': ['case', SELECTED_ROUTE_FILTER, 0, 1],\n 'icon-text-fit': 'both',\n 'icon-text-fit-padding': [10, 5, 5, 10],\n 'text-font': [MAP_MEDIUM_FONT],\n 'text-size': 13,\n 'icon-padding': 0,\n 'text-justify': 'left',\n 'text-line-height': 1.5,\n 'text-field': [\n 'format',\n ['get', 'formattedDuration'],\n {\n 'text-font': ['literal', [MAP_BOLD_FONT]],\n 'text-color': ['case', SELECTED_ROUTE_FILTER, 'black', DESELECTED_SECONDARY_COLOR],\n },\n ['concat', '\\t\\t', ['get', 'formattedDistance']],\n { 'text-color': DESELECTED_SECONDARY_COLOR },\n ['case', hasFormattedTraffic, '\\n', ''],\n {},\n [\n 'image',\n [\n 'case',\n hasFormattedTraffic,\n [\n 'match',\n ['get', 'magnitudeOfDelay'],\n 'major',\n trafficMajorID,\n 'moderate',\n trafficModerateID,\n 'minor',\n trafficMinorID,\n trafficClearID,\n ],\n '',\n ],\n ],\n {},\n ['case', hasFormattedTraffic, ['concat', ' ', ['get', 'formattedTraffic']], ''],\n {\n 'text-font': ['literal', [MAP_BOLD_FONT]],\n 'text-color': magnitudeOfDelayTextColor,\n },\n ],\n },\n paint: {\n 'icon-translate': [0, -35],\n 'text-translate': [0, -35],\n },\n };\n};\n\n/**\n * Default summary bubble symbol layer (without instance suffix)\n * @ignore\n */\nexport const summaryBubbleSymbolPoint: LayerSpecTemplate<SymbolLayerSpecification> = buildSummaryBubbleSymbolPoint();\n","import type { PlaceDisplayProps } from '../../places';\n\n/**\n * @group Routing\n */\nexport const START_INDEX = 'start';\n/**\n * @group Routing\n */\nexport const MIDDLE_INDEX = 'middle';\n/**\n * @group Routing\n */\nexport const FINISH_INDEX = 'finish';\n/**\n * @group Routing\n */\nexport type WaypointIndexType = typeof START_INDEX | typeof MIDDLE_INDEX | typeof FINISH_INDEX;\n\n/**\n * Display properties for a waypoint marker on the map.\n *\n * Extends location display properties with waypoint-specific information\n * including position in the route and stop numbering.\n *\n * @remarks\n * Waypoints are displayed differently based on their position:\n * - **Start**: Origin marker (often \"A\" or green pin)\n * - **Middle**: Numbered stop markers (1, 2, 3, etc.)\n * - **Finish**: Destination marker (often \"B\" or red pin)\n *\n * @example\n * ```typescript\n * // Start waypoint\n * const start: WaypointDisplayProps = {\n * id: 'waypoint-0',\n * iconID: 'waypoint-start',\n * index: 0,\n * indexType: 'start',\n * title: 'Amsterdam Central Station'\n * };\n *\n * // Intermediate stop\n * const stop: WaypointDisplayProps = {\n * id: 'waypoint-1',\n * iconID: 'waypoint-stop',\n * index: 1,\n * indexType: 'middle',\n * stopDisplayIndex: 1,\n * title: 'Schiphol Airport'\n * };\n *\n * // Destination\n * const finish: WaypointDisplayProps = {\n * id: 'waypoint-2',\n * iconID: 'waypoint-finish',\n * index: 2,\n * indexType: 'finish',\n * title: 'Rotterdam Central Station'\n * };\n * ```\n *\n * @group Routing\n */\nexport type WaypointDisplayProps = PlaceDisplayProps & {\n /**\n * The index of the waypoint in relation to the other waypoints.\n *\n * @remarks\n * Zero-based index of this waypoint in the complete waypoints array,\n * including start, all stops, and finish.\n *\n * @example\n * ```typescript\n * index: 0 // First waypoint (start)\n * index: 1 // Second waypoint (first stop or finish)\n * index: 2 // Third waypoint\n * ```\n */\n index: number;\n\n /**\n * The type associated to the index, describing how the waypoint sits in the list of waypoints.\n *\n * @remarks\n * Determines the waypoint's role and visual representation:\n * - `start`: Origin point\n * - `middle`: Intermediate stop\n * - `finish`: Final destination\n *\n * This affects icon selection and labeling behavior.\n */\n indexType: WaypointIndexType;\n\n /**\n * The stop index to be displayed.\n *\n * @remarks\n * Stops are the non-soft waypoints added between origin and destination,\n * numbered starting from 1. Only present for middle waypoints.\n *\n * **Display Behavior:**\n * - Start waypoint: undefined\n * - First stop: 1\n * - Second stop: 2\n * - Finish waypoint: undefined\n *\n * Used for displaying stop numbers (e.g., \"Stop 1\", \"Stop 2\") in the UI.\n *\n * @example\n * ```typescript\n * stopDisplayIndex: 1 // First intermediate stop\n * stopDisplayIndex: 2 // Second intermediate stop\n * stopDisplayIndex: undefined // Start or finish waypoint\n * ```\n */\n stopDisplayIndex?: number;\n};\n\n/**\n * @ignore\n */\nexport const INDEX_TYPE = 'indexType';\n\n/**\n * @ignore\n */\nexport const STOP_DISPLAY_INDEX = 'stopDisplayIndex';\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\nimport type { LayerSpecTemplate } from '../../shared';\nimport { MAP_BOLD_FONT } from '../../shared/layers/commonLayerProps';\nimport {\n ICON_ID,\n pinIconBaseLayout,\n pinIconBasePaint,\n pinTextBaseLayout,\n pinTextBasePaint,\n} from '../../shared/layers/symbolLayers';\nimport { INDEX_TYPE, MIDDLE_INDEX, STOP_DISPLAY_INDEX } from '../types/waypointDisplayProps';\n\n/**\n * Waypoint start image ID.\n * @group Routing\n */\nexport const WAYPOINT_START_IMAGE_ID = 'waypointStart';\n/**\n * Waypoint stop image ID.\n *\n * @remarks\n * Used for intermediate waypoints in a route.\n *\n * @group Routing.\n */\nexport const WAYPOINT_STOP_IMAGE_ID = 'waypointStop';\n/**\n * Soft waypoint image ID.\n *\n * @remarks\n * This is currently unsupported in Orbis maps.\n *\n * @group Routing\n */\nexport const WAYPOINT_SOFT_IMAGE_ID = 'waypointSoft';\n/**\n * Waypoint finish image ID.\n *\n * @group Routing\n */\nexport const WAYPOINT_FINISH_IMAGE_ID = 'waypointFinish';\n\nconst isSoftWaypoint: ExpressionSpecification = [\n 'all',\n ['==', ['get', INDEX_TYPE], MIDDLE_INDEX],\n ['!', ['has', STOP_DISPLAY_INDEX]],\n];\n\nconst pinIndexLabelPaint: SymbolLayerSpecification['paint'] = {\n 'text-color': '#ffffff',\n};\n\nconst pinIndexLabelLayout: SymbolLayerSpecification['layout'] = {\n // optional centered text to indicate stop numbers (1, 2 ...):\n 'text-field': ['get', STOP_DISPLAY_INDEX],\n 'text-font': [MAP_BOLD_FONT],\n 'text-size': ['interpolate', ['linear'], ['zoom'], 13, 14, 18, 16],\n 'text-offset': [0, -1.6],\n // pin vs circle:\n 'icon-anchor': [\n 'case',\n isSoftWaypoint,\n 'center',\n // else\n 'bottom',\n ],\n 'text-allow-overlap': true,\n};\n\n// TODO: reusable display of pins with indexes, not just waypoints\n/**\n * @ignore\n */\nexport const waypointSymbols: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n paint: {\n ...pinIconBasePaint,\n ...pinIndexLabelPaint,\n },\n layout: {\n ...pinIconBaseLayout,\n ...pinIndexLabelLayout,\n 'symbol-sort-key': [\n 'case',\n ['==', ['get', ICON_ID], WAYPOINT_SOFT_IMAGE_ID],\n 0,\n ['abs', ['-', ['get', 'index'], 1000]],\n ],\n },\n};\n\n/**\n * @ignore\n */\nexport const waypointLabels: LayerSpecTemplate<SymbolLayerSpecification> = {\n type: 'symbol',\n paint: {\n ...pinTextBasePaint,\n 'text-color': 'black',\n 'text-halo-width': 1.5,\n 'text-halo-color': '#ffffff',\n },\n layout: {\n ...pinTextBaseLayout,\n 'text-anchor': 'top',\n 'text-offset': [0, 0.4],\n },\n};\n","import { mapStyleLayerIDs } from '../../shared';\nimport type { RouteLayersConfig, RoutingModuleConfig } from '../types/routeModuleConfig';\nimport { chargingStopSymbol } from './chargingStopLayers';\nimport { instructionArrow, instructionLine, instructionOutline } from './guidanceLayers';\nimport { routeFerriesLine, routeFerriesSymbol } from './routeFerrySectionLayers';\nimport {\n routeDeselectedLine,\n routeDeselectedOutline,\n routeLineArrows,\n routeMainLine,\n routeOutline,\n} from './routeMainLineLayers';\nimport { routeTollRoadsOutline, routeTollRoadsSymbol } from './routeTollRoadLayers';\nimport {\n routeIncidentsBGLine,\n routeIncidentsCauseSymbol,\n routeIncidentsDashedLine,\n routeIncidentsJamSymbol,\n} from './routeTrafficSectionLayers';\nimport { routeTunnelsLine } from './routeTunnelSectionLayers';\nimport { routeVehicleRestrictedBackgroundLine, routeVehicleRestrictedDottedLine } from './routeVehicleRestrictedLayers';\nimport { buildSummaryBubbleSymbolPoint, summaryBubbleSymbolPoint } from './summaryBubbleLayers';\nimport { waypointLabels, waypointSymbols } from './waypointLayers';\n\n/**\n * Helper function to add layer ID prefix to beforeID references, but only for internal routing layer IDs\n * @ignore\n */\nconst prefixBeforeID = (beforeID: string | undefined, layerIDPrefix: string | undefined): string | undefined => {\n if (!beforeID || !layerIDPrefix) {\n return beforeID;\n }\n // Don't prefix map style layer IDs (they start with capital letters or contain specific prefixes)\n if (beforeID.startsWith('route') || beforeID.startsWith('waypoint')) {\n return `${layerIDPrefix}-${beforeID}`;\n }\n return beforeID;\n};\n\n/**\n * Helper function to process additional layers and prefix their beforeID fields\n * @ignore\n */\nconst prefixBeforeIDs = (\n additional: Record<string, any> | undefined,\n layerIDPrefix: string | undefined,\n): Record<string, any> | undefined => {\n if (!additional || !layerIDPrefix) {\n return additional;\n }\n\n return Object.fromEntries(\n Object.entries(additional).map(([key, layer]) => [\n key,\n layer?.beforeID ? { ...layer, beforeID: prefixBeforeID(layer.beforeID, layerIDPrefix) } : layer,\n ]),\n );\n};\n\n/**\n * Helper function to add instance suffix to image IDs for supporting multiple RoutingModule instances\n * @ignore\n */\nconst suffixImageID = (imageID: string | undefined, instanceIndex: number | undefined): string | undefined => {\n if (!imageID || instanceIndex === undefined) {\n return imageID;\n }\n return `${imageID}-${instanceIndex}`;\n};\n\n/**\n * Generates the routing layers configuration for route visualization on the map.\n * @param config - Optional routing module configuration to customize layer properties.\n * @param layerIDPrefix - Optional prefix to add to layer IDs for supporting multiple instances.\n * @param instanceIndex - Optional instance index for image ID suffixes.\n * @ignore\n */\nexport const buildRoutingLayers = (\n config: RoutingModuleConfig = {},\n layerIDPrefix?: string,\n instanceIndex?: number,\n): Required<RouteLayersConfig> => {\n const configLayers = config.layers;\n const configSectionLayers = configLayers?.sections;\n const mainColor = config.theme?.mainColor;\n\n return {\n mainLines: {\n routeLineArrows: {\n ...routeLineArrows,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configLayers?.mainLines?.routeLineArrows,\n },\n routeLine: {\n ...routeMainLine({ color: mainColor }),\n beforeID: prefixBeforeID('routeIncidentBackgroundLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeLine,\n },\n routeOutline: {\n ...routeOutline,\n beforeID: prefixBeforeID('routeLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeOutline,\n },\n routeDeselectedLine: {\n ...routeDeselectedLine,\n beforeID: prefixBeforeID('routeOutline', layerIDPrefix),\n ...configLayers?.mainLines?.routeDeselectedLine,\n },\n routeDeselectedOutline: {\n ...routeDeselectedOutline,\n beforeID: prefixBeforeID('routeDeselectedLine', layerIDPrefix),\n ...configLayers?.mainLines?.routeDeselectedOutline,\n },\n ...prefixBeforeIDs(configLayers?.mainLines?.additional, layerIDPrefix),\n },\n waypoints: {\n routeWaypointSymbol: {\n ...waypointSymbols,\n beforeID: prefixBeforeID('routeSummaryBubbleSymbol', layerIDPrefix),\n ...configLayers?.waypoints?.routeWaypointSymbol,\n },\n routeWaypointLabel: {\n ...waypointLabels,\n beforeID: prefixBeforeID('routeWaypointSymbol', layerIDPrefix),\n ...configLayers?.waypoints?.routeWaypointLabel,\n },\n ...prefixBeforeIDs(configLayers?.waypoints?.additional, layerIDPrefix),\n },\n chargingStops: {\n routeChargingStopSymbol: {\n ...chargingStopSymbol(config.chargingStops),\n beforeID: prefixBeforeID('routeWaypointSymbol', layerIDPrefix),\n ...configLayers?.chargingStops?.routeChargingStopSymbol,\n },\n ...prefixBeforeIDs(configLayers?.chargingStops?.additional, layerIDPrefix),\n },\n sections: {\n incident: {\n routeIncidentJamSymbol: {\n ...routeIncidentsJamSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentJamSymbol,\n },\n routeIncidentCauseSymbol: {\n ...routeIncidentsCauseSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentCauseSymbol,\n },\n routeIncidentBackgroundLine: {\n ...routeIncidentsBGLine,\n beforeID: prefixBeforeID('routeIncidentDashedLine', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentBackgroundLine,\n },\n routeIncidentDashedLine: {\n ...routeIncidentsDashedLine,\n beforeID: prefixBeforeID('routeTunnelLine', layerIDPrefix),\n ...configSectionLayers?.incident?.routeIncidentDashedLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.incident?.additional, layerIDPrefix),\n },\n ferry: {\n routeFerryLine: {\n ...routeFerriesLine,\n beforeID: prefixBeforeID('routeLineArrows', layerIDPrefix),\n ...configSectionLayers?.ferry?.routeFerryLine,\n },\n routeFerrySymbol: {\n ...routeFerriesSymbol,\n beforeID: prefixBeforeID('routeIncidentJamSymbol', layerIDPrefix),\n ...configSectionLayers?.ferry?.routeFerrySymbol,\n },\n ...prefixBeforeIDs(configSectionLayers?.ferry?.additional, layerIDPrefix),\n },\n tollRoad: {\n routeTollRoadOutline: {\n ...routeTollRoadsOutline,\n beforeID: prefixBeforeID('routeDeselectedOutline', layerIDPrefix),\n ...configSectionLayers?.tollRoad?.routeTollRoadOutline,\n },\n routeTollRoadSymbol: {\n ...routeTollRoadsSymbol,\n beforeID: prefixBeforeID('routeChargingStopSymbol', layerIDPrefix),\n ...configSectionLayers?.tollRoad?.routeTollRoadSymbol,\n },\n ...prefixBeforeIDs(configSectionLayers?.tollRoad?.additional, layerIDPrefix),\n },\n tunnel: {\n routeTunnelLine: {\n ...routeTunnelsLine,\n beforeID: prefixBeforeID('routeLineArrows', layerIDPrefix),\n ...configSectionLayers?.tunnel?.routeTunnelLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.tunnel?.additional, layerIDPrefix),\n },\n vehicleRestricted: {\n routeVehicleRestrictedBackgroundLine: {\n ...routeVehicleRestrictedBackgroundLine,\n beforeID: prefixBeforeID('routeVehicleRestrictedForegroundLine', layerIDPrefix),\n ...configSectionLayers?.vehicleRestricted?.routeVehicleRestrictedBackgroundLine,\n },\n routeVehicleRestrictedForegroundLine: {\n ...routeVehicleRestrictedDottedLine,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configSectionLayers?.vehicleRestricted?.routeVehicleRestrictedForegroundLine,\n },\n ...prefixBeforeIDs(configSectionLayers?.vehicleRestricted?.additional, layerIDPrefix),\n },\n },\n instructionLines: {\n routeInstructionLine: {\n ...instructionLine,\n beforeID: mapStyleLayerIDs.lowestLabel,\n ...configLayers?.instructionLines?.routeInstructionLine,\n },\n routeInstructionOutline: {\n ...instructionOutline,\n beforeID: prefixBeforeID('routeInstructionLine', layerIDPrefix),\n ...configLayers?.instructionLines?.routeInstructionOutline,\n },\n ...prefixBeforeIDs(configLayers?.instructionLines?.additional, layerIDPrefix),\n },\n instructionArrows: {\n routeInstructionArrowSymbol: {\n ...instructionArrow,\n beforeID: prefixBeforeID('routeInstructionLine', layerIDPrefix),\n ...(instanceIndex !== undefined && {\n layout: {\n ...instructionArrow.layout,\n 'icon-image': suffixImageID(instructionArrow.layout?.['icon-image'] as string, instanceIndex),\n },\n }),\n ...configLayers?.instructionArrows?.routeInstructionArrowSymbol,\n },\n ...prefixBeforeIDs(configLayers?.instructionArrows?.additional, layerIDPrefix),\n },\n summaryBubbles: {\n routeSummaryBubbleSymbol: {\n ...(instanceIndex !== undefined\n ? buildSummaryBubbleSymbolPoint(instanceIndex)\n : summaryBubbleSymbolPoint),\n ...configLayers?.summaryBubbles?.routeSummaryBubbleSymbol,\n },\n ...prefixBeforeIDs(configLayers?.summaryBubbles?.additional, layerIDPrefix),\n },\n };\n};\n\n/**\n * Default routing layers configuration. Calls routingLayers with no parameters.\n *\n * @remarks\n * This configuration defines the complete visual styling for all route-related map layers,\n * including main route lines, waypoints, special road sections (ferries, tunnels, toll roads, etc.),\n * turn-by-turn guidance instructions, and route summary information.\n *\n * **Usage:**\n * - Automatically applied when initializing {@link RoutingModule} without custom layer configuration\n * - Can be used as a reference or starting point for creating custom layer configurations\n * - Individual properties can be selectively overridden while keeping defaults for others\n *\n * @see {@link buildRoutingLayers} for details.\n *\n * @see {@link RouteLayersConfig} for the configuration type definition\n * @see {@link RoutingModule.get} for initialization options\n * @see {@link RoutingModule.applyConfig} for runtime configuration updates\n *\n * @group Routing\n */\nexport const defaultRoutingLayers: Required<RouteLayersConfig> = buildRoutingLayers();\n","import type { StyleImageMetadata } from 'maplibre-gl';\nimport { SVGIconStyleOptions } from '../../shared';\nimport { isDOMImageSupported, svgToImg } from '../../shared/imageUtils';\nimport { parseSvg, pinSvg } from '../../shared/resources';\nimport circleSvgRaw from './circle.svg?raw';\nimport finishSvgRaw from './finish.svg?raw';\nimport instructionArrowSvgRaw from './instruction-line-arrow.svg?raw';\nimport startSvgRaw from './start.svg?raw';\nimport summaryMapBubbleSvgRaw from './summary-map-bubble.svg?raw';\nimport trafficSvgRaw from './traffic.svg?raw';\n\nlet instructionArrowIconImg: HTMLImageElement;\n\n// defensive check for SSR\nif (isDOMImageSupported()) {\n instructionArrowIconImg = svgToImg(parseSvg(instructionArrowSvgRaw));\n}\n\n/**\n * @ignore\n */\nexport { instructionArrowIconImg };\n\n/**\n * @ignore\n */\nexport const summaryMapBubbleImg = (color: string): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg: SVGElement = parseSvg(summaryMapBubbleSvgRaw);\n svg.querySelector('#bubble')?.setAttribute('fill', color);\n svg.querySelector('#pin')?.setAttribute('fill', color);\n return svgToImg(svg);\n};\n\n/**\n * Options to effectively stretch the summary bubble image to fit its text.\n * * They are tightly coupled with the SVG original dimensions.\n * @ignore\n */\nexport const summaryBubbleImageOptions: Partial<StyleImageMetadata> = {\n pixelRatio: 2,\n stretchX: [\n [20, 45],\n [100, 130],\n ],\n stretchY: [[20, 35]],\n content: [10, 10, 130, 45],\n};\n\n/**\n * @ignore\n * @param color\n */\nexport const trafficImg = (color: string): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg: SVGElement = parseSvg(trafficSvgRaw);\n const main = svg.querySelector('#main') as Element;\n main.setAttribute('transform', 'scale(2)');\n main.setAttribute('fill', color);\n return svgToImg(svg);\n};\n\n/**\n * @ignore\n */\nexport const waypointIcon = (\n foregroundSvg: SVGElement | undefined,\n svgOptions: SVGIconStyleOptions | undefined,\n): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n const svg = pinSvg(svgOptions);\n if (foregroundSvg) {\n svg.appendChild(foregroundSvg);\n }\n return svgToImg(svg);\n};\n\n/**\n * @ignore\n */\nexport const waypointStartIcon = (svgOptions: SVGIconStyleOptions | undefined): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return waypointIcon(parseSvg(startSvgRaw), svgOptions);\n};\n\n/**\n * @ignore\n */\nexport const waypointFinishIcon = (svgOptions: SVGIconStyleOptions | undefined): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return waypointIcon(parseSvg(finishSvgRaw), svgOptions);\n};\n\n/**\n * @ignore\n */\nexport const softWaypointIcon = (): HTMLImageElement => {\n // defensive check for SSR and node-test environments:\n if (!isDOMImageSupported()) {\n return undefined as never as HTMLImageElement;\n }\n return svgToImg(parseSvg(circleSvgRaw));\n};\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"50\\\" height=\\\"50\\\">\\n <path d=\\\"M1.562 40.569L25 1.562l23.438 39.007L25 27.566 1.562 40.569z\\\" fill=\\\"#fff\\\"/>\\n <path d=\\\"M25.369.107c.782.429.506.167.917.682l23.438 39.007c.79 1.316-.671 2.829-2.014 2.085L25 29.281l-22.71 12.6c-1.343.744-2.804-.769-2.014-2.085L23.714.789c.461-.694.9-.682 1.656-.682zM25 1.562L1.562 40.569 25 27.566l23.438 13.003L25 1.562z\\\"\\n fill=\\\"gray\\\"/>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"160\\\" height=\\\"65\\\">\\n <g filter=\\\"url(#A)\\\">\\n <rect id=\\\"pin\\\" x=\\\"81.899\\\" y=\\\"47\\\" width=\\\"10\\\" height=\\\"10\\\" rx=\\\"2\\\" transform=\\\"rotate(45 81.899 47)\\\"/>\\n <rect id=\\\"bubble\\\" x=\\\"10\\\" y=\\\"10\\\" width=\\\"140\\\" height=\\\"45\\\" rx=\\\"12\\\"/>\\n </g>\\n <defs>\\n <filter id=\\\"A\\\">\\n <feDropShadow dx=\\\"0\\\" dy=\\\"2\\\" stdDeviation=\\\"4\\\" flood-opacity=\\\".3\\\"/>\\n </filter>\\n </defs>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"32\\\" height=\\\"32\\\">\\n <g id=\\\"main\\\">\\n <path fill-rule=\\\"evenodd\\\"\\n d=\\\"M11.389 4H9.025a.86.86 0 0 0-.517.173l-.001-.001a.5.5 0 0 1-.464.059l-.028-.011a.5.5 0 0 1-.129-.83A1.86 1.86 0 0 1 9.025 3h2.364a2 2 0 0 1 1.789 1.106l1.02 2.04c.471.176.802.63.802 1.156v.803c0 .543 0 .815-.071 1.038a1.5 1.5 0 0 1-.914.953c-.219.08-.491.092-1.034.114l-1.481.062v-.166-.803a2.23 2.23 0 0 0-1.055-1.897l-.74-1.479 2.326.033h.955l-.703-1.406A1 1 0 0 0 11.389 4zM12 8.4a1 1 0 1 1 2 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z\\\"/>\\n <path d=\\\"M11.833 11.115c0-.109.089-.198.198-.198h1.979c.109 0 .198.089.198.198 0 .328-.266.594-.594.594h-1.187c-.328 0-.594-.266-.594-.594z\\\"/>\\n <path fill-rule=\\\"evenodd\\\"\\n d=\\\"M1.802 8.146C1.331 8.321 1 8.776 1 9.302v.803c0 .543 0 .815.071 1.038a1.5 1.5 0 0 0 .914.953c.219.08.491.092 1.034.114l2.731.114 2.731-.114c.543-.023.814-.034 1.034-.114a1.5 1.5 0 0 0 .914-.953c.071-.223.071-.494.071-1.038v-.803c0-.526-.331-.98-.802-1.156l-1.02-2.04A2 2 0 0 0 6.889 5H4.611a2 2 0 0 0-1.789 1.106l-1.02 2.04zM6.889 6H4.611a1 1 0 0 0-.894.553l-.703 1.406h.955 3.563.955l-.703-1.406A1 1 0 0 0 6.889 6zM2 10.4a1 1 0 1 1 2 0 .5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm6.5-1a1 1 0 0 0-1 1 .5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5 1 1 0 0 0-1-1z\\\"/>\\n <path d=\\\"M1.792 13.115c0-.109.089-.198.198-.198h1.979c.109 0 .198.089.198.198 0 .328-.266.594-.594.594H2.385c-.328 0-.594-.266-.594-.594zm5.739-.198c-.109 0-.198.089-.198.198 0 .328.266.594.594.594h1.188c.328 0 .594-.266.594-.594 0-.109-.089-.198-.198-.198h-1.98z\\\"/>\\n </g>\\n</svg>\\n\"","import { TomTomConfig } from '@tomtom-org/maps-sdk/core';\nimport type { ToBeAddedLayerSpecTemplate, ToBeAddedLayerSpecWithoutSource } from '../../shared';\nimport { buildRoutingLayers } from '../layers/routingLayers';\nimport type { RouteLayersConfig, RoutingModuleConfig } from '../types/routeModuleConfig';\nimport type { RoutingLayersSpecs } from '../types/routingSourcesAndLayers';\n\n/**\n * @ignore\n */\nconst mapLayerSpecs = (\n layerSpecs: Record<string, Partial<ToBeAddedLayerSpecTemplate>> = {},\n layerIDPrefix?: string,\n): ToBeAddedLayerSpecWithoutSource[] =>\n // The key of the entry is the layer ID:\n Object.entries(layerSpecs).map(\n ([id, spec]) =>\n ({\n ...spec,\n id: layerIDPrefix ? `${layerIDPrefix}-${id}` : id,\n }) as ToBeAddedLayerSpecWithoutSource,\n );\n\n/**\n * @ignore\n */\nexport const createLayersSpecs = (\n layerConfigs: RouteLayersConfig = {},\n layerIDPrefix?: string,\n): RoutingLayersSpecs => ({\n mainLines: mapLayerSpecs(layerConfigs.mainLines, layerIDPrefix),\n waypoints: mapLayerSpecs(layerConfigs.waypoints, layerIDPrefix),\n chargingStops: mapLayerSpecs(layerConfigs?.chargingStops, layerIDPrefix),\n ferries: mapLayerSpecs(layerConfigs.sections?.ferry, layerIDPrefix),\n incidents: mapLayerSpecs(layerConfigs.sections?.incident, layerIDPrefix),\n tollRoads: mapLayerSpecs(layerConfigs.sections?.tollRoad, layerIDPrefix),\n tunnels: mapLayerSpecs(layerConfigs.sections?.tunnel, layerIDPrefix),\n vehicleRestricted: mapLayerSpecs(layerConfigs.sections?.vehicleRestricted, layerIDPrefix),\n instructionLines: mapLayerSpecs(layerConfigs.instructionLines, layerIDPrefix),\n instructionArrows: mapLayerSpecs(layerConfigs.instructionArrows, layerIDPrefix),\n summaryBubbles: mapLayerSpecs(layerConfigs.summaryBubbles, layerIDPrefix),\n});\n\n/**\n * @ignore\n */\nexport const routeModuleConfigWithDefaults = (\n config: RoutingModuleConfig | undefined,\n layerIDPrefix: string,\n instanceIndex: number,\n): RoutingModuleConfig => {\n const globalDisplayUnits = TomTomConfig.instance.get().displayUnits;\n const displayUnits = config?.displayUnits;\n return {\n // First apply the provided configuration not to lose any properties:\n ...config,\n ...(displayUnits ? {} : { displayUnits: globalDisplayUnits }),\n layers: buildRoutingLayers(config, layerIDPrefix, instanceIndex),\n };\n};\n","import {\n ChargingStop,\n ChargingStopProps,\n formatDuration,\n generateId,\n type Place,\n type Routes,\n} from '@tomtom-org/maps-sdk/core';\nimport { FeatureCollection, Point } from 'geojson';\nimport { PlaceDisplayProps } from '../../places';\nimport type { DisplayRouteProps, RouteStateProps } from '../types/displayRoutes';\nimport { RoutingModuleConfig } from '../types/routeModuleConfig';\n\nconst getIconID = (chargingStop: ChargingStop, config: RoutingModuleConfig | undefined): string => {\n const iconConfig = config?.chargingStops?.icon;\n if (iconConfig?.mapping) {\n const mapping = iconConfig.mapping;\n switch (mapping.basedOn) {\n case 'chargingSpeed':\n if (chargingStop.properties.chargingConnectionInfo?.chargingSpeed) {\n return mapping.value[chargingStop.properties.chargingConnectionInfo?.chargingSpeed];\n }\n break;\n case 'custom':\n return mapping.fn(chargingStop);\n }\n }\n\n // default: (genesis-like) categorySet ID for \"EV Charging Station\" based on search\n return '7309';\n};\n\nconst formatTitle = (chargingStop: ChargingStop): string => {\n const properties = chargingStop.properties;\n return properties.chargingParkName ?? (properties.chargingParkOperatorName as string);\n};\n\ntype DisplayChargingStopProps = PlaceDisplayProps &\n ChargingStopProps &\n RouteStateProps & {\n chargingDuration: string;\n chargingPower: string;\n };\n\ntype DisplayChargingStops = FeatureCollection<Point, DisplayChargingStopProps>;\n\n/**\n * Generates display-ready charging stations for the given planning context ones.\n * @param routes The routes return for ldEV.\n * @param config The charging stops display configuration.\n * @see chargingStopLayers\n * @ignore\n */\nexport const toDisplayChargingStops = (\n routes: Routes<DisplayRouteProps>,\n config: RoutingModuleConfig | undefined,\n): DisplayChargingStops => {\n const displayChargingStops: Place<DisplayChargingStopProps>[] = [];\n\n if (config?.chargingStops?.visible !== false) {\n for (const route of routes.features) {\n for (const leg of route.properties.sections.leg) {\n const chargingStop = leg.summary.chargingInformationAtEndOfLeg;\n\n if (chargingStop) {\n const properties = chargingStop.properties;\n displayChargingStops.push({\n ...chargingStop,\n properties: {\n ...chargingStop.properties,\n id: chargingStop.properties.chargingParkId ?? generateId(),\n iconID: getIconID(chargingStop, config),\n title: formatTitle(chargingStop),\n chargingPower: `${properties.chargingConnectionInfo?.chargingPowerInkW} kW`,\n chargingDuration: formatDuration(\n properties.chargingTimeInSeconds,\n config?.displayUnits?.time,\n ) as string,\n routeState: route.properties.routeState,\n },\n });\n }\n }\n }\n }\n return { type: 'FeatureCollection', features: displayChargingStops };\n};\n","import { formatDuration, type TrafficSectionProps } from '@tomtom-org/maps-sdk/core';\nimport type { DisplayTrafficSectionProps } from '../types/routeSections';\n\nconst hasJam = (sectionProps: TrafficSectionProps): boolean => sectionProps.categories.includes('jam');\n\nconst buildTitle = (sectionProps: TrafficSectionProps): string | undefined => {\n if (hasJam(sectionProps)) {\n return formatDuration(sectionProps.delayInSeconds);\n }\n return undefined;\n};\n\nconst toTrafficJamIconSuffix = (title: string | undefined): 'collapsed' | 'small' | 'medium' | 'large' => {\n if (!title?.length) {\n return 'collapsed';\n }\n if (title.length < 6) {\n // 1 digit minutes\n return 'small';\n }\n if (title.length < 8) {\n // 2 digit minutes\n return 'medium';\n }\n // hours + minutes\n return 'large';\n};\n\nconst toJamIconID = (sectionProps: TrafficSectionProps, title: string | undefined): string | null => {\n if (!hasJam(sectionProps)) {\n return null;\n }\n const magnitude = sectionProps.magnitudeOfDelay ?? 'unknown';\n return `traffic-jam-${magnitude}-${toTrafficJamIconSuffix(title)}`;\n};\n\nconst toCauseIconID = (sectionProps: TrafficSectionProps): string | null => {\n const firstNonJamCategory = sectionProps.categories.find((category) => category !== 'jam');\n switch (firstNonJamCategory) {\n case 'accident':\n return 'traffic-incidents-accident';\n case 'roadworks':\n return 'traffic-incidents-roadworks';\n case 'road-closed':\n return 'traffic-incidents-road_closed';\n case 'danger':\n case 'animals-on-road':\n return 'traffic-incidents-danger';\n case 'broken-down-vehicle':\n return 'traffic-incidents-broken_down_vehicle';\n case 'lane-closed':\n case 'narrow-lanes':\n return 'traffic-incidents-lane_closed';\n case 'wind':\n return 'traffic-incidents-wind';\n case 'fog':\n return 'traffic-incidents-fog';\n case 'rain':\n return 'traffic-incidents-rain';\n case 'frost':\n return 'traffic-incidents-frost';\n case 'flooding':\n return 'traffic-incidents-flooding';\n default:\n return null;\n }\n};\n\n/**\n * @ignore\n */\nexport const toDisplayTrafficSectionProps = (\n sectionProps: TrafficSectionProps,\n): Omit<DisplayTrafficSectionProps, 'routeState' | 'routeIndex'> => {\n const title = buildTitle(sectionProps);\n const jamIconID = toJamIconID(sectionProps, title);\n const causeIconID = toCauseIconID(sectionProps);\n return {\n ...sectionProps,\n ...(jamIconID && { jamIconID }),\n ...(causeIconID && { causeIconID }),\n ...(title && { title }),\n };\n};\n","import type { CommonPlaceProps, Waypoint, WaypointLike, Waypoints } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPosition } from '@tomtom-org/maps-sdk/core';\nimport type { Point, Position } from 'geojson';\nimport { suffixNumber } from '../../shared/layers/utils';\nimport {\n WAYPOINT_FINISH_IMAGE_ID,\n WAYPOINT_SOFT_IMAGE_ID,\n WAYPOINT_START_IMAGE_ID,\n WAYPOINT_STOP_IMAGE_ID,\n} from '../layers/waypointLayers';\nimport type { PlanningWaypoint } from '../types/planningWaypoint';\nimport type { WaypointsConfig } from '../types/routeModuleConfig';\nimport type { WaypointDisplayProps, WaypointIndexType } from '../types/waypointDisplayProps';\nimport { FINISH_INDEX, MIDDLE_INDEX, START_INDEX } from '../types/waypointDisplayProps';\n\nconst indexTypeFor = (index: number, arrayLength: number): WaypointIndexType =>\n index === 0 ? START_INDEX : index < arrayLength - 1 ? MIDDLE_INDEX : FINISH_INDEX;\n\n/**\n * Builds the title of the given waypoint.\n * @param waypoint The waypoint for which to build the title.\n */\nexport const buildWaypointTitle = (waypoint: Waypoint): string | undefined => {\n const placeProperties = waypoint?.properties as CommonPlaceProps;\n return placeProperties?.poi?.name ?? placeProperties?.address?.freeformAddress ?? undefined;\n};\n\nexport const getImageIDForWaypoint = (\n waypoint: Waypoint,\n indexType: WaypointIndexType,\n instanceIndex?: number,\n): string => {\n if (waypoint.properties.radiusMeters) {\n return instanceIndex !== undefined\n ? suffixNumber(WAYPOINT_SOFT_IMAGE_ID, instanceIndex)\n : WAYPOINT_SOFT_IMAGE_ID;\n }\n let baseImageID: string;\n switch (indexType) {\n case 'start':\n baseImageID = WAYPOINT_START_IMAGE_ID;\n break;\n case 'finish':\n baseImageID = WAYPOINT_FINISH_IMAGE_ID;\n break;\n default:\n baseImageID = WAYPOINT_STOP_IMAGE_ID;\n break;\n }\n return instanceIndex !== undefined ? suffixNumber(baseImageID, instanceIndex) : baseImageID;\n};\n\nconst toWaypointFromPosition = (position: Position): Waypoint => ({\n type: 'Feature',\n geometry: {\n type: 'Point',\n coordinates: position,\n },\n properties: {},\n});\n\nconst toWaypointFromPoint = (point: Point): Waypoint => ({\n type: 'Feature',\n geometry: point,\n properties: {},\n ...(point.bbox && { bbox: point.bbox }),\n});\n\nconst asWaypoint = (waypointInput: WaypointLike): Waypoint => {\n if (Array.isArray(waypointInput)) {\n return toWaypointFromPosition(waypointInput);\n }\n if (waypointInput.type === 'Point') {\n return toWaypointFromPoint(waypointInput);\n }\n return waypointInput as Waypoint;\n};\n\n/**\n * Determines whether the given waypoint is a regular start/stop/destination with guidance attached,\n * as opposed to a circle (soft) waypoint.\n * @param waypoint The waypoint to verify.\n */\nexport const isHardWaypoint = (waypoint: Waypoint): boolean => !waypoint.properties.radiusMeters;\n\n/**\n * Generates display-ready waypoints for the given planning context ones.\n * @param waypoints The planning context waypoints.\n * @param options\n * @param instanceIndex Optional instance index for supporting multiple RoutingModule instances\n */\nexport const toDisplayWaypoints = (\n waypoints: PlanningWaypoint[],\n options: WaypointsConfig | undefined,\n instanceIndex?: number,\n): Waypoints<WaypointDisplayProps> => {\n // Since waypoints are of mixed types (hard and soft), we need to calculate the hard-only indexes\n // in case we have stops with numbered icons:\n let hardWaypointIndex = -1;\n return {\n type: 'FeatureCollection',\n features: waypoints\n .map((waypointInput, index) => {\n if (!waypointInput) {\n // (we consider placeholder waypoints to be \"hard\", since they typically occupy a position in a planner panel)\n hardWaypointIndex++;\n // (will be filtered out below - we don't pre-filter it to keep the original input index)\n return null as unknown as Waypoint<WaypointDisplayProps>;\n }\n const waypoint: Waypoint = asWaypoint(waypointInput);\n const indexType = indexTypeFor(index, waypoints.length);\n const hardWaypoint = isHardWaypoint(waypoint);\n if (hardWaypoint) {\n hardWaypointIndex++;\n }\n const title = buildWaypointTitle(waypoint);\n const id = (waypoint.id as string) ?? generateId();\n return {\n ...waypoint,\n ...(options?.entryPoints === 'main-when-available' && {\n geometry: {\n type: 'Point',\n // We replace the waypoint coordinates with their main entry point ones:\n coordinates: getPosition(waypoint, { useEntryPoint: 'main-when-available' }),\n } as Point,\n }),\n id,\n properties: {\n ...waypoint.properties,\n id,\n index,\n indexType,\n ...(title && { title }),\n iconID: getImageIDForWaypoint(waypoint, indexType, instanceIndex),\n ...(hardWaypoint && indexType === MIDDLE_INDEX && { stopDisplayIndex: hardWaypointIndex }),\n },\n };\n })\n .filter((feature) => feature),\n };\n};\n","import { generateId, Route, Routes, SectionProps, SectionType } from '@tomtom-org/maps-sdk/core';\nimport type { DisplayRouteProps } from '../types/displayRoutes';\nimport type { DisplaySectionProps, RouteSection, RouteSections } from '../types/routeSections';\n\nconst buildRouteSectionsFromRoute = <\n S extends SectionProps = SectionProps,\n D extends DisplaySectionProps = DisplaySectionProps,\n>(\n route: Route<DisplayRouteProps>,\n sectionType: SectionType,\n displaySectionPropsBuilder?: (\n sectionProps: S,\n routeProps?: DisplayRouteProps,\n ) => Omit<D, 'routeState' | 'routeIndex'>,\n): RouteSection<D>[] =>\n (route.properties.sections[sectionType] as S[])?.map((sectionProps) => {\n const id = sectionProps.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: {\n type: 'LineString',\n coordinates: route.geometry.coordinates.slice(\n sectionProps.startPointIndex,\n sectionProps.endPointIndex + 1,\n ),\n },\n properties: {\n ...(displaySectionPropsBuilder\n ? displaySectionPropsBuilder(sectionProps, route.properties)\n : sectionProps),\n routeState: route.properties.routeState,\n routeIndex: route.properties.index,\n id, // we need id in properties due to promoteId feature\n } as D,\n };\n }) || [];\n\n/**\n * Builds display-ready LineString features to render the sections of a given type, from the given route.\n * @param routes The routes from which to extract the section props.\n * @param sectionType The type of the sections to extract.\n * @param displaySectionPropsBuilder An optional function which will convert each section props into an extended display-ready version.\n * @ignore\n */\nexport const toDisplayRouteSections = <\n S extends SectionProps = SectionProps,\n D extends DisplaySectionProps = DisplaySectionProps,\n>(\n routes: Routes<DisplayRouteProps>,\n sectionType: SectionType,\n displaySectionPropsBuilder?: (\n sectionProps: S,\n routeProps?: DisplayRouteProps,\n ) => Omit<D, 'routeState' | 'routeIndex'>,\n): RouteSections<D> => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap((route) =>\n buildRouteSectionsFromRoute<S, D>(route, sectionType, displaySectionPropsBuilder),\n ),\n});\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FeatureCollection, Geometry } from 'geojson';\nimport type { GeoJSONSourceWithLayers } from '../../shared';\nimport type { DisplayRouteProps, DisplayRouteRelatedProps } from '../types/displayRoutes';\n\n/**\n * Rebuilds route-related display features such as sections or instructions, with the updated route selection based on the given routes.\n * @ignore\n */\nexport const rebuildFeaturesWithRouteSelection = <T extends FeatureCollection<Geometry, DisplayRouteRelatedProps>>(\n routes: Routes<DisplayRouteProps>,\n featureCollection: T,\n): T => ({\n ...featureCollection,\n features: featureCollection.features.map((features) => ({\n ...features,\n properties: {\n ...features.properties,\n routeState: routes.features[features.properties.routeIndex || 0].properties.routeState,\n },\n })),\n});\n\n/**\n * @ignore\n */\nexport const showFeaturesWithRouteSelection = <T extends FeatureCollection<Geometry, DisplayRouteRelatedProps>>(\n routesWithSelection: Routes<DisplayRouteProps>,\n sourceWithLayers: GeoJSONSourceWithLayers<T>,\n): void =>\n sourceWithLayers.show(rebuildFeaturesWithRouteSelection(routesWithSelection, sourceWithLayers.shownFeatures));\n","import {\n DelayMagnitude,\n DisplayUnits,\n formatDistance,\n formatDuration,\n generateId,\n Route,\n Routes,\n TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { DisplayRouteProps, DisplayRouteSummaries } from '../types/displayRoutes';\n\n/**\n * Builds map display-ready routes, applying default style props.\n * @ignore\n */\nexport const toDisplayRoutes = (routes: Route | Routes, selectedIndex = 0): Routes<DisplayRouteProps> => {\n const routesCollection: Routes = 'features' in routes ? routes : { type: 'FeatureCollection', features: [routes] };\n return {\n ...routesCollection,\n features: routesCollection.features.map((route, index) => {\n const id = route.id ?? generateId();\n return {\n ...route,\n id,\n properties: {\n ...route.properties,\n id, // we need id in properties due to promoteId feature\n routeState: index === selectedIndex ? 'selected' : 'deselected',\n },\n };\n }),\n };\n};\n\nconst hasMagnitude = (sections: TrafficSectionProps[], magnitude: DelayMagnitude): boolean =>\n sections.some((section) => section.magnitudeOfDelay === magnitude);\n\nconst summaryDelayMagnitude = (route: Route): DelayMagnitude | undefined => {\n const trafficSections = route.properties.sections.traffic;\n if (!trafficSections?.length) {\n return undefined;\n }\n if (hasMagnitude(trafficSections, 'major')) {\n return 'major';\n }\n if (hasMagnitude(trafficSections, 'moderate')) {\n return 'moderate';\n }\n if (hasMagnitude(trafficSections, 'minor')) {\n return 'minor';\n }\n return undefined;\n};\n/**\n * Builds map display-ready route summaries based on display routes.\n * @ignore\n */\nexport const toDisplayRouteSummaries = (\n routes: Routes<DisplayRouteProps>,\n displayUnits?: DisplayUnits,\n): DisplayRouteSummaries => ({\n type: 'FeatureCollection',\n features: routes.features.map((route) => {\n const summary = route.properties.summary;\n const routeCoordinates = route.geometry.coordinates;\n const formattedTraffic = formatDuration(summary.trafficDelayInSeconds, displayUnits?.time);\n const magnitudeOfDelay = summaryDelayMagnitude(route);\n const id = route.id ?? generateId();\n return {\n type: 'Feature',\n id,\n geometry: {\n type: 'Point',\n coordinates: routeCoordinates[Math.round(routeCoordinates.length / 2)],\n },\n properties: {\n id, // we need id in properties due to promoteId feature\n routeIndex: route.properties.index,\n routeState: route.properties.routeState,\n formattedDistance: formatDistance(summary.lengthInMeters, displayUnits?.distance),\n ...(magnitudeOfDelay && { magnitudeOfDelay }),\n ...(formattedTraffic && { formattedTraffic }),\n formattedDuration: formatDuration(summary.travelTimeInSeconds, displayUnits?.time),\n },\n };\n }),\n});\n","import type { Route, Routes, Waypoint, Waypoints } from '@tomtom-org/maps-sdk/core';\nimport { isEqual } from 'lodash-es';\nimport type { StyleImageMetadata } from 'maplibre-gl';\nimport {\n AbstractMapModule,\n EventsModule,\n GeoJSONSourceWithLayers,\n mapStyleLayerIDs,\n SVGIconStyleOptions,\n} from '../shared';\nimport { suffixNumber } from '../shared/layers/utils';\nimport { addLayers, addOrUpdateImage, updateLayersAndSource, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { INSTRUCTION_ARROW_IMAGE_ID } from './layers/guidanceLayers';\nimport { DESELECTED_SUMMARY_POPUP_IMAGE_ID, SELECTED_SUMMARY_POPUP_IMAGE_ID } from './layers/routeMainLineLayers';\nimport { MAJOR_DELAY_COLOR, MINOR_DELAY_LABEL_COLOR, MODERATE_DELAY_COLOR, UNKNOWN_DELAY_COLOR } from './layers/shared';\nimport {\n TRAFFIC_CLEAR_IMAGE_ID,\n TRAFFIC_MAJOR_IMAGE_ID,\n TRAFFIC_MINOR_IMAGE_ID,\n TRAFFIC_MODERATE_IMAGE_ID,\n} from './layers/summaryBubbleLayers';\nimport {\n WAYPOINT_FINISH_IMAGE_ID,\n WAYPOINT_SOFT_IMAGE_ID,\n WAYPOINT_START_IMAGE_ID,\n WAYPOINT_STOP_IMAGE_ID,\n} from './layers/waypointLayers';\nimport {\n instructionArrowIconImg,\n softWaypointIcon,\n summaryBubbleImageOptions,\n summaryMapBubbleImg,\n trafficImg,\n waypointFinishIcon,\n waypointIcon,\n waypointStartIcon,\n} from './resources';\nimport type { DisplayRouteProps, DisplayRouteSummary } from './types/displayRoutes';\nimport type { DisplayInstruction } from './types/guidance';\nimport type { PlanningWaypoint } from './types/planningWaypoint';\nimport type { RoutingModuleConfig } from './types/routeModuleConfig';\nimport type { DisplayTrafficSectionProps, RouteSection, RouteSections } from './types/routeSections';\nimport type { RoutingLayersSpecs, RoutingSourcesWithLayers } from './types/routingSourcesAndLayers';\nimport type { ShowRoutesOptions } from './types/showOptions';\nimport type { WaypointDisplayProps } from './types/waypointDisplayProps';\nimport { createLayersSpecs, routeModuleConfigWithDefaults } from './util/config';\nimport { toDisplayChargingStops } from './util/displayChargingStops';\nimport { toDisplayTrafficSectionProps } from './util/displayTrafficSectionProps';\nimport { toDisplayWaypoints } from './util/displayWaypoints';\nimport { toDisplayInstructionArrows, toDisplayInstructions } from './util/guidance';\nimport { toDisplayRouteSections } from './util/routeSections';\nimport { showFeaturesWithRouteSelection } from './util/routeSelection';\nimport { toDisplayRouteSummaries, toDisplayRoutes } from './util/routes';\n\n/**\n * Map module for displaying and managing route visualizations.\n *\n * The RoutingModule provides comprehensive functionality for displaying routes on the map,\n * including route lines, alternative routes, turn-by-turn guidance, and interactive waypoints.\n * It integrates seamlessly with the TomTom Routing API.\n *\n * @remarks\n * **Features:**\n * - Display route lines with customizable styling\n * - Show alternative routes with different styling\n * - Interactive waypoint markers (drag, add, remove)\n * - Turn-by-turn guidance instructions\n * - Route section highlighting\n * - Distance and duration information\n * - Support for multiple routes simultaneously\n *\n * **Common Use Cases:**\n * - Turn-by-turn navigation displays\n * - Route planning and comparison\n * - Multi-stop route optimization\n * - Interactive route editing\n * - Fleet management route visualization\n *\n * @example\n * ```typescript\n * // Create the module\n * const routingModule = await RoutingModule.getInstance(map, {\n * displayUnits: {\n * distance: { type: 'metric' }\n * }\n * });\n *\n * // Calculate and display a route\n * const result = await calculateRoute({\n * key: 'your-api-key',\n * locations: [\n * [4.9041, 52.3676], // Amsterdam\n * [4.4777, 51.9244] // Rotterdam\n * ],\n * routeOptions: {\n * travelMode: 'car',\n * routeType: 'fastest'\n * }\n * });\n *\n * await routingModule.showRoutes(result);\n * ```\n *\n * @see [Routing Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/routing)\n *\n * @group Routing\n */\nexport class RoutingModule extends AbstractMapModule<RoutingSourcesWithLayers, RoutingModuleConfig> {\n private static lastInstanceIndex = -1;\n private layersSpecs!: RoutingLayersSpecs;\n private layerIDPrefix!: string;\n /**\n * The index of this instance, to generate unique source and layer IDs.\n * * Starts with 0 and each instance increments it by one.\n * @private\n */\n private instanceIndex!: number;\n\n /**\n * Make sure the map is ready before create an instance of the module and any other interaction with the map\n * @param tomtomMap The TomTomMap instance.\n * @param config The module optional configuration\n * @returns {Promise} Returns a promise with a new instance of this module\n *\n * @remarks\n * **Configuration Options:**\n * - `displayUnits`: Distance units (metric/imperial)\n * - `waypointsSource`: Waypoint entry point options\n * - `layers`: Complete layer styling configuration\n *\n * **Default Styling:**\n * If no custom layers are provided, uses {@link defaultRoutingLayers}.\n *\n * @example\n * Default initialization:\n * ```typescript\n * const routingModule = await RoutingModule.get(map);\n * ```\n *\n * @example\n * With custom configuration:\n * ```typescript\n * const routingModule = await RoutingModule.get(map, {\n * displayUnits: 'imperial',\n * waypointsSource: {\n * entryPoints: 'main-when-available'\n * }\n * });\n * ```\n */\n static async get(tomtomMap: TomTomMap, config?: RoutingModuleConfig): Promise<RoutingModule> {\n await waitUntilMapIsReady(tomtomMap);\n return new RoutingModule(tomtomMap, config);\n }\n\n private constructor(map: TomTomMap, config?: RoutingModuleConfig) {\n super('geojson', map, config);\n }\n\n private createSourcesWithLayers(layersSpecs: RoutingLayersSpecs): RoutingSourcesWithLayers {\n const sourcePrefix = suffixNumber('routes', this.instanceIndex);\n return {\n mainLines: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-mainLines`,\n layersSpecs.mainLines,\n false,\n ),\n waypoints: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-waypoints`,\n layersSpecs.waypoints,\n false,\n ),\n incidents: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-incidents`,\n layersSpecs.incidents,\n false,\n ),\n ferries: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-ferries`,\n layersSpecs.ferries,\n false,\n ),\n chargingStops: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-chargingStops`,\n layersSpecs.chargingStops,\n false,\n ),\n tollRoads: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-tollRoads`,\n layersSpecs.tollRoads,\n false,\n ),\n tunnels: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-tunnels`,\n layersSpecs.tunnels,\n false,\n ),\n vehicleRestricted: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-vehicleRestricted`,\n layersSpecs.vehicleRestricted,\n false,\n ),\n instructionLines: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-instructionLines`,\n layersSpecs.instructionLines,\n false,\n ),\n instructionArrows: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-instructionArrows`,\n layersSpecs.instructionArrows,\n false,\n ),\n summaryBubbles: new GeoJSONSourceWithLayers(\n this.mapLibreMap,\n `${sourcePrefix}-summaryBubbles`,\n layersSpecs.summaryBubbles,\n false,\n ),\n };\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers(config?: RoutingModuleConfig, restore?: boolean): RoutingSourcesWithLayers {\n // Only increment the instance index for new instances, not for restore operations\n if (!restore) {\n RoutingModule.lastInstanceIndex++;\n this.instanceIndex = RoutingModule.lastInstanceIndex;\n this.layerIDPrefix = suffixNumber('routes', this.instanceIndex);\n }\n\n this.layersSpecs = createLayersSpecs(\n routeModuleConfigWithDefaults(config, this.layerIDPrefix, this.instanceIndex).layers,\n this.layerIDPrefix,\n );\n const routingSourcesWithLayers: RoutingSourcesWithLayers = this.createSourcesWithLayers(this.layersSpecs);\n addLayers(\n Object.values(routingSourcesWithLayers).flatMap((source) => source._layerSpecs),\n this.mapLibreMap,\n );\n\n const svgIconOptions: SVGIconStyleOptions = {\n // first comes the main theme fill color, if any:\n fillColor: config?.theme?.mainColor,\n // then come any icon style configs for the waypoint icons, if any:\n ...config?.waypoints?.icon?.style,\n };\n const options: Partial<StyleImageMetadata> = { pixelRatio: 2 };\n\n // Generate instance-specific image IDs to support multiple RoutingModule instances\n const waypointStartImageId = suffixNumber(WAYPOINT_START_IMAGE_ID, this.instanceIndex);\n const waypointStopImageId = suffixNumber(WAYPOINT_STOP_IMAGE_ID, this.instanceIndex);\n const waypointSoftImageId = suffixNumber(WAYPOINT_SOFT_IMAGE_ID, this.instanceIndex);\n const waypointFinishImageId = suffixNumber(WAYPOINT_FINISH_IMAGE_ID, this.instanceIndex);\n const instructionArrowImageId = suffixNumber(INSTRUCTION_ARROW_IMAGE_ID, this.instanceIndex);\n const selectedSummaryPopupImageId = suffixNumber(SELECTED_SUMMARY_POPUP_IMAGE_ID, this.instanceIndex);\n const deselectedSummaryPopupImageId = suffixNumber(DESELECTED_SUMMARY_POPUP_IMAGE_ID, this.instanceIndex);\n const trafficClearImageId = suffixNumber(TRAFFIC_CLEAR_IMAGE_ID, this.instanceIndex);\n const trafficMajorImageId = suffixNumber(TRAFFIC_MAJOR_IMAGE_ID, this.instanceIndex);\n const trafficModerateImageId = suffixNumber(TRAFFIC_MODERATE_IMAGE_ID, this.instanceIndex);\n const trafficMinorImageId = suffixNumber(TRAFFIC_MINOR_IMAGE_ID, this.instanceIndex);\n\n // loading of extra assets if not present in the map style:\n this.addImageIfNotExisting(waypointStartImageId, waypointStartIcon(svgIconOptions), options);\n this.addImageIfNotExisting(waypointStopImageId, waypointIcon(undefined, svgIconOptions), options);\n this.addImageIfNotExisting(waypointSoftImageId, softWaypointIcon(), options);\n this.addImageIfNotExisting(waypointFinishImageId, waypointFinishIcon(svgIconOptions), options);\n this.addImageIfNotExisting(instructionArrowImageId, instructionArrowIconImg, options);\n this.addImageIfNotExisting(\n selectedSummaryPopupImageId,\n summaryMapBubbleImg('white'),\n summaryBubbleImageOptions,\n );\n this.addImageIfNotExisting(\n deselectedSummaryPopupImageId,\n summaryMapBubbleImg('#EEEEEE'),\n summaryBubbleImageOptions,\n );\n this.addImageIfNotExisting(trafficClearImageId, trafficImg(UNKNOWN_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficMajorImageId, trafficImg(MAJOR_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficModerateImageId, trafficImg(MODERATE_DELAY_COLOR), options);\n this.addImageIfNotExisting(trafficMinorImageId, trafficImg(MINOR_DELAY_LABEL_COLOR), options);\n\n // If we have custom icons, ensure they're added to the map style:\n for (const customChargingStopIcon of config?.chargingStops?.icon?.customIcons ?? []) {\n this.addImageIfNotExisting(customChargingStopIcon.id, customChargingStopIcon.image as string, {\n pixelRatio: customChargingStopIcon.pixelRatio ?? 2,\n });\n }\n\n return routingSourcesWithLayers;\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config?: RoutingModuleConfig) {\n const mergedConfig = routeModuleConfigWithDefaults(config, this.layerIDPrefix, this.instanceIndex);\n\n // If there was already some config set, we must update the changes:\n if (this.config) {\n // replace existing configuration with new one\n const newLayersSpecs = createLayersSpecs(mergedConfig.layers, this.layerIDPrefix);\n\n // here we assume that keys for layer specs and sources are the same, please keep it that way to simplify the logic\n Object.keys(newLayersSpecs).forEach((layersSpecID) => {\n const id = layersSpecID as keyof RoutingSourcesWithLayers;\n updateLayersAndSource(\n newLayersSpecs[id],\n this.layersSpecs[id],\n this.sourcesWithLayers[id],\n this.mapLibreMap,\n );\n });\n // we need to add layers correctly\n const listOfSources = Object.values(this.sourcesWithLayers) as GeoJSONSourceWithLayers[];\n addLayers(\n listOfSources.flatMap((source) => source._layerSpecs),\n this.mapLibreMap,\n );\n // set the correct visibility if there are new layers\n listOfSources.forEach((source) => source.setLayersVisible(!!source.shownFeatures.features.length));\n this.layersSpecs = newLayersSpecs;\n }\n\n // Summary bubbles have dedicated sources and contain distance-units dependent text ...\n // ... so we need to re-show or clear them if relevant config parts changed:\n const summaryBubblesVisible = mergedConfig.summaryBubbles?.visible !== false;\n const visibilityChanged = !isEqual(this.config?.summaryBubbles?.visible, mergedConfig.summaryBubbles?.visible);\n const displayUnitsChanged = !isEqual(this.config?.displayUnits, mergedConfig.displayUnits);\n const hasSummaryBubbles = this.sourcesWithLayers.summaryBubbles.shownFeatures.features.length > 0;\n const hasRoutes = this.sourcesWithLayers.mainLines.shownFeatures.features.length > 0;\n\n if (!summaryBubblesVisible && visibilityChanged) {\n this.sourcesWithLayers.summaryBubbles.clear();\n } else if (\n summaryBubblesVisible &&\n ((visibilityChanged && hasRoutes) || (displayUnitsChanged && hasSummaryBubbles))\n ) {\n this.sourcesWithLayers.summaryBubbles.show(\n toDisplayRouteSummaries(this.sourcesWithLayers.mainLines.shownFeatures, mergedConfig.displayUnits),\n );\n }\n\n return mergedConfig;\n }\n\n /**\n * @ignore\n */\n protected restoreDataAndConfigImpl() {\n const previouslyShown = Object.entries(this.sourcesWithLayers)\n .map((entry) => ({\n [entry[0]]: entry[1].shownFeatures,\n }))\n .reduce((acc, item) => ({ ...acc, ...item }), {}) as Record<keyof RoutingSourcesWithLayers, any>;\n\n this.initSourcesWithLayers(this.config, true);\n this._applyConfig(this.config);\n\n for (const key of Object.keys(previouslyShown) as (keyof RoutingSourcesWithLayers)[]) {\n this.sourcesWithLayers[key].show(previouslyShown[key]);\n }\n }\n\n private addImageIfNotExisting(\n imageId: string,\n image: string | HTMLImageElement,\n options: Partial<StyleImageMetadata> | undefined,\n ) {\n addOrUpdateImage('if-not-in-sprite', imageId, image, this.mapLibreMap, options);\n }\n\n /**\n * Displays the given routes on the map.\n *\n * @param routes - Route data from Routing API or custom routes.\n * @param options - Optional configuration for route selection and display.\n * @param options.selectedIndex - Index of the route to display as selected (default: 0).\n *\n * @remarks\n * **Behavior:**\n * - Replaces any previously shown routes\n * - Shows all route-related features: lines, sections, summaries, guidance\n * - First route is selected by default (appears more prominent)\n * - Waypoints are NOT shown automatically (use {@link showWaypoints})\n *\n * **Route Features:**\n * - Main route lines (selected and deselected styles)\n * - Traffic sections with delays\n * - Ferry, tunnel, and toll sections\n * - EV charging stations (for EV routes)\n * - Turn-by-turn instruction lines and arrows\n * - Summary bubbles with distance/time/traffic info\n *\n * @example\n * Show single route:\n * ```typescript\n * await routing.showRoutes(response.routes);\n * ```\n *\n * @example\n * Show multiple routes with specific selection:\n * ```typescript\n * await routing.showRoutes(response.routes, { selectedIndex: 1 });\n * ```\n *\n * @example\n * Complete routing workflow:\n * ```typescript\n * import { routing as routingAPI } from '@tomtom-international/maps-sdk-js/services';\n *\n * // Calculate route\n * const response = await routingAPI.calculateRoute({\n * locations: [[4.9, 52.4], [4.5, 51.9]],\n * traffic: true,\n * travelMode: 'car'\n * });\n *\n * // Display on map\n * const routing = await RoutingModule.get(map);\n * await routing.showRoutes(response.routes);\n * await routing.showWaypoints(response.routes[0].legs[0].points);\n * ```\n */\n async showRoutes(routes: Route | Routes, options?: ShowRoutesOptions) {\n const displayRoutes = toDisplayRoutes(routes, options?.selectedIndex);\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.mainLines.show(displayRoutes);\n this.sourcesWithLayers.vehicleRestricted.show(toDisplayRouteSections(displayRoutes, 'vehicleRestricted'));\n this.sourcesWithLayers.incidents.show(\n toDisplayRouteSections(displayRoutes, 'traffic', toDisplayTrafficSectionProps),\n );\n this.sourcesWithLayers.chargingStops.show(toDisplayChargingStops(displayRoutes, this.config));\n this.sourcesWithLayers.ferries.show(toDisplayRouteSections(displayRoutes, 'ferry'));\n this.sourcesWithLayers.tunnels.show(toDisplayRouteSections(displayRoutes, 'tunnel'));\n this.sourcesWithLayers.tollRoads.show(toDisplayRouteSections(displayRoutes, 'toll'));\n this.sourcesWithLayers.instructionLines.show(toDisplayInstructions(displayRoutes));\n this.sourcesWithLayers.instructionArrows.show(toDisplayInstructionArrows(displayRoutes));\n if (this.config?.summaryBubbles?.visible !== false) {\n this.sourcesWithLayers.summaryBubbles.show(\n toDisplayRouteSummaries(displayRoutes, this.config?.displayUnits),\n );\n } else {\n this.sourcesWithLayers.summaryBubbles.clear();\n }\n }\n\n /**\n * Clears any previously shown routes from the map.\n *\n * @remarks\n * - Clears all route-related layers (lines, sections, guidance, summaries)\n * - Does NOT clear waypoints (use {@link clearWaypoints})\n * - Module remains initialized and ready for new routes\n *\n * @example\n * ```typescript\n * await routing.clearRoutes();\n * ```\n */\n async clearRoutes() {\n await this.waitUntilModuleReady();\n for (const key of Object.keys(this.sourcesWithLayers) as (keyof RoutingSourcesWithLayers)[]) {\n if (key !== 'waypoints') {\n this.sourcesWithLayers[key as keyof RoutingSourcesWithLayers].clear();\n }\n }\n }\n\n /**\n * Changes which route appears as selected.\n *\n * @param index - Zero-based index of the route to select.\n *\n * @remarks\n * **Visual Changes:**\n * - Selected route appears more prominent (thicker, brighter)\n * - Previously selected route becomes deselected style\n * - Updates all route-related features (sections, guidance)\n *\n * **Requirements:**\n * - Route must already be displayed via {@link showRoutes}\n * - Index must be within range of displayed routes\n *\n * @example\n * ```typescript\n * // Show multiple routes\n * await routingModule.showRoutes(routes);\n *\n * // User clicks alternative route\n * await routingModule.selectRoute(1);\n *\n * // Switch back to first route\n * await routingModule.selectRoute(0);\n * ```\n */\n async selectRoute(index: number) {\n const updatedRoutes = toDisplayRoutes(this.sourcesWithLayers.mainLines.shownFeatures, index);\n\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.mainLines.show(updatedRoutes);\n // TODO: simply update route style instead of regenerating EV stations again\n this.sourcesWithLayers.chargingStops.show(toDisplayChargingStops(updatedRoutes, this.config));\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.vehicleRestricted);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.incidents);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.ferries);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.tollRoads);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.tunnels);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.instructionLines);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.instructionArrows);\n showFeaturesWithRouteSelection(updatedRoutes, this.sourcesWithLayers.summaryBubbles);\n }\n\n /**\n * Shows the given waypoints on the map.\n * @param waypoints The waypoint-like inputs to show.\n */\n async showWaypoints(waypoints: PlanningWaypoint[] | Waypoints) {\n const displayWaypoints = Array.isArray(waypoints)\n ? toDisplayWaypoints(waypoints, this.config?.waypoints, this.instanceIndex)\n : // FeatureCollection expected:\n toDisplayWaypoints(waypoints.features as PlanningWaypoint[], this.config?.waypoints, this.instanceIndex);\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.waypoints.show(displayWaypoints);\n }\n\n /**\n * Clears any previously shown waypoints from the map.\n * * If nothing was shown before, nothing happens.\n */\n async clearWaypoints() {\n await this.waitUntilModuleReady();\n this.sourcesWithLayers.waypoints.clear();\n }\n\n /**\n * Returns the currently shown routes and waypoints.\n *\n * @returns An object containing all currently displayed routing data.\n *\n * @remarks\n * Returns the exact data that was passed to the `showRoutes()` and `showWaypoints()` methods.\n *\n * **Returned Data:**\n * - `mainLines`: Main route lines (selected and alternative)\n * - `waypoints`: Route waypoints (start, stops, finish)\n * - `incidents`: Traffic incidents on routes\n * - `ferries`: Ferry sections\n * - `chargingStops`: EV charging stations\n * - `tollRoads`: Toll road sections\n * - `tunnels`: Tunnel sections\n * - `vehicleRestricted`: Vehicle-restricted sections\n * - `instructionLines`: Turn-by-turn instruction lines\n * - `instructionArrows`: Instruction arrow markers\n * - `summaryBubbles`: Route summary popups\n *\n * @example\n * ```typescript\n * const shown = routingModule.getShown();\n * console.log(`Showing ${shown.mainLines.features.length} routes`);\n * console.log(`Showing ${shown.waypoints.features.length} waypoints`);\n * console.log(`Showing ${shown.chargingStops.features.length} charging stops`);\n * ```\n *\n * @example\n * Check if any routes are displayed:\n * ```typescript\n * const shown = routingModule.getShown();\n * if (shown.mainLines.features.length > 0) {\n * console.log('Routes are displayed');\n * } else {\n * console.log('No routes displayed');\n * }\n * ```\n */\n getShown() {\n return {\n mainLines: this.sourcesWithLayers.mainLines.shownFeatures,\n waypoints: this.sourcesWithLayers.waypoints.shownFeatures,\n incidents: this.sourcesWithLayers.incidents.shownFeatures,\n ferries: this.sourcesWithLayers.ferries.shownFeatures,\n chargingStops: this.sourcesWithLayers.chargingStops.shownFeatures,\n tollRoads: this.sourcesWithLayers.tollRoads.shownFeatures,\n tunnels: this.sourcesWithLayers.tunnels.shownFeatures,\n vehicleRestricted: this.sourcesWithLayers.vehicleRestricted.shownFeatures,\n instructionLines: this.sourcesWithLayers.instructionLines.shownFeatures,\n instructionArrows: this.sourcesWithLayers.instructionArrows.shownFeatures,\n summaryBubbles: this.sourcesWithLayers.summaryBubbles.shownFeatures,\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return {\n mainLines: new EventsModule<Route<DisplayRouteProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.mainLines,\n this.config?.events,\n ),\n waypoints: new EventsModule<Waypoint<WaypointDisplayProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.waypoints,\n this.config?.events,\n ),\n chargingStops: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.chargingStops,\n this.config?.events,\n ),\n summaryBubbles: new EventsModule<DisplayRouteSummary>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.summaryBubbles,\n this.config?.events,\n ),\n incidents: new EventsModule<RouteSections<DisplayTrafficSectionProps>>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.incidents,\n this.config?.events,\n ),\n vehicleRestricted: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.vehicleRestricted,\n this.config?.events,\n ),\n ferries: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.ferries,\n this.config?.events,\n ),\n tollRoads: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.tollRoads,\n this.config?.events,\n ),\n tunnels: new EventsModule<RouteSection>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.tunnels,\n this.config?.events,\n ),\n instructionLines: new EventsModule<DisplayInstruction>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.instructionLines,\n this.config?.events,\n ),\n };\n }\n\n /**\n * Returns the map style layer under which route lines are rendered.\n * * Useful if you want to render extra layers just above the route ones but not on top of everything else.\n * * It might differ depending on the loaded style/version.\n */\n getLayerToRenderLinesUnder(): string {\n return mapStyleLayerIDs.lowestLabel;\n }\n}\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path d=\\\"M63.734 86.383c-1.404.026-2.78-.393-3.93-1.197s-2.01-1.949-2.46-3.271l-3.15-9.339c-1.093-3.403-3.748-6.089-7.154-7.238L37.5 62.21c-2.742-.902-4.567-3.477-4.5-6.345-.058-2.874 1.761-5.457 4.5-6.388l36-11.886c2.461-.88 5.213-.27 7.062 1.567s2.466 4.571 1.578 7.015L70.17 81.918c-.938 2.719-3.54 4.526-6.436 4.469h0z\\\"\\n fill=\\\"#f4f5f6\\\"/>\\n</svg>\\n\"","export default \"<svg width=\\\"32\\\" height=\\\"32\\\" viewBox=\\\"0 0 32 32\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n <circle id=\\\"circle\\\" cx=\\\"16\\\" cy=\\\"16\\\" r=\\\"12\\\" fill=\\\"white\\\" stroke=\\\"#105287\\\" stroke-width=\\\"4\\\"/>\\n</svg>\\n\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"120\\\" height=\\\"140\\\">\\n <path d=\\\"M33 83.404a2.99 2.99 0 0 1-3-2.978V44.681a2.99 2.99 0 1 1 6 0v35.745a2.99 2.99 0 0 1-3 2.978zm48-38.723H39v17.872h42V44.681zm3.878 37.851a3.02 3.02 0 0 0 4.244 0c.561-.559.878-1.317.878-2.106V44.681a2.99 2.99 0 1 0-6 0v35.745c0 .789.317 1.547.878 2.106z\\\"\\n fill=\\\"#f4f5f6\\\"/>\\n</svg>\\n\"","import { generateId, Routes } from '@tomtom-org/maps-sdk/core';\nimport { bearing } from '@turf/turf';\nimport type { DisplayRouteProps } from '../types/displayRoutes';\nimport type {\n DisplayInstruction,\n DisplayInstructionArrow,\n DisplayInstructionArrows,\n DisplayInstructions,\n} from '../types/guidance';\n\n/**\n * @ignore\n */\nexport const toDisplayInstructions = (routes: Routes<DisplayRouteProps>): DisplayInstructions => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap(\n (route, routeIndex) =>\n route.properties.guidance?.instructions\n ?.filter((instruction) => instruction.routePath?.length)\n .map(\n (instruction): DisplayInstruction => ({\n type: 'Feature',\n geometry: {\n type: 'LineString',\n coordinates: instruction.routePath.map((pathPoint) => pathPoint.point),\n },\n properties: {\n ...instruction,\n id: generateId(),\n routeIndex,\n routeState: route.properties.routeState,\n },\n }),\n ) || [],\n ),\n});\n\n/**\n * @ignore\n */\nexport const toDisplayInstructionArrows = (routes: Routes<DisplayRouteProps>): DisplayInstructionArrows => ({\n type: 'FeatureCollection',\n features: routes.features.flatMap(\n (route, routeIndex) =>\n route.properties.guidance?.instructions\n ?.filter((instruction) => instruction.routePath?.length && instruction.routePath.length > 1)\n .map((instruction): DisplayInstructionArrow => {\n const instructionLastSegment = [\n instruction.routePath[instruction.routePath.length - 2]?.point,\n instruction.routePath[instruction.routePath.length - 1]?.point,\n ];\n\n return {\n type: 'Feature',\n geometry: { type: 'Point', coordinates: instructionLastSegment[1] },\n properties: {\n ...instruction,\n id: generateId(),\n routeIndex,\n routeState: route.properties.routeState,\n lastPointBearingDegrees: bearing(instructionLastSegment[0], instructionLastSegment[1]),\n },\n };\n }) || [],\n ),\n});\n","import type { StyleSpecification } from 'maplibre-gl';\nimport type { InternalTomTomMapParams, StandardStyle, StandardStyleID, StyleInput, StyleModule } from './types/mapInit';\nimport { styleModules } from './types/mapInit';\n\nexport const DEFAULT_STANDARD_STYLE_ID: StandardStyleID = 'standardLight';\nconst URL_PREFIX = '${baseURL}/maps/orbis/assets/styles/${version}/style.json?&apiVersion=1&key=${apiKey}';\n\nconst standardStyleModulesValues: Record<StandardStyleID, Record<StyleModule, string>> = {\n standardLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_light',\n },\n standardDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_dark',\n },\n drivingLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_light',\n },\n drivingDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_dark',\n },\n monoLight: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_mono-light',\n },\n monoDark: {\n trafficIncidents: 'incidents_dark',\n trafficFlow: 'flow_relative-dark',\n hillshade: 'hillshade_mono-dark',\n },\n satellite: {\n trafficIncidents: 'incidents_light',\n trafficFlow: 'flow_relative-light',\n hillshade: 'hillshade_satellite',\n },\n};\n\nconst baseMapStyleUrlTemplate = (suffix: string): string => `${URL_PREFIX}&map=${suffix}`;\n\nconst baseMapStyleUrlTemplates: Record<StandardStyleID, string> = {\n standardLight: baseMapStyleUrlTemplate('basic_street-light'),\n standardDark: baseMapStyleUrlTemplate('basic_street-dark'),\n drivingLight: baseMapStyleUrlTemplate('basic_street-light-driving'),\n drivingDark: baseMapStyleUrlTemplate('basic_street-dark-driving'),\n monoLight: baseMapStyleUrlTemplate('basic_mono-light'),\n monoDark: baseMapStyleUrlTemplate('basic_mono-dark'),\n satellite: baseMapStyleUrlTemplate('basic_street-satellite'),\n};\n\nconst buildStandardStyleUrl = (standardStyle: StandardStyle, baseUrl: string, apiKey: string): string => {\n const standardStyleID = standardStyle.id ?? DEFAULT_STANDARD_STYLE_ID;\n\n const styleURL = new URL(\n baseMapStyleUrlTemplates[standardStyleID]\n .replace('${baseURL}', baseUrl)\n .replace('${version}', standardStyle.version ?? '0.6.0-0')\n .replace('${apiKey}', apiKey),\n );\n\n for (const module of standardStyle.include ?? styleModules) {\n styleURL.searchParams.append(module, standardStyleModulesValues[standardStyleID][module]);\n }\n\n return styleURL.toString();\n};\n\nconst withApiKey = (givenUrl: string, apiKey: string): string => {\n const url = new URL(givenUrl);\n if (!url.searchParams.has('key')) {\n url.searchParams.set('key', apiKey);\n } else {\n console.warn(\n 'The style URL is coming with an API key parameter which takes priority. ' +\n 'If you want to use the SDK configured API key, remove the key param from the style URL',\n );\n }\n return url.toString();\n};\n\n/**\n * @ignore\n * @param mapParams The SDK parameters to convert to input renderer style.\n * @return The map style to load into the renderer.\n */\nexport const buildStyleInput = (mapParams: InternalTomTomMapParams): StyleSpecification | string => {\n const style = mapParams.style;\n const baseUrl = mapParams.commonBaseURL;\n const apiKey = mapParams.apiKey;\n\n if (typeof style === 'string') {\n return buildStandardStyleUrl({ id: style }, baseUrl, apiKey);\n } else if (style?.type === 'standard') {\n return buildStandardStyleUrl(style, baseUrl, apiKey);\n } else if (style?.type === 'custom' && style?.url) {\n return withApiKey(style.url, apiKey);\n } else if (style?.type === 'custom' && style?.json) {\n return style.json;\n }\n\n // no style defined, use default\n return buildStandardStyleUrl({ id: DEFAULT_STANDARD_STYLE_ID }, baseUrl, apiKey);\n};\n\n/**\n * Includes the previous standard style parts into the given standard style if the new one didn't define any.\n * * Both new and previous styles must be of \"standard\" type.\n * @ignore\n */\nexport const withPreviousStyleParts = (style: StyleInput, previousStyle?: StyleInput): StyleInput => {\n if (\n previousStyle &&\n typeof previousStyle === 'object' &&\n previousStyle.type === 'standard' &&\n previousStyle.include\n ) {\n if (typeof style === 'string' || (style.type === 'standard' && !style.include)) {\n return {\n type: 'standard',\n id: typeof style === 'string' ? style : style.id,\n include: (previousStyle as StandardStyle).include,\n };\n }\n }\n return style;\n};\n","import { type BBox, type Language, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { isEqual } from 'lodash-es';\nimport { getRTLTextPluginStatus, Map, setRTLTextPlugin, setWorkerCount } from 'maplibre-gl';\nimport { version as maplibreVersion } from 'maplibre-gl/package.json';\nimport type { InternalTomTomMapParams, MapLibreOptions, StyleInput, TomTomMapParams } from './init';\nimport { buildMapOptions } from './init/buildMapOptions';\nimport { buildStyleInput, DEFAULT_STANDARD_STYLE_ID, withPreviousStyleParts } from './init/styleInputBuilder';\nimport {\n EventsProxy,\n filterLayersBySources,\n HILLSHADE_SOURCE_ID,\n LightDark,\n TRAFFIC_FLOW_SOURCE_ID,\n TRAFFIC_INCIDENTS_SOURCE_ID,\n} from './shared';\nimport { isLayerLocalizable } from './shared/localization';\nimport { addPinCategoriesSpriteToStyle, getStyleLightDarkTheme } from './shared/mapUtils';\n\n/**\n * Handler interface for responding to map style changes.\n *\n * @remarks\n * This interface defines callbacks that are invoked when the map style changes via {@link TomTomMap.setStyle}.\n * Use this to perform cleanup or reinitialization of custom map features when styles are switched.\n *\n * **Lifecycle:**\n * 1. `onStyleAboutToChange` - Called before the new style is applied\n * 2. Style change occurs\n * 3. `onStyleChanged` - Called after the new style has been fully loaded\n *\n * **Common Use Cases:**\n * - Saving and restoring custom layers or sources\n * - Reinitializing map modules after style changes\n * - Updating UI components based on the new style\n * - Cleaning up resources tied to the previous style\n *\n * @example\n * ```typescript\n * const styleHandler: StyleChangeHandler = {\n * onStyleAboutToChange: () => {\n * console.log('Style changing - saving state...');\n * // Save custom layer data\n * },\n * onStyleChanged: () => {\n * console.log('Style changed - restoring state...');\n * // Restore custom layers\n * }\n * };\n *\n * map.addStyleChangeHandler(styleHandler);\n * ```\n *\n * @see {@link TomTomMap.addStyleChangeHandler}\n * @see {@link TomTomMap.setStyle}\n *\n * @group Map Style\n */\nexport type StyleChangeHandler = {\n /**\n * Callback invoked immediately before a style change begins.\n *\n * @remarks\n * Use this to perform cleanup or save state before the current style is removed.\n * This method can be synchronous or asynchronous.\n *\n * @returns void or a Promise that resolves when preparation is complete\n */\n onStyleAboutToChange?: () => void | Promise<void>;\n /**\n * Callback invoked after a new style has been fully loaded.\n *\n * @remarks\n * Use this to restore state, reinitialize layers, or perform other setup\n * that depends on the new style being ready. This method can be synchronous or asynchronous.\n *\n * @returns void or a Promise that resolves when reinitialization is complete\n */\n onStyleChanged?: () => void | Promise<void>;\n};\n\n/**\n * Main TomTom Map class for displaying interactive maps in web applications.\n *\n * This is the entry point for rendering TomTom maps. It wraps MapLibre GL JS and provides\n * a simplified, enhanced API for common mapping tasks.\n *\n * @remarks\n * **Key Features:**\n * - Built on MapLibre GL JS for high-performance rendering\n * - Seamless style switching without map reload\n * - Integrated event handling system\n * - Multi-language support with dynamic switching\n * - Compatible with TomTom map modules (traffic, POIs, routing, etc.)\n *\n * **Architecture:**\n * - Exposes the underlying MapLibre Map instance via {@link mapLibreMap}\n * - Manages map lifecycle and style transitions\n * - Coordinates with map modules for data visualization\n *\n * @example\n * Basic map initialization:\n * ```typescript\n * import { TomTomMap } from '@tomtom-international/maps-sdk-js/map';\n *\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: 'standardLight',\n * mapLibre: {\n * container: 'map',\n * center: [4.9041, 52.3676],\n * zoom: 10\n * }\n * });\n * ```\n *\n * @example\n * With modules and configuration:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: {\n * type: 'standard',\n * id: 'standardDark',\n * include: ['trafficFlow', 'trafficIncidents']\n * },\n * language: 'en-US',\n * events: {\n * precisionMode: 'point-then-box',\n * cursorOnHover: 'pointer'\n * },\n * mapLibre: {\n * container: 'map',\n * center: [-74.006, 40.7128],\n * zoom: 12\n * }\n * });\n *\n * // Access MapLibre functionality directly\n * map.mapLibreMap.on('load', () => {\n * console.log('Map loaded');\n * });\n * ```\n *\n * @group Map\n */\nexport class TomTomMap {\n /**\n * Indicates whether the map style has been fully loaded and is ready for interaction.\n *\n * @remarks\n * - `true` when the style is loaded and modules can be safely initialized\n * - `false` during map construction or style changes\n * - Check this before performing style-dependent operations\n *\n * @example\n * ```typescript\n * if (map.mapReady) {\n * // Safe to initialize modules\n * const trafficFlowModule = await TrafficFlowModule.get(map);\n * }\n * ```\n */\n mapReady = false;\n\n /**\n * The underlying MapLibre GL JS Map instance.\n *\n * @remarks\n * **When to Use:**\n * - Access advanced MapLibre functionality not exposed by TomTomMap\n * - Add custom layers, sources, or controls\n * - Listen to MapLibre-specific events\n * - Integrate third-party MapLibre plugins\n *\n * **Important:**\n * - Available immediately after TomTomMap construction\n * - Direct modifications may affect SDK module behavior\n * - Coordinate with SDK modules to avoid conflicts\n *\n * @example\n * Add custom layer:\n * ```typescript\n * map.mapLibreMap.addLayer({\n * id: 'custom-layer',\n * type: 'circle',\n * source: 'my-data',\n * paint: {\n * 'circle-radius': 6,\n * 'circle-color': '#ff0000'\n * }\n * });\n * ```\n *\n * @example\n * Listen to events:\n * ```typescript\n * map.mapLibreMap.on('moveend', () => {\n * console.log('Camera position:', map.mapLibreMap.getCenter());\n * });\n * ```\n *\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/map/ | MapLibre Map Documentation}\n */\n readonly mapLibreMap: Map;\n /**\n * @ignore\n */\n readonly _eventsProxy: EventsProxy;\n /**\n * @ignore\n */\n _params: InternalTomTomMapParams;\n\n styleLightDarkTheme: LightDark;\n\n private readonly styleChangeHandlers: StyleChangeHandler[] = [];\n\n /**\n * Constructs a new TomTom Map instance and attaches it to a DOM element.\n *\n * @param mapParams - Combined TomTom and MapLibre parameters for map initialization.\n * Includes API key, style, events, and MapLibre options like container, center, zoom, etc.\n * See {@link TomTomMapParams} for all available parameters.\n *\n * @remarks\n * **Initialization Process:**\n * 1. Merges `mapParams` with global configuration\n * 2. Creates underlying MapLibre map instance\n * 3. Loads specified style asynchronously\n * 4. Sets `mapReady` to `true` when complete\n *\n * **Configuration Priority:**\n * - Parameters passed here override global configuration\n * - Allows per-map customization while sharing common settings\n *\n * @example\n * Minimal initialization:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * mapLibre: {\n * container: 'map',\n * center: [0, 0],\n * zoom: 2\n * }\n * });\n * ```\n *\n * @example\n * Full configuration:\n * ```typescript\n * const map = new TomTomMap({\n * key: 'YOUR_API_KEY',\n * style: {\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * },\n * language: 'en-US',\n * events: {\n * precisionMode: 'point-then-box',\n * paddingBoxPx: 10\n * },\n * mapLibre: {\n * container: 'map',\n * center: [-122.4194, 37.7749],\n * zoom: 13,\n * pitch: 45,\n * bearing: -17.6,\n * antialias: true,\n * maxZoom: 18,\n * minZoom: 8\n * }\n * });\n * ```\n *\n * @throws Will log errors if RTL text plugin fails to load (non-blocking)\n *\n * @see {@link MapLibreOptions}\n * @see {@link TomTomMapParams}\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/map/ | MapLibre Map Parameters}\n * @see [Map Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/quickstart)\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n * @see [User Interaction Events Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/user-events)\n */\n constructor(mapParams: TomTomMapParams) {\n this._params = mergeFromGlobal(mapParams);\n if (this._params.style === undefined) {\n this._params = { ...this._params, style: DEFAULT_STANDARD_STYLE_ID };\n }\n this.styleLightDarkTheme = getStyleLightDarkTheme(this._params.style);\n this.ensureMapLibreCSSLoaded();\n\n // Set worker count before creating the Map instance.\n // MapLibre defaults to 1 worker (3 on Safari). 4 workers gives smooth\n // tile-by-tile transitions during style changes without starving the main thread.\n setWorkerCount(4);\n\n this.mapLibreMap = new Map(buildMapOptions(this._params));\n this.mapLibreMap.once('styledata', () => {\n this.handleStyleData(false);\n });\n this._eventsProxy = new EventsProxy(this.mapLibreMap, this._params?.events);\n\n this.loadRTLTextPlugin();\n }\n\n private loadRTLTextPlugin(): void {\n // deferred (just in case), lazy loading of the RTL plugin:\n setTimeout(() => {\n if (!['deferred', 'loaded'].includes(getRTLTextPluginStatus())) {\n setRTLTextPlugin(\n 'https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.3.0/dist/mapbox-gl-rtl-text.js',\n true,\n ).catch((error) => console.error('Something went wrong when setting RTL plugin', error));\n }\n });\n }\n\n /**\n * Dynamically loads the MapLibre CSS stylesheet from CDN.\n */\n private ensureMapLibreCSSLoaded(): void {\n if (typeof document === 'undefined') {\n return;\n }\n // Check if the CSS is already loaded to avoid duplicates:\n const existingLink = Array.from(document.querySelectorAll('link[rel=\"stylesheet\"], style')).some((element) =>\n element.textContent?.includes('.maplibregl-map'),\n );\n if (existingLink) {\n return;\n }\n\n // Create and inject a link tag to load CSS from unpkg CDN\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = `https://unpkg.com/maplibre-gl@${maplibreVersion}/dist/maplibre-gl.css`;\n document.head.appendChild(link);\n }\n\n /**\n * Changes the map style dynamically without reloading the entire map.\n *\n * @param style - The new style to apply. Can be a string ID or a detailed style configuration.\n * @param options - Configuration options for the style change behavior.\n * @param options.keepState - Whether to preserve SDK-rendered items and configurations when changing styles.\n * When `true` (default), maintains traffic layers, routes, markers, and other SDK features.\n * When `false`, performs a clean style switch without preserving previous state.\n *\n * @remarks\n * **Behavior:**\n * - Temporarily sets {@link mapReady} to `false` during the transition\n * - Triggers all registered {@link StyleChangeHandler} callbacks\n * - Resets {@link mapReady} to `true` when the new style is fully loaded\n *\n * **State Preservation (keepState: true):**\n * - Merges style parts from the previous style with the new one\n * - Maintains SDK module layers (traffic, routes, POIs, etc.)\n * - Preserves language settings\n *\n * **Clean Switch (keepState: false):**\n * - Applies the new style without merging previous configuration\n * - Removes all SDK module layers\n * - Useful for complete style resets\n *\n * @example\n * Simple style change:\n * ```typescript\n * // Switch to dark mode\n * map.setStyle('standardDark');\n * ```\n *\n * @example\n * Style change with detailed configuration:\n * ```typescript\n * map.setStyle({\n * type: 'standard',\n * id: 'standardLight',\n * include: ['trafficFlow', 'hillshade']\n * });\n * ```\n *\n * @example\n * Clean style switch without state preservation:\n * ```typescript\n * // Complete reset - removes all SDK layers and modules\n * map.setStyle('standardDark', { keepState: false });\n * ```\n *\n * @example\n * With style change handlers:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * console.log('Preparing for style change...');\n * },\n * onStyleChanged: () => {\n * console.log('New style applied!');\n * }\n * });\n *\n * map.setStyle('standardDark');\n * ```\n *\n * @see {@link TomTomMapParams.style} - For setting style during initialization\n * @see {@link StyleChangeHandler} - For handling style change events\n * @see {@link getStyle} - For retrieving the current style\n * @see [Map Styles Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/map-styles)\n */\n setStyle = (style: StyleInput, options: { keepState?: boolean } = { keepState: true }): void => {\n this.mapReady = false;\n\n // Notify all modules that the style is about to change (they mark themselves as not ready).\n for (const handler of this.styleChangeHandlers) {\n try {\n handler.onStyleAboutToChange?.();\n } catch (e) {\n console.error(e);\n }\n }\n const effectiveStyle = options.keepState ? withPreviousStyleParts(style, this._params.style) : style;\n this._params = { ...this._params, style: effectiveStyle };\n this.styleLightDarkTheme = getStyleLightDarkTheme(effectiveStyle);\n this.mapLibreMap.once('styledata', () => {\n // We only handle the style data change if the applied style is still the same as the one we set,\n // to prevent race conditions when handling stale styles applied quickly in succession.\n // (If the current style parameters are different, there's likely a new style being set, which will trigger the handler soon after)\n if (!this.mapReady && isEqual(effectiveStyle, this._params.style)) {\n this.handleStyleData(options.keepState || true);\n }\n });\n this.mapLibreMap.setStyle(buildStyleInput(this._params), { validate: false });\n };\n\n /**\n * Retrieves the current style configuration of the map.\n *\n * @returns The current {@link StyleInput} configuration, or `undefined` if no style is set.\n *\n * @remarks\n * Returns the style configuration as it was set, not the fully resolved MapLibre style object.\n * Use this to inspect or store the current style configuration for later restoration.\n *\n * **Return Value:**\n * - String ID (e.g., `'standardLight'`) for simple style configurations\n * - Style object with `type`, `id`, and optional `include` properties for detailed configurations\n * - `undefined` if no style has been explicitly set\n *\n * @example\n * ```typescript\n * const currentStyle = map.getStyle();\n * console.log('Current style:', currentStyle);\n *\n * // Save style for later\n * const savedStyle = map.getStyle();\n *\n * // Later, restore it\n * if (savedStyle) {\n * map.setStyle(savedStyle);\n * }\n * ```\n *\n * @example\n * Conditional logic based on current style:\n * ```typescript\n * const style = map.getStyle();\n * if (typeof style === 'string' && style.includes('Dark')) {\n * console.log('Dark mode is active');\n * }\n * ```\n *\n * @see {@link setStyle} - For changing the map style\n * @see {@link StyleInput} - For available style configuration options\n */\n getStyle = (): StyleInput | undefined => {\n return this._params.style;\n };\n\n private _setLanguage(language: Language) {\n this._params = { ...this._params, language };\n const mapLanguage = language?.includes('-') ? language.split('-')[0] : language;\n this.mapLibreMap.getStyle().layers.forEach((layer) => {\n if (layer.type === 'symbol' && isLayerLocalizable(layer)) {\n const textFieldValue = mapLanguage\n ? ['coalesce', ['get', `name_${mapLanguage}`], ['get', 'name']]\n : ['get', 'name'];\n this.mapLibreMap.setLayoutProperty(layer.id, 'text-field', textFieldValue, { validate: false });\n }\n });\n }\n\n /**\n * Changes the language of the map.\n * * You can use this method to change the language at runtime.\n * * To set the language upon initialization, you can better do it via {@link core!TomTomConfig global config}\n * or {@link TomTomMapParams}.\n * @param language The language to be used in map translations.\n *\n * @remarks\n * **Behavior:**\n * - Updates all localizable map labels to the specified language\n * - Falls back to the default label name if the requested language is unavailable\n * - Can be called before or after the map is fully loaded\n * - If called before map is ready, will apply once the style loads\n *\n * **Language Format:**\n * - Simple language codes: `'en'`, `'fr'`, `'de'`, `'ja'`, `'zh'`\n * - Locale-specific codes: `'en-US'`, `'en-GB'`, `'zh-CN'`, `'pt-BR'`\n * - When using locale codes (with `-`), only the language portion is used for labels\n *\n * **Persistence:**\n * - Language setting persists across style changes (when `keepState: true`)\n * - Set during initialization via {@link TomTomMapParams.language} for immediate application\n *\n * @example\n * Change language at runtime:\n * ```typescript\n * // Switch to French\n * map.setLanguage('fr');\n * ```\n *\n * @example\n * Use locale-specific codes:\n * ```typescript\n * // Use Simplified Chinese\n * map.setLanguage('zh-CN');\n *\n * // Use Brazilian Portuguese\n * map.setLanguage('pt-BR');\n * ```\n *\n * @example\n * Language switcher UI:\n * ```typescript\n * const languageSelector = document.getElementById('lang-select');\n * languageSelector.addEventListener('change', (e) => {\n * map.setLanguage(e.target.value);\n * });\n * ```\n *\n * @see {@link TomTomMapParams.language} - For setting language during initialization\n * @see {@link https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes | ISO 639-1 Language Codes}\n */\n setLanguage(language: Language) {\n if (this.mapReady) {\n this._setLanguage(language);\n } else {\n this.mapLibreMap.once('styledata', () => this.setLanguage(language));\n }\n }\n\n /**\n * Retrieves the current visible map area as a GeoJSON bounding box.\n *\n * @returns A {@link https://tools.ietf.org/html/rfc7946#section-5 | GeoJSON BBox} array\n * in the format `[west, south, east, north]` representing the map's current viewport bounds.\n *\n * @remarks\n * **Return Format:**\n * - Array of four numbers: `[minLongitude, minLatitude, maxLongitude, maxLatitude]`\n * - Coordinates are in WGS84 decimal degrees\n * - West/East values range from -180 to 180\n * - South/North values range from -90 to 90\n *\n * @example\n * Get current bounds:\n * ```typescript\n * const bbox = map.getBBox();\n * console.log('Bounds:', bbox);\n * // Output: [-122.5, 37.7, -122.3, 37.8]\n * // [west, south, east, north]\n * ```\n *\n * @example\n * Use bounds for spatial query:\n * ```typescript\n * const bbox = map.getBBox();\n * const results = await searchAPI.searchInBoundingBox({\n * bbox: bbox,\n * query: 'restaurants'\n * });\n * ```\n *\n * @example\n * Save and restore map view:\n * ```typescript\n * // Save current view\n * const savedBounds = map.getBBox();\n * const savedZoom = map.mapLibreMap.getZoom();\n *\n * // Later, restore the view\n * const [west, south, east, north] = savedBounds;\n * map.mapLibreMap.fitBounds([[west, south], [east, north]]);\n * ```\n *\n * @see {@link https://tools.ietf.org/html/rfc7946#section-5 | GeoJSON BBox Specification}\n * @see {@link https://maplibre.org/maplibre-gl-js-docs/api/geography/#lnglatbounds | MapLibre LngLatBounds}\n */\n getBBox(): BBox {\n return this.mapLibreMap.getBounds().toArray().flat() as BBox;\n }\n\n private handleStyleData(keepState: boolean) {\n // We ensure to make traffic and hillshade hidden by default (even if right after the modules bring it back to visible state)\n // This way we ensure such layers are invisible even of their related SDK modules are not used.\n for (const layer of filterLayersBySources(this.mapLibreMap, [\n TRAFFIC_INCIDENTS_SOURCE_ID,\n TRAFFIC_FLOW_SOURCE_ID,\n HILLSHADE_SOURCE_ID,\n ])) {\n this.mapLibreMap.setLayoutProperty(layer.id, 'visibility', 'none', { validate: false });\n }\n\n // For most use cases we'll need to have pins available (places, routing...) so we add them by default:\n // (subsequent loads for the same sprite should be cached)\n addPinCategoriesSpriteToStyle(this._params, this.mapLibreMap);\n // We restore the language if it was set before:\n this._params.language && this._setLanguage(this._params.language);\n\n this.mapReady = true;\n if (keepState) {\n for (const handler of this.styleChangeHandlers) {\n try {\n handler.onStyleChanged?.();\n } catch (e) {\n console.error(e);\n }\n }\n }\n }\n\n /**\n * Registers a handler to be notified when the map style changes.\n *\n * @param handler - A {@link StyleChangeHandler} object with callbacks for style change events.\n *\n * @remarks\n * **When to Use:**\n * - You have custom layers or sources that need to be recreated after style changes\n * - Your application needs to respond to style switches (e.g., light/dark mode transitions)\n * - You need to save and restore state during style changes\n * - Map modules need to reinitialize when styles change\n *\n * **Handler Lifecycle:**\n * 1. `onStyleAboutToChange()` - Called before the style change begins\n * 2. Style change occurs\n * 3. `onStyleChanged()` - Called after the new style has been fully loaded\n *\n * **Multiple Handlers:**\n * - Multiple handlers can be registered and will all be called in registration order\n * - Each handler's errors are caught independently and logged to the console\n * - One failing handler won't prevent others from executing\n *\n * **Important Notes:**\n * - Handlers are only triggered by {@link setStyle} calls, not initial map construction\n * - Only called when `keepState: true` in {@link setStyle} options\n * - Handlers persist for the lifetime of the TomTomMap instance\n *\n * @example\n * Basic usage:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * console.log('Style is changing...');\n * },\n * onStyleChanged: () => {\n * console.log('Style changed successfully!');\n * }\n * });\n *\n * // Later trigger the handlers\n * map.setStyle('standardDark');\n * ```\n *\n * @example\n * Preserve custom layers across style changes:\n * ```typescript\n * let customLayerData = null;\n *\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * // Save custom layer data before style changes\n * if (map.mapLibreMap.getLayer('my-custom-layer')) {\n * customLayerData = map.mapLibreMap.getSource('my-data')._data;\n * map.mapLibreMap.removeLayer('my-custom-layer');\n * map.mapLibreMap.removeSource('my-data');\n * }\n * },\n * onStyleChanged: () => {\n * // Restore custom layer after new style is loaded\n * if (customLayerData) {\n * map.mapLibreMap.addSource('my-data', {\n * type: 'geojson',\n * data: customLayerData\n * });\n * map.mapLibreMap.addLayer({\n * id: 'my-custom-layer',\n * type: 'circle',\n * source: 'my-data',\n * paint: { 'circle-radius': 6, 'circle-color': '#007cbf' }\n * });\n * }\n * }\n * });\n * ```\n *\n * @example\n * Async handler for external API calls:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: async () => {\n * await saveStateToAPI(map.getStyle());\n * },\n * onStyleChanged: async () => {\n * await loadStateFromAPI();\n * }\n * });\n * ```\n *\n * @example\n * Update UI based on style:\n * ```typescript\n * map.addStyleChangeHandler({\n * onStyleAboutToChange: () => {\n * document.body.classList.add('style-changing');\n * },\n * onStyleChanged: () => {\n * document.body.classList.remove('style-changing');\n * const style = map.getStyle();\n * if (typeof style === 'string' && style.includes('Dark')) {\n * document.body.classList.add('dark-mode');\n * } else {\n * document.body.classList.remove('dark-mode');\n * }\n * }\n * });\n * ```\n *\n * @see {@link StyleChangeHandler} - Handler interface definition\n * @see {@link setStyle} - Method that triggers the handlers\n */\n addStyleChangeHandler(handler: StyleChangeHandler): void {\n this.styleChangeHandlers.push(handler);\n }\n}\n","import type { MapOptions } from 'maplibre-gl';\nimport { transformRequest } from '../shared/mapUtils';\nimport { buildStyleInput } from './styleInputBuilder';\nimport type { InternalTomTomMapParams } from './types/mapInit';\n\n/**\n * @ignore\n * @param tomtomMapParams\n */\nexport const buildMapOptions = (tomtomMapParams: InternalTomTomMapParams): MapOptions => {\n return {\n // defaults (can be overwritten by given options)\n validateStyle: false,\n maxTileCacheZoomLevels: 22,\n cancelPendingTileRequestsWhileZooming: false,\n // given options:\n ...tomtomMapParams.mapLibre,\n // SDK overrides (won't have any effect via given options):\n style: buildStyleInput(tomtomMapParams),\n attributionControl: { compact: false },\n transformRequest: transformRequest(tomtomMapParams),\n };\n};\n","import type { ExpressionSpecification, SymbolLayerSpecification } from 'maplibre-gl';\n\n/**\n * @ignore\n */\nexport const isLayerLocalizable = (layer: SymbolLayerSpecification): boolean => {\n const textField = (layer.layout?.['text-field'] ?? '') as string | ExpressionSpecification;\n return textField\n ? // tries to detect layers which have a \"text-field\" that can be localized\n // ex. \"text-field\": \"{name}\" or \"text-field\": [\"get\", \"name\"]\n textField === '{name}' ||\n (textField.length === 2 && textField[1] === 'name') ||\n // tries to detect layers which have \"text-field\" that was localized already\n // ex. \"text-field\": [\"coalesce\", [\"get\", \"name_en\"], [\"get\", \"name\"]]\n (textField.length === 3 &&\n Array.isArray(textField[1]) &&\n typeof textField[1][1] === 'string' &&\n textField[1][1].includes('name_') &&\n Array.isArray(textField[2]) &&\n textField[2].includes('name'))\n : false;\n};\n","import { indexedMagnitudes } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type {\n ExpressionFilterSpecification,\n FilterSpecification,\n LayerSpecification,\n LegacyFilterSpecification,\n Map,\n} from 'maplibre-gl';\nimport type { MultiSyntaxFilter, ValuesFilter } from '../../shared';\nimport { buildValuesFilter, getMergedAllFilter, getMergedAnyFilter } from '../../shared/mapLibreFilterUtils';\nimport type { TrafficCommonFilter } from '../types/trafficCommonConfig';\nimport type { TrafficFlowFilter, TrafficFlowFilters } from '../types/trafficFlowConfig';\nimport type { DelayFilter, TrafficIncidentsFilter, TrafficIncidentsFilters } from '../types/trafficIncidentsConfig';\nimport { incidentToIconCategoryMapping } from '../util/trafficIncidentMapping';\n\nconst toMultiSyntaxAllFilter = (\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n): MultiSyntaxFilter | null => {\n if (!newSyntaxExpressions.length) {\n return null;\n }\n if (newSyntaxExpressions.length === 1) {\n return {\n expression: newSyntaxExpressions[0] as ExpressionFilterSpecification,\n legacy: legacySyntaxExpressions[0] as LegacyFilterSpecification,\n };\n }\n return {\n expression: ['all', ...newSyntaxExpressions] as ExpressionFilterSpecification,\n legacy: ['all', ...legacySyntaxExpressions] as LegacyFilterSpecification,\n };\n};\n\nconst delayFilterToMapLibre = (delayFilter: DelayFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions = [];\n const legacySyntaxExpressions = [];\n if (delayFilter.mustHaveDelay && delayFilter.minDelayMinutes) {\n // there must be a delay and with the min specified value:\n const delaySeconds = delayFilter.minDelayMinutes * 60;\n newSyntaxExpressions.push(['>=', ['get', 'delay'], delaySeconds]);\n legacySyntaxExpressions.push(['>=', 'delay', delaySeconds]);\n } else if (delayFilter.mustHaveDelay) {\n // just expects a delay of any kind\n newSyntaxExpressions.push(['>', ['get', 'delay'], 0]);\n legacySyntaxExpressions.push(['>', 'delay', 0]);\n } else if (delayFilter.minDelayMinutes) {\n // Min delay expected, but also allows for non-existing delays:\n const delaySeconds = delayFilter.minDelayMinutes * 60;\n newSyntaxExpressions.push([\n 'any',\n ['!', ['has', 'delay']],\n ['==', ['get', 'delay'], 0],\n ['>=', ['get', 'delay'], delaySeconds],\n ]);\n legacySyntaxExpressions.push(['any', ['!has', 'delay'], ['==', 'delay', 0], ['>=', 'delay', delaySeconds]]);\n }\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\nconst addFilter = (\n filter: MultiSyntaxFilter | undefined | null,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n) => {\n if (filter) {\n newSyntaxExpressions.push(filter.expression);\n legacySyntaxExpressions.push(filter.legacy);\n }\n};\n\nconst addValuesFilter = (\n valuesFilter: ValuesFilter<string> | undefined,\n propName: string,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n) => {\n if (valuesFilter) {\n addFilter(buildValuesFilter(propName, valuesFilter), newSyntaxExpressions, legacySyntaxExpressions);\n }\n};\n\nconst addCommonFilterExpressions = (\n sdkFilter: TrafficCommonFilter,\n newSyntaxExpressions: unknown[],\n legacySyntaxExpressions: unknown[],\n): void => {\n addValuesFilter(sdkFilter.roadCategories, 'road_category', newSyntaxExpressions, legacySyntaxExpressions);\n addValuesFilter(sdkFilter.roadSubCategories, 'road_subcategory', newSyntaxExpressions, legacySyntaxExpressions);\n};\n\nconst buildMapLibreIncidentsFilter = (sdkFilter: TrafficIncidentsFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions: unknown[] = [];\n const legacySyntaxExpressions: unknown[] = [];\n\n addCommonFilterExpressions(sdkFilter, newSyntaxExpressions, legacySyntaxExpressions);\n\n if (sdkFilter.incidentCategories) {\n const incidentCategoryFilter = buildValuesFilter(\n 'icon_category_0',\n sdkFilter.incidentCategories,\n (value) => incidentToIconCategoryMapping[value],\n );\n addFilter(incidentCategoryFilter, newSyntaxExpressions, legacySyntaxExpressions);\n }\n if (sdkFilter.magnitudes) {\n const magnitudesFilter = buildValuesFilter('magnitude_of_delay', sdkFilter.magnitudes, (magnitude) =>\n indexedMagnitudes.indexOf(magnitude),\n );\n addFilter(magnitudesFilter, newSyntaxExpressions, legacySyntaxExpressions);\n }\n if (sdkFilter.delays) {\n addFilter(delayFilterToMapLibre(sdkFilter.delays), newSyntaxExpressions, legacySyntaxExpressions);\n }\n\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\n/**\n * @ignore\n */\nexport const buildMapLibreIncidentFilters = (incidentFilters: TrafficIncidentsFilters): MultiSyntaxFilter | null => {\n if (!incidentFilters?.any?.length) {\n return null;\n }\n const mapLibreFilters = incidentFilters.any\n .map(buildMapLibreIncidentsFilter)\n .filter((mapLibreFilter) => !isNil(mapLibreFilter));\n return getMergedAnyFilter(mapLibreFilters);\n};\n\nconst buildMapLibreFlowFilter = (sdkFilter: TrafficFlowFilter): MultiSyntaxFilter | null => {\n const newSyntaxExpressions: unknown[] = [];\n const legacySyntaxExpressions: unknown[] = [];\n\n addCommonFilterExpressions(sdkFilter, newSyntaxExpressions, legacySyntaxExpressions);\n if (sdkFilter.showRoadClosures) {\n const operator = sdkFilter.showRoadClosures === 'only' ? '==' : '!=';\n newSyntaxExpressions.push([operator, ['get', 'road_closure'], true]);\n legacySyntaxExpressions.push([operator, 'road_closure', true]);\n }\n\n return toMultiSyntaxAllFilter(newSyntaxExpressions, legacySyntaxExpressions);\n};\n\n/**\n * @ignore\n */\nexport const buildMapLibreFlowFilters = (flowFilters: TrafficFlowFilters): MultiSyntaxFilter | null => {\n if (!flowFilters?.any?.length) {\n return null;\n }\n const mapLibreFilters = flowFilters.any\n .map(buildMapLibreFlowFilter)\n .filter((mapLibreFilter) => !isNil(mapLibreFilter));\n return getMergedAnyFilter(mapLibreFilters);\n};\n\n/**\n * @ignore\n * @param filter\n * @param layers\n * @param mapLibreMap\n * @param originalFilters\n */\nexport const applyFilter = (\n filter: MultiSyntaxFilter | undefined,\n layers: LayerSpecification[],\n mapLibreMap: Map,\n originalFilters: Record<string, FilterSpecification | undefined>,\n) => {\n for (const layer of layers) {\n mapLibreMap.setFilter(\n layer.id,\n filter ? getMergedAllFilter(filter, originalFilters[layer.id]) : originalFilters[layer.id],\n );\n }\n};\n","import { isNil, omitBy } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport type { LayerSpecWithSource } from '../shared';\nimport {\n AbstractMapModule,\n EventsModule,\n filterLayersBySources,\n StyleSourceWithLayers,\n TRAFFIC_FLOW_SOURCE_ID,\n} from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { applyFilter, buildMapLibreFlowFilters } from './filters/trafficFilters';\nimport type { FlowConfig, TrafficFlowFilters } from './types/trafficFlowConfig';\nimport type { TrafficFlowModuleFeature } from './types/trafficFlowFeature';\nimport { trafficFlowMapping } from './util/trafficFlowMapping';\n\n/**\n * IDs of sources and layers for traffic flow module.\n */\ntype TrafficFlowSourcesWithLayers = {\n trafficFlow: StyleSourceWithLayers;\n};\n\n/**\n * Traffic Flow Module for displaying and configuring real-time traffic flow information on the map.\n *\n * This module controls the vector tile traffic flow layers that visualize current\n * traffic speed conditions using color-coded road segments.\n *\n * @remarks\n * **Features:**\n * - Toggle traffic flow visibility on/off\n * - Filter by road categories and types\n * - Color-coded speed visualization (green = free flow, red = congestion)\n * - Real-time traffic data from vector tiles\n * - Filter road closures\n *\n * **Visual Representation:**\n * - Green: Free-flowing traffic\n * - Yellow/Orange: Slow traffic\n * - Red: Heavy congestion\n * - Dark gray: Road closures\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { TrafficFlowModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module (auto-add to style if needed)\n * const trafficFlow = await TrafficFlowModule.get(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * trafficFlow.setVisible(false);\n * trafficFlow.setVisible(true);\n * ```\n *\n * @example\n * Filter by road type:\n * ```typescript\n * // Show only highway traffic\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk']\n * }\n * }]\n * });\n *\n * // Hide local streets\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'all_except',\n * values: ['street']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Show only road closures:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * showRoadClosures: 'only'\n * }]\n * });\n * ```\n *\n * @see [Traffic Flow Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic-flow)\n * @see [Traffic Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic)\n *\n * @group Traffic Flow\n */\nexport class TrafficFlowModule extends AbstractMapModule<TrafficFlowSourcesWithLayers, FlowConfig> {\n private originalFilters!: Record<string, FilterSpecification | undefined>;\n\n /**\n * Retrieves a TrafficFlowModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization, visibility, and filters.\n *\n * @returns A promise that resolves to the initialized TrafficFlowModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state\n * - `ensureAddedToStyle`: Auto-add traffic flow to style if missing\n * - `filters`: Road category and type filters\n *\n * **Style Requirement:**\n * Traffic flow must be included in the map style or added via `ensureAddedToStyle`.\n *\n * @throws Error if traffic flow source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const trafficFlowModule = await TrafficFlowModule.get(map);\n * ```\n *\n * @example\n * Auto-add to style:\n * ```typescript\n * const trafficFlowModule = await TrafficFlowModule.get(map, {\n * visible: true,\n * filters: {\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk', 'primary']\n * }\n * }]\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: FlowConfig): Promise<TrafficFlowModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, TRAFFIC_FLOW_SOURCE_ID, 'trafficFlow');\n return new TrafficFlowModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: FlowConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const flowSource = this.mapLibreMap.getSource(TRAFFIC_FLOW_SOURCE_ID);\n if (!flowSource) {\n throw notInTheStyle(`init ${TrafficFlowModule.name} with source ID ${TRAFFIC_FLOW_SOURCE_ID}`);\n }\n this.originalFilters = {};\n for (const layer of this.getLayers()) {\n this.originalFilters[layer.id] = layer.filter;\n }\n return { trafficFlow: new StyleSourceWithLayers(this.mapLibreMap, flowSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: FlowConfig | undefined) {\n this.setVisible(config?.visible ?? false);\n this._filter(config?.filters, false);\n return config;\n }\n\n private getLayers(): LayerSpecWithSource[] {\n return filterLayersBySources(this.tomtomMap.mapLibreMap, [TRAFFIC_FLOW_SOURCE_ID]);\n }\n\n /**\n * Applies filters to traffic flow display.\n *\n * @param filters - Filter configuration for road types, categories, and closures.\n * Pass `undefined` to reset to defaults (show all).\n *\n * @remarks\n * **Filter Options:**\n * - `roadCategories`: Filter by road importance (motorway, trunk, primary, etc.)\n * - `roadSubCategories`: Filter by specific street types\n * - `showRoadClosures`: Show only closures or exclude them\n *\n * **Available Road Categories:**\n * - `motorway`: Major highways\n * - `trunk`: Major roads\n * - `primary`: Primary roads\n * - `secondary`: Secondary roads\n * - `tertiary`: Tertiary roads\n * - `street`: Local streets\n *\n * **Filter Logic:**\n * Uses \"any\" (OR) logic - traffic matching any filter is shown.\n *\n * @example\n * Show only major roads:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'only',\n * values: ['motorway', 'trunk', 'primary']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Hide street-level traffic:\n * ```typescript\n * trafficFlow.filter({\n * any: [{\n * roadCategories: {\n * show: 'all_except',\n * values: ['street']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Multiple filter criteria:\n * ```typescript\n * trafficFlow.filter({\n * any: [\n * {\n * roadCategories: { show: 'only', values: ['motorway'] }\n * },\n * {\n * showRoadClosures: 'only'\n * }\n * ]\n * });\n * ```\n *\n * @example\n * Reset filters:\n * ```typescript\n * trafficFlow.filter(undefined);\n * ```\n */\n filter(filters?: TrafficFlowFilters) {\n this._filter(filters);\n }\n\n private _filter(filters: TrafficFlowFilters | undefined, updateConfig = true) {\n if (this.tomtomMap.mapReady) {\n if (filters?.any?.length) {\n const filterExpression = buildMapLibreFlowFilters(filters);\n if (filterExpression) {\n applyFilter(filterExpression, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n } else if (this.config?.filters?.any?.length) {\n applyFilter(undefined, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n }\n\n if (updateConfig) {\n this.config = omitBy({ ...this.config, filters }, isNil);\n }\n }\n\n /**\n * Sets the visibility of traffic flow layers.\n *\n * @param visible - `true` to show traffic flow, `false` to hide it.\n *\n * @example\n * ```typescript\n * trafficFlow.setVisible(true); // Show traffic\n * trafficFlow.setVisible(false); // Hide traffic\n * ```\n */\n setVisible(visible: boolean): void {\n this.config = { ...this.config, visible };\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficFlow.setLayersVisible(visible);\n }\n }\n\n /**\n * Returns if any layer for traffic flow is visible or not.\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.trafficFlow.isAnyLayerVisible();\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible traffic flow features.\n *\n * @remarks\n * Returns all traffic flow features currently rendered in the visible map area.\n * These represent road segments with real-time traffic speed information.\n *\n * **Feature Properties:**\n * Properties depend on the traffic flow vector tile schema and may include:\n * - Current speed\n * - Free flow speed\n * - Road category\n * - Congestion level\n * - Road closure status\n *\n * @example\n * ```typescript\n * const shown = trafficFlow.getShown();\n * console.log(`Visible flow segments: ${shown.trafficFlow.length}`);\n * shown.trafficFlow.forEach(segment => {\n * console.log('Road segment:', segment.properties);\n * });\n * ```\n *\n * @example\n * Analyze congestion in viewport:\n * ```typescript\n * const shown = trafficFlow.getShown();\n * const congested = shown.trafficFlow.filter(\n * segment => segment.properties.speed < segment.properties.freeFlowSpeed * 0.5\n * );\n * console.log(`Congested segments: ${congested.length}`);\n * ```\n */\n getShown(): { trafficFlow: TrafficFlowModuleFeature[] } {\n return {\n trafficFlow: this.mapLibreMap\n .queryRenderedFeatures({\n layers: this.sourcesWithLayers.trafficFlow.sourceAndLayerIDs.layerIDs,\n validate: false,\n })\n .map(trafficFlowMapping),\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule<TrafficFlowModuleFeature>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.trafficFlow,\n this.config?.events,\n trafficFlowMapping,\n );\n }\n}\n","import { isEmpty, isNil, omitBy } from 'lodash-es';\nimport type { FilterSpecification } from 'maplibre-gl';\nimport type { LayerSpecWithSource } from '../shared';\nimport {\n AbstractMapModule,\n EventsModule,\n filterLayersBySources,\n StyleSourceWithLayers,\n TRAFFIC_INCIDENTS_SOURCE_ID,\n} from '../shared';\nimport { notInTheStyle } from '../shared/errorMessages';\nimport { ensureAddedToStyle, waitUntilMapIsReady } from '../shared/mapUtils';\nimport type { TomTomMap } from '../TomTomMap';\nimport { applyFilter, buildMapLibreIncidentFilters } from './filters/trafficFilters';\nimport type { IncidentsConfig, TrafficIncidentsFilters } from './types/trafficIncidentsConfig';\nimport { TrafficIncidentsModuleFeature } from './types/trafficIncidentsFeature';\nimport { trafficIncidentMapping } from './util/trafficIncidentMapping';\n\n/**\n * IDs of sources and layers for traffic incidents module.\n */\ntype TrafficIncidentsSourcesWithLayers = {\n trafficIncidents: StyleSourceWithLayers;\n};\n\n/**\n * Traffic Incidents Module for displaying and configuring real-time traffic incidents on the map.\n *\n * This module controls the vector tile traffic incidents layers that show traffic\n * events like accidents, road closures, construction, and hazards.\n *\n * @remarks\n * **Features:**\n * - Toggle incidents visibility on/off\n * - Separate control for incident icons\n * - Filter by incident type (accident, construction, etc.)\n * - Filter by severity/delay magnitude\n * - Filter by road categories\n * - Icon and line/polygon visualization\n *\n * **Incident Types:**\n * - Accidents\n * - Road closures\n * - Construction/road works\n * - Weather conditions (fog, ice, rain, etc.)\n * - Lane closures\n * - Traffic jams\n * - Broken down vehicles\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { TrafficIncidentsModule } from '@tomtom-international/maps-sdk-js/map';\n *\n * // Get module (auto-add to style if needed)\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map, {\n * visible: true\n * });\n *\n * // Toggle visibility\n * trafficIncidentsModule.setVisible(false);\n * trafficIncidentsModule.setVisible(true);\n *\n * // Control icons separately\n * trafficIncidentsModule.setIconsVisible(false);\n * ```\n *\n * @example\n * Filter by incident type:\n * ```typescript\n * // Show only accidents and road closures\n * trafficIncidentsModule.filter({\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road-closed']\n * }\n * }]\n * });\n *\n * // Hide construction\n * trafficIncidentsModule.filter({\n * any: [{\n * incidentCategories: {\n * show: 'all_except',\n * values: ['roadworks']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter by severity:\n * ```typescript\n * // Show only major delays\n * incidents.filter({\n * any: [{\n * magnitudes: {\n * show: 'only',\n * values: ['major']\n * }\n * }]\n * });\n *\n * // Show incidents with at least 10 minutes delay\n * incidents.filter({\n * any: [{\n * delays: {\n * mustHaveDelay: true,\n * minDelayMinutes: 10\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter icons separately from incident areas:\n * ```typescript\n * // Show all incidents but only major icons\n * incidents.filter(\n * {\n * any: [{}] // Show all incidents\n * },\n * {\n * any: [{\n * magnitudes: { show: 'only', values: ['major'] }\n * }]\n * }\n * );\n * ```\n *\n * @see [Traffic Incidents Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic-incidents)\n * @see [Traffic Guide](https://docs.tomtom.com/maps-sdk-js/guides/map/traffic)\n *\n * @group Traffic Incidents\n */\nexport class TrafficIncidentsModule extends AbstractMapModule<TrafficIncidentsSourcesWithLayers, IncidentsConfig> {\n private originalFilters!: Record<string, FilterSpecification | undefined>;\n\n /**\n * Retrieves a TrafficIncidentsModule instance for the given map.\n *\n * @param map - The TomTomMap instance to attach this module to.\n * @param config - Optional configuration for initialization, visibility, and filters.\n *\n * @returns A promise that resolves to the initialized TrafficIncidentsModule.\n *\n * @remarks\n * **Configuration:**\n * - `visible`: Initial visibility state for all incidents\n * - `icons.visible`: Initial visibility for incident icons\n * - `ensureAddedToStyle`: Auto-add traffic incidents to style if missing\n * - `filters`: Incident type, severity, and delay filters\n * - `icons.filters`: Separate filters for icons\n *\n * @throws Error if traffic incidents source is not in style and `ensureAddedToStyle` is false\n *\n * @example\n * Default initialization:\n * ```typescript\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map);\n * ```\n *\n * @example\n * With configuration:\n * ```typescript\n * const trafficIncidentsModule = await TrafficIncidentsModule.get(map, {\n * visible: true,\n * icons: { visible: true },\n * filters: {\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road-closed', 'jam']\n * }\n * }]\n * }\n * });\n * ```\n */\n static async get(map: TomTomMap, config?: IncidentsConfig): Promise<TrafficIncidentsModule> {\n await waitUntilMapIsReady(map);\n await ensureAddedToStyle(map, TRAFFIC_INCIDENTS_SOURCE_ID, 'trafficIncidents');\n return new TrafficIncidentsModule(map, config);\n }\n\n private constructor(map: TomTomMap, config?: IncidentsConfig) {\n super('style', map, config);\n }\n\n /**\n * @ignore\n */\n protected _initSourcesWithLayers() {\n const incidentsSource = this.mapLibreMap.getSource(TRAFFIC_INCIDENTS_SOURCE_ID);\n if (!incidentsSource) {\n throw notInTheStyle(`init ${TrafficIncidentsModule.name} with source ID ${TRAFFIC_INCIDENTS_SOURCE_ID}`);\n }\n this.originalFilters = {};\n for (const layer of this.getLayers()) {\n this.originalFilters[layer.id] = layer.filter;\n }\n return { trafficIncidents: new StyleSourceWithLayers(this.mapLibreMap, incidentsSource) };\n }\n\n /**\n * @ignore\n */\n protected _applyConfig(config: IncidentsConfig | undefined) {\n // We do not update config in setVisible since it could override icons visibility setting:\n this._setVisible(config?.visible ?? false, { updateConfig: false });\n if (!isNil(config?.icons?.visible)) {\n this.setIconsVisible(config.icons.visible);\n }\n this._filter(config?.filters, config?.icons?.filters, false);\n return config;\n }\n\n /**\n * Applies filters to traffic incidents display.\n *\n * @param incidentFilters - Filter for incident areas/lines. Pass `undefined` to reset.\n * @param iconFilters - Optional separate filter for incident icons. Pass `undefined` to reset.\n *\n * @remarks\n * **Filter Options:**\n * - `incidentCategories`: Filter by incident type\n * - `magnitudes`: Filter by delay severity (minor/moderate/major/unknown)\n * - `delays`: Filter by delay duration\n * - `roadCategories`: Filter by road importance\n * - `roadSubCategories`: Filter by specific road types\n *\n * **Available Incident Categories:**\n * - `accident`, `road-closed`, `lane-closed`\n * - `roadworks` (construction)\n * - `jam` (traffic jam)\n * - `fog`, `rain`, `frost`, `wind`, `flooding`\n * - `danger`, `animals-on-road`, `narrow-lanes`\n * - `broken-down-vehicle`\n * - `other`\n *\n * **Delay Magnitudes:**\n * - `minor`: Small delays\n * - `moderate`: Moderate delays\n * - `major`: Significant delays\n * - `unknown`: Unknown or no delay info\n *\n * @example\n * Filter by type:\n * ```typescript\n * incidents.filter({\n * any: [{\n * incidentCategories: {\n * show: 'only',\n * values: ['accident', 'road-closed']\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Filter by severity and delay:\n * ```typescript\n * incidents.filter({\n * any: [{\n * magnitudes: { show: 'only', values: ['major', 'moderate'] },\n * delays: {\n * mustHaveDelay: true,\n * minDelayMinutes: 5\n * }\n * }]\n * });\n * ```\n *\n * @example\n * Different filters for icons and areas:\n * ```typescript\n * // Show all incidents on roads\n * const incidentFilter = {\n * any: [{\n * roadCategories: { show: 'only', values: ['motorway', 'trunk'] }\n * }]\n * };\n *\n * // But only show icons for major incidents\n * const iconFilter = {\n * any: [{\n * magnitudes: { show: 'only', values: ['major'] }\n * }]\n * };\n *\n * incidents.filter(incidentFilter, iconFilter);\n * ```\n */\n filter(incidentFilters?: TrafficIncidentsFilters, iconFilters?: TrafficIncidentsFilters) {\n this._filter(incidentFilters, iconFilters);\n }\n\n private _filter(\n incidentFilters: TrafficIncidentsFilters | undefined,\n iconFilters: TrafficIncidentsFilters | undefined,\n updateConfig = true,\n ) {\n if (this.tomtomMap.mapReady) {\n if (incidentFilters?.any?.length) {\n const incidentFilterExpression = buildMapLibreIncidentFilters(incidentFilters);\n if (incidentFilterExpression) {\n const layers = iconFilters ? this.getNonSymbolLayers() : this.getLayers();\n applyFilter(incidentFilterExpression, layers, this.mapLibreMap, this.originalFilters);\n }\n } else if (this.config?.filters?.any?.length) {\n applyFilter(undefined, this.getLayers(), this.mapLibreMap, this.originalFilters);\n }\n if (iconFilters?.any?.length) {\n const iconFilterExpression = buildMapLibreIncidentFilters(iconFilters);\n if (iconFilterExpression) {\n applyFilter(iconFilterExpression, this.getSymbolLayers(), this.mapLibreMap, this.originalFilters);\n }\n }\n }\n\n // else: default incidents visibility has been set already if necessary\n if (updateConfig) {\n this.config = omitBy(\n {\n ...this.config,\n filters: incidentFilters,\n icons: { ...this.config?.icons, filters: iconFilters },\n },\n isNil,\n );\n }\n }\n\n private getLayers(): LayerSpecWithSource[] {\n return filterLayersBySources(this.tomtomMap.mapLibreMap, [TRAFFIC_INCIDENTS_SOURCE_ID]);\n }\n\n private getSymbolLayers(): LayerSpecWithSource[] {\n return this.getLayers().filter((layer) => layer.type === 'symbol');\n }\n\n private getNonSymbolLayers(): LayerSpecWithSource[] {\n return this.getLayers().filter((layer) => layer.type != 'symbol');\n }\n\n /**\n * Sets the visibility of incident icon layers.\n *\n * @param visible - `true` to show icons, `false` to hide them.\n *\n * @remarks\n * This controls only the icon/symbol layers, not the incident area polygons or lines.\n *\n * @example\n * ```typescript\n * // Hide icons but keep incident areas visible\n * incidents.setIconsVisible(false);\n *\n * // Show icons\n * incidents.setIconsVisible(true);\n * ```\n */\n setIconsVisible(visible: boolean): void {\n // We adjust the config for this change (but it might be overwritten if it's part of an \"applyConfig\" call)\n this.config = { ...this.config, icons: { ...this.config?.icons, visible } };\n\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficIncidents.setLayersVisible(\n visible,\n (layerSpec) => layerSpec.type === 'symbol',\n );\n }\n }\n\n /**\n * Sets the visibility of all traffic incident layers.\n *\n * @param visible - `true` to show incidents, `false` to hide them.\n *\n * @remarks\n * This controls all incident layers including icons, lines, and polygons.\n *\n * @example\n * ```typescript\n * incidents.setVisible(false); // Hide all incidents\n * incidents.setVisible(true); // Show all incidents\n * ```\n */\n setVisible(visible: boolean): void {\n this._setVisible(visible);\n }\n\n private _setVisible(visible: boolean, options?: { updateConfig: boolean }): void {\n const updateConfig = options?.updateConfig ?? true;\n if (updateConfig) {\n // setting all traffic visible also nullifies the icons visible setting\n delete this.config?.icons?.visible;\n // we remove empty values from config to avoid confusion (in case icons part is just empty after deleting visible)\n this.config = { ...omitBy({ ...this.config }, isEmpty), visible };\n }\n if (this.tomtomMap.mapReady) {\n this.sourcesWithLayers.trafficIncidents.setLayersVisible(visible);\n }\n }\n\n /**\n * Checks if any traffic incident layers are currently visible.\n *\n * @returns `true` if any incident layer is visible, `false` if all are hidden.\n *\n * @example\n * ```typescript\n * if (incidents.isVisible()) {\n * console.log('Incidents are displayed');\n * }\n * ```\n */\n isVisible(): boolean {\n return this.sourcesWithLayers.trafficIncidents.isAnyLayerVisible();\n }\n\n /**\n * Checks if any incident icon layers are currently visible.\n *\n * @returns `true` if any icon layer is visible, `false` if all icons are hidden.\n *\n * @example\n * ```typescript\n * if (incidents.anyIconLayersVisible()) {\n * console.log('Incident icons are shown');\n * }\n * ```\n */\n anyIconLayersVisible(): boolean {\n return !!this.sourcesWithLayers.trafficIncidents?.isAnyLayerVisible((layerSpec) => layerSpec.type === 'symbol');\n }\n\n /**\n * Returns features currently visible in the viewport.\n *\n * @returns An object containing visible traffic incident features.\n *\n * @remarks\n * Returns all incident features currently rendered in the visible map area,\n * including both incident areas/lines and icon markers.\n *\n * **Feature Properties:**\n * Properties depend on the traffic incidents vector tile schema and may include:\n * - Incident type/category\n * - Severity/magnitude\n * - Delay information\n * - Road affected\n * - Description\n *\n * @example\n * ```typescript\n * const shown = incidents.getShown();\n * console.log(`Visible incidents: ${shown.trafficIncidents.length}`);\n * shown.trafficIncidents.forEach(incident => {\n * console.log('Incident:', incident.properties);\n * });\n * ```\n *\n * @example\n * Filter and analyze visible incidents:\n * ```typescript\n * const shown = incidents.getShown();\n * const accidents = shown.trafficIncidents.filter(\n * incident => incident.properties.category === 'accident'\n * );\n * console.log(`Accidents in view: ${accidents.length}`);\n * ```\n */\n getShown(): { trafficIncidents: TrafficIncidentsModuleFeature[] } {\n return {\n trafficIncidents: this.mapLibreMap\n .queryRenderedFeatures({\n layers: this.sourcesWithLayers.trafficIncidents.sourceAndLayerIDs.layerIDs,\n validate: false,\n })\n .map(trafficIncidentMapping),\n };\n }\n\n /**\n * Create the events on/off for this module\n * @returns An instance of EventsModule\n */\n get events() {\n return new EventsModule<TrafficIncidentsModuleFeature>(\n this.tomtomMap._eventsProxy,\n this.sourcesWithLayers.trafficIncidents,\n this.config?.events,\n trafficIncidentMapping,\n );\n }\n}\n","import type { FilterShowMode, ValuesFilter } from '../../shared';\n\n/**\n * Available road hierarchy category identifiers.\n *\n * @remarks\n * These categories represent different levels in the road network hierarchy,\n * from major highways to local streets.\n *\n * @group Traffic\n */\nexport const roadCategories = [\n 'motorway',\n 'motorway_link',\n 'trunk',\n 'trunk_link',\n 'primary',\n 'primary_link',\n 'secondary',\n 'secondary_link',\n 'tertiary',\n 'tertiary_link',\n 'street',\n 'service',\n 'track',\n] as const;\n\n/**\n * Road hierarchy category type.\n *\n * @remarks\n * Classifies roads by their importance and capacity in the transportation network.\n * Used for filtering traffic data display based on road significance.\n *\n * Road hierarchy (from highest to lowest):\n * - `motorway` / `motorway_link` - High-capacity highways with restricted access\n * - `trunk` / `trunk_link` - Major inter-city roads\n * - `primary` / `primary_link` - Primary through routes\n * - `secondary` / `secondary_link` - Secondary through routes\n * - `tertiary` / `tertiary_link` - Connecting roads\n * - `street` - Local streets (see {@link StreetRoadSubCategory})\n * - `service` - Service roads (see {@link ServiceRoadSubCategory})\n * - `track` - Unpaved or agricultural tracks\n *\n * @group Traffic\n */\nexport type RoadCategory = (typeof roadCategories)[number];\n\n/**\n * Available street road sub-category identifiers.\n *\n * @remarks\n * Provides finer granularity for classifying local streets.\n *\n * @group Traffic\n */\nexport const streetRoadSubCategories = ['unclassified', 'residential', 'living_street'] as const;\n\n/**\n * Street road sub-category type.\n *\n * @remarks\n * Further classifies `street` roads into specific sub-types.\n *\n * Sub-categories:\n * - `unclassified` - Unclassified local roads\n * - `residential` - Roads in residential areas\n * - `living_street` - Pedestrian-priority shared streets\n *\n * @group Traffic\n */\nexport type StreetRoadSubCategory = (typeof streetRoadSubCategories)[number];\n\n/**\n * Available service road sub-category identifiers.\n *\n * @remarks\n * Provides finer granularity for classifying service roads.\n *\n * @group Traffic\n */\nexport const serviceRoadSubCategories = ['parking', 'driveway', 'alley'] as const;\n\n/**\n * Service road sub-category type.\n *\n * @remarks\n * Further classifies `service` roads into specific sub-types.\n *\n * Sub-categories:\n * - `parking` - Roads within parking areas\n * - `driveway` - Private access roads\n * - `alley` - Narrow back-access lanes\n *\n * @group Traffic\n */\nexport type ServiceRoadSubCategory = (typeof serviceRoadSubCategories)[number];\n\n/**\n * Road sub-category type.\n *\n * @remarks\n * Combines street and service sub-categories. Only `street` and `service` road categories have sub-categories.\n *\n * @group Traffic\n */\nexport type RoadSubCategory = StreetRoadSubCategory | ServiceRoadSubCategory;\n\n/**\n * Common filter configuration shared between traffic incidents and flow visualization.\n *\n * @remarks\n * Provides road category filtering capabilities used by both incident and flow modules.\n *\n * @group Traffic\n */\nexport type TrafficCommonFilter = {\n /**\n * Filters traffic data by road hierarchy categories.\n *\n * @remarks\n * Controls which road types display traffic information.\n * Use the `mode` field to specify whether to show or hide the selected categories.\n *\n * @example\n * ```ts\n * // Show only motorways and trunk roads\n * roadCategories: { show: 'only', values: ['motorway', 'trunk'] }\n * ```\n */\n roadCategories?: ValuesFilter<RoadCategory>;\n\n /**\n * Filters traffic data by road sub-categories.\n *\n * @remarks\n * Provides finer-grained control for street and service roads.\n * Applies to sub-categories of {@link StreetRoadSubCategory} and {@link ServiceRoadSubCategory}.\n *\n * @example\n * ```ts\n * // Hide minor local streets\n * roadSubCategories: { show: 'all_except', values: ['living_street'] }\n * ```\n */\n roadSubCategories?: ValuesFilter<RoadSubCategory>;\n};\n\nexport type { FilterShowMode };\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { Map } from 'maplibre-gl';\nimport type { TomTomMap } from '../TomTomMap';\nimport type {\n CalculateFittingBBoxOptions,\n CalculateMapBoundsOptions,\n CalculateMapCenterOptions,\n} from './types/paddedBounds';\n\n// (Originally mostly AI-generated)\n\ntype BoundsPX = {\n left: number;\n top: number;\n right: number;\n bottom: number;\n};\n\nconst getRelativeElementBounds = (elementRect: DOMRect, containerRect: DOMRect): BoundsPX => ({\n left: elementRect.left - containerRect.left,\n top: elementRect.top - containerRect.top,\n right: elementRect.right - containerRect.left,\n bottom: elementRect.bottom - containerRect.top,\n});\n\nconst isElementOutsideContainer = (bounds: BoundsPX, containerWidth: number, containerHeight: number): boolean =>\n bounds.right <= 0 || bounds.left >= containerWidth || bounds.bottom <= 0 || bounds.top >= containerHeight;\n\n// Adjusts the current visible bounds to account for a horizontal bar element (that spans the whole width)\nconst adjustForHorizontalBar = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerHeight: number,\n padding: number,\n): void => {\n const distanceFromTop = bounds.top;\n const distanceFromBottom = containerHeight - bounds.bottom;\n if (distanceFromTop <= distanceFromBottom) {\n currentVisible.top = Math.max(currentVisible.top, bounds.bottom + padding);\n } else {\n currentVisible.bottom = Math.min(currentVisible.bottom, bounds.top - padding);\n }\n};\n\n// Adjusts the current visible bounds to account for a vertical bar element (that spans the whole height)\nconst adjustForVerticalBar = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerWidth: number,\n padding: number,\n): void => {\n const distanceFromLeft = bounds.left;\n const distanceFromRight = containerWidth - bounds.right;\n if (distanceFromLeft <= distanceFromRight) {\n currentVisible.left = Math.max(currentVisible.left, bounds.right + padding);\n } else {\n currentVisible.right = Math.min(currentVisible.right, bounds.left - padding);\n }\n};\n\n// Adjusts the current visible bounds to account for a floating element (that does not span full width or height)\nconst adjustForFloatingElement = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n containerWidth: number,\n containerHeight: number,\n padding: number,\n): void => {\n const distanceFromLeft = bounds.left;\n const distanceFromRight = containerWidth - bounds.right;\n const distanceFromTop = bounds.top;\n const distanceFromBottom = containerHeight - bounds.bottom;\n const minDistance = Math.min(distanceFromLeft, distanceFromRight, distanceFromTop, distanceFromBottom);\n\n if (minDistance === distanceFromLeft) {\n currentVisible.left = Math.max(currentVisible.left, bounds.right + padding);\n } else if (minDistance === distanceFromRight) {\n currentVisible.right = Math.min(currentVisible.right, bounds.left - padding);\n } else if (minDistance === distanceFromTop) {\n currentVisible.top = Math.max(currentVisible.top, bounds.bottom + padding);\n } else {\n currentVisible.bottom = Math.min(currentVisible.bottom, bounds.top - padding);\n }\n};\n\n/**\n * Clamps the element bounds to the container boundaries.\n * This handles cases where UI elements extend beyond the visible container\n * (e.g., a sidebar that goes past the screen edge).\n */\nconst clampBoundsToContainer = (bounds: BoundsPX, containerWidth: number, containerHeight: number): BoundsPX => ({\n left: Math.max(0, bounds.left),\n top: Math.max(0, bounds.top),\n right: Math.min(containerWidth, bounds.right),\n bottom: Math.min(containerHeight, bounds.bottom),\n});\n\nconst adjustVisibleBoundsForElement = (\n bounds: BoundsPX,\n currentVisible: BoundsPX, // will be mutated\n mapWidthPX: number,\n mapHeightPX: number,\n padding: number,\n): void => {\n // Clamp bounds to container to handle elements that extend beyond the screen\n const clampedBounds = clampBoundsToContainer(bounds, mapWidthPX, mapHeightPX);\n\n // Determine if the element touches or extends beyond the full width or height of the container\n // An element \"spans full width\" if it touches both left and right edges (or extends beyond)\n const spansFullWidth = bounds.left <= 0 && bounds.right >= mapWidthPX;\n const spansFullHeight = bounds.top <= 0 && bounds.bottom >= mapHeightPX;\n\n // For elements spanning full width (horizontal bars), only consider vertical adjustment\n if (spansFullWidth && !spansFullHeight) {\n adjustForHorizontalBar(clampedBounds, currentVisible, mapHeightPX, padding);\n return;\n }\n\n // For elements spanning full height (vertical bars), only consider horizontal adjustment\n if (spansFullHeight && !spansFullWidth) {\n adjustForVerticalBar(clampedBounds, currentVisible, mapWidthPX, padding);\n return;\n }\n\n // Check if element touches/extends beyond an edge (makes it act like a bar on that side)\n const touchesLeft = bounds.left <= 0;\n const touchesRight = bounds.right >= mapWidthPX;\n const touchesTop = bounds.top <= 0;\n const touchesBottom = bounds.bottom >= mapHeightPX;\n\n // If element touches opposite edges in one dimension but not the other,\n // treat it as a bar in that dimension\n if ((touchesLeft || touchesRight) && !touchesTop && !touchesBottom) {\n // Element extends horizontally but not vertically - treat as horizontal bar\n adjustForHorizontalBar(clampedBounds, currentVisible, mapHeightPX, padding);\n return;\n }\n if ((touchesTop || touchesBottom) && !touchesLeft && !touchesRight) {\n // Element extends vertically but not horizontally - treat as vertical bar\n adjustForVerticalBar(clampedBounds, currentVisible, mapWidthPX, padding);\n return;\n }\n\n // For corner elements (touching edges in both dimensions) or floating elements\n // determine the best adjustment based on which edge the element is closest to\n adjustForFloatingElement(clampedBounds, currentVisible, mapWidthPX, mapHeightPX, padding);\n};\n\n/**\n * Resolves a surrounding element reference to an HTMLElement.\n * If the reference is a string, it's treated as a DOM selector.\n * @param ref - Either an HTMLElement or a DOM selector string\n * @returns The resolved HTMLElement, or null if not found\n */\nconst toElement = (ref: HTMLElement | string): HTMLElement | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref);\n }\n return ref;\n};\n\n/**\n * Resolves a map reference to a MapLibre Map instance.\n * If the reference is a TomTomMap, it extracts the underlying mapLibreMap.\n * @param map - Either a TomTomMap or a MapLibre Map instance\n * @returns The MapLibre Map instance\n */\nconst getMapLibreMap = (map: TomTomMap | Map): Map => {\n if ('mapLibreMap' in map) {\n return map.mapLibreMap;\n }\n return map;\n};\n\ntype VisibleAreaResult = {\n visibleAreaBounds: BoundsPX;\n mapWidthPX: number;\n mapHeightPX: number;\n} | null;\n\n/**\n * Calculates the visible area bounds in pixel coordinates after accounting for\n * surrounding UI elements and padding.\n */\nconst calculateVisibleAreaBounds = (\n map: TomTomMap | Map,\n surroundingElements: (HTMLElement | string)[],\n paddingPX: number,\n): VisibleAreaResult => {\n const containerRect = getMapLibreMap(map).getContainer().getBoundingClientRect();\n const { width: mapWidthPX, height: mapHeightPX } = containerRect;\n\n // Initialize visible bounds with padding applied to all edges (will be mutated)\n const visibleAreaBounds: BoundsPX = {\n left: paddingPX,\n top: paddingPX,\n right: mapWidthPX - paddingPX,\n bottom: mapHeightPX - paddingPX,\n };\n\n for (const elementRef of surroundingElements) {\n const element = toElement(elementRef);\n if (!element) {\n continue;\n }\n const elementRect = element.getBoundingClientRect();\n const elementBounds = getRelativeElementBounds(elementRect, containerRect);\n\n if (isElementOutsideContainer(elementBounds, mapWidthPX, mapHeightPX)) {\n continue;\n }\n\n adjustVisibleBoundsForElement(elementBounds, visibleAreaBounds, mapWidthPX, mapHeightPX, paddingPX);\n }\n\n if (visibleAreaBounds.left >= visibleAreaBounds.right || visibleAreaBounds.top >= visibleAreaBounds.bottom) {\n return null;\n }\n\n return { visibleAreaBounds, mapWidthPX, mapHeightPX };\n};\n\n/**\n * Calculates the bounding box in lng-lat coordinates of the visible map area\n * that does not overlap with the given UI HTML elements.\n *\n * @remarks\n * This is useful for determining the area of the map that is not obscured by UI components.\n *\n * @param options - The options for calculating map bounds\n * @returns The padded bounding box as [west, south, east, north], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculatePaddedBBox = (options: CalculateMapBoundsOptions): BBox | null => {\n const { map, surroundingElements, paddingPX = 0 } = options;\n\n const result = calculateVisibleAreaBounds(map, surroundingElements, paddingPX);\n if (!result) {\n return null;\n }\n\n const { visibleAreaBounds } = result;\n const mapLibreMap = getMapLibreMap(map);\n const sw = mapLibreMap.unproject([visibleAreaBounds.left, visibleAreaBounds.bottom]);\n const ne = mapLibreMap.unproject([visibleAreaBounds.right, visibleAreaBounds.top]);\n\n return [sw.lng, sw.lat, ne.lng, ne.lat];\n};\n\n/**\n * Calculates the center point in lng-lat coordinates of the visible map area\n * that does not overlap with the given UI HTML elements.\n *\n * @remarks\n * * This is useful to offset the map center in a way that it looks harmonious with surrounding UI components.\n * * It's equivalent to the center of calculatePaddedBBox.\n *\n * @param options - The options for calculating map center\n * @returns The center as [lng, lat], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculatePaddedCenter = (options: CalculateMapCenterOptions): Position | null => {\n const { map, surroundingElements } = options;\n const bbox = calculatePaddedBBox({ map, surroundingElements });\n if (!bbox) {\n return null;\n }\n const [west, south, east, north] = bbox;\n return [(west + east) / 2, (south + north) / 2];\n};\n\n/**\n * Calculates an expanded bounding box that, when the map is zoomed to it, ensures that the given to-be-contained bounding box\n * is visible within the area not obscured by surrounding UI elements, including optional padding.\n * * In other words, calculates a bounding box that ensure the to-be-contained bbox fits within the visible area of the map.\n *\n * @remarks\n * This is useful when you have a specific geographic area (containedBBox) that you want to be fully visible\n * in the unobscured portion of the map (the area not covered by UI components).\n * The function returns a larger bounding box that accounts for the space taken by UI elements.\n *\n * @param options - The options for calculating expanded bounds\n * @returns The expanded bounding box as [west, south, east, north], or null if the visible area is too small\n *\n * @group Utils\n */\nexport const calculateFittingBBox = (options: CalculateFittingBBoxOptions): BBox | null => {\n const { map, toBeContainedBBox, surroundingElements, paddingPX = 0 } = options;\n\n const result = calculateVisibleAreaBounds(map, surroundingElements, paddingPX);\n if (!result) {\n return null;\n }\n\n const { visibleAreaBounds, mapWidthPX, mapHeightPX } = result;\n\n // Calculate the ratios of how much the visible area is offset from the full container\n // These represent what fraction of the full map the visible area occupies\n const leftRatio = visibleAreaBounds.left / mapWidthPX;\n const rightRatio = visibleAreaBounds.right / mapWidthPX;\n const topRatio = visibleAreaBounds.top / mapHeightPX;\n const bottomRatio = visibleAreaBounds.bottom / mapHeightPX;\n\n // Calculate the width and height ratios of the visible area\n const widthRatio = rightRatio - leftRatio;\n const heightRatio = bottomRatio - topRatio;\n\n // Target bbox coordinates\n const [west, south, east, north] = toBeContainedBBox;\n const targetWidth = east - west;\n const targetHeight = north - south;\n\n // Calculate the full map extent needed so that when cropped by the visible area,\n // the target bbox fits exactly\n const fullWidth = targetWidth / widthRatio;\n const fullHeight = targetHeight / heightRatio;\n\n // Calculate the fitting expanded bounds\n // The visible area starts at leftRatio of the full width, so we need to extend west\n const expandedWest = west - leftRatio * fullWidth;\n const expandedEast = expandedWest + fullWidth;\n\n // For latitude, remember that in pixel coordinates, top is smaller Y but higher latitude\n // topRatio represents the fraction from top, which corresponds to north\n const expandedNorth = north + topRatio * fullHeight;\n const expandedSouth = expandedNorth - fullHeight;\n\n return [expandedWest, expandedSouth, expandedEast, expandedNorth];\n};\n"],"names":["FLOW_TAGS","trafficFlowMapping","feature","properties","id","type","geometry","roadCategory","road_category","leftHandTraffic","Boolean","left_hand_traffic","roadClosure","road_closure","relativeSpeed","relative_speed","road_subcategory","roadSubcategory","absolute_speed","absoluteSpeed","part_of_two_way_road","partOfTwoWayRoad","openlr","display_class","displayClass","INCIDENT_TAGS","incidentToIconCategoryMapping","other","accident","fog","danger","rain","frost","jam","roadworks","wind","flooding","iconToIncidentCategoryMapping","Object","fromEntries","entries","map","category","code","trafficIncidentMapping","generateId","description","description_0","icon_category_0","magnitudeOfDelay","indexedMagnitudes","magnitude_of_delay","delay","delayInSeconds","start_time","startTime","Date","end_time","endTime","probability_of_occurrence","probabilityOfOccurrence","number_of_reports","numberOfReports","last_report_time","lastReportTime","average_speed_kmph","averageSpeedKmph","time_validity","timeValidity","point_type","pointType","notInTheStyle","actionText","Error","isDOMImageSupported","document","DOMParser","btoa","svgToImg","svgDomElement","img","createElement","src","XMLSerializer","serializeToString","parseSvg","svgString","parseFromString","documentElement","pinSvg","options","element","fillColor","querySelector","setAttribute","outlineColor","outlineOpacity","toString","asDefined","value","isNil","assertDefined","TomTomMapSource","constructor","spec","runtimeSource","this","ensureAddedToMap","getSource","addSource","AbstractSourceWithLayers","source","layerSpecs","_layerSpecs","_updateSourceAndLayerIDs","isAnyLayerVisible","filter","getLayerSpecs","some","layer","isLayerVisible","areAllLayersVisible","every","_sourceAndLayerIDs","sourceID","layerIDs","getLayoutProperty","setLayersVisible","visible","layerSpec","setLayoutProperty","validate","sourceAndLayerIDs","equalSourceAndLayerIDs","length","index","filterLayersBySources","loadedMap","sourceIDs","getStyle","layers","includes","StyleSourceWithLayers","super","sources","AddedSourceWithLayers","sourceId","sourceSpec","ensureLayersAddedToMap","getLayer","addLayer","beforeID","ensureAddedToMapWithVisibility","addLayersToMap","emptyFeatureCollection","features","GeoJSONSourceWithLayers","data","promoteId","shownFeatures","show","featureCollection","setData","clear","findFeature","find","f","putEventState","mode","eventState","state","cleanEventState","cleanEventStates","changed","states","waitUntilMapIsReady","async","tomtomMap","mapReady","mapLibreMap","once","deserializeFeatures","keys","key","JSON","parse","_e","changeLayerProps","newLayerProps","prevLayerProps","layerId","maxzoom","minzoom","setLayerZoomRange","getMinZoom","getMaxZoom","setFilter","property","layout","paint","setPaintProperty","changeLayersProps","forEach","layoutPaint","addLayers","layersToAdd","layerIdsAlreadyOnMap","Set","visibility","add","mapIdDependency","has","push","idsWeCanProcess","console","error","stringify","updateStyleWithModule","style","styleModule","include","cannotAddStyleModuleToCustomStyle","ensureAddedToStyle","isStyleLoaded","setStyle","isSourceLoaded","waitUntilSourceIsLoaded","addOrUpdateImage","imageId","imageToLoad","warn","addOrUpdateToMap","imgElement","hasImage","addImage","ensureImageLoaded","complete","naturalWidth","onload","onerror","loadImage","getStandardStyleTheme","standardStyleID","getStyleLightDarkTheme","styleInput","standardStyle","AbstractMapModule","sourceType","config","_initializing","moduleReady","eventsProxy","_eventsProxy","initSourcesWithLayers","applyConfig","addStyleChangeHandler","onStyleAboutToChange","onStyleChanged","restoreDataAndConfig","restore","sourcesWithLayers","_initSourcesWithLayers","name","sourceWithLayers","updateIfRegistered","waitUntilModuleReady","Promise","resolve","interval","setInterval","clearInterval","_applyConfig","resetConfig","requestAnimationFrame","restoreDataAndConfigImpl","getConfig","EventsModule","eventProxy","mapping","on","handler","addEventHandler","rest","off","remove","AbstractEventProxy","interactiveLayerIDs","handlers","findHandlers","types","flatMap","sourceEventTypeHandlers","ensureInteractiveLayerIDsAdded","handlerFn","fn","matchesLayers","isEmpty","removeAll","hasSourceID","values","sourceHandlers","isHighPriority","eventType","featureId","featuresToUpdate","i","findFeatureById","updatedFeature","omit","splice","removeEventStateAndShow","newEventType","rawFeature","prevFeaturesToUpdate","updatedIndex","updateEventState","eventFeature","prevEventFeature","prevSourceWithLayers","featureB","featureA","eventsProxyDefaultConfig","precisionMode","paddingBoxPx","cursorOnHover","cursorOnMouseDown","cursorOnMap","longHoverDelayAfterMapMoveMS","longHoverDelayOnStillMapMS","EventsProxy","enabled","firstDelayedHoverSinceMapMove","mapCanvas","getCanvas","cursor","lastCursorStyle","listenToEvents","ev","onMouseMove","onMouseStart","onMouseOut","onMouseDown","onMouseUp","onMapClick","enable","clearLongHoverTimeout","toPaddedBounds","point","padding","x","y","isEnabled","isMoving","getRenderedFeatures","precision","renderedFeatures","queryRenderedFeatures","window","clearTimeout","longHoverTimeoutHandlerID","restartLongHoverTimeout","setTimeout","handleLongHoverTimeout","hoveringSourceWithLayers","hoveringFeature","hoveringLngLat","hoveringFeatures","hoveredTopFeature","hoverChanged","mouseInMotionOverHoveredFeature","hoveringPoint","prevHoveredPoint","prevHoveredFeature","detectHoverState","lngLat","prevHoveredSourceWithLayers","firstHandler","updateHoverCursor","hoverHandlers","hoverMoveHandlers","clickType","clickedFeatures","prevClickedFeature","lastClickedFeature","prevClickedSourceWithLayers","lastClickedSourceWithLayers","clickHandlers","mapStyleLayerIDs","country","lowestPlaceLabel","poi","lowestLabel","lowestRoadLine","lowestBuilding","POI_SOURCE_ID","HILLSHADE_SOURCE_ID","BASE_MAP_SOURCE_ID","TRAFFIC_INCIDENTS_SOURCE_ID","TRAFFIC_FLOW_SOURCE_ID","poiLayerIDs","mapDisplayPoiCategoryMappings","ACCESS_GATEWAY","ADVENTURE_SPORTS_FACILITY","ADVENTURE_SPORTS_VENUE","AGRICULTURE","AIRPORT","AMUSEMENT_PARK","AQUATIC_ZOO","ASHRAM","ATM","AUTOMOTIVE_DEALER","BANK","BEACH","BUS_STOP","BUSINESS_PARK","CAFE_PUB","CAMPING_GROUND","CAR_WASH","CASH_DISPENSER","CASINO","CHURCH","CINEMA","CLOTHING_SHOP","CLUB_ASSOCIATION","COLLEGE_UNIVERSITY","COMMERCIAL_BUILDING","COMMUNITY_CENTER","COMPANY","CONCERT_HALL","COURTHOUSE","CULTURAL_CENTER","DENTIST","DEPARTMENT_STORE","DOCTOR","ELECTRIC_VEHICLE_STATION","EMBASSY","EMERGENCY_MEDICAL_SERVICE","EMERGENCY_ROOM","ENTERTAINMENT","EXCHANGE","EXHIBITION_CONVENTION_CENTER","FERRY_TERMINAL","FIRE_STATION_BRIGADE","FRONTIER_CROSSING","FUEL_FACILITIES","GAS_STATION","GEOGRAPHIC_FEATURE","GOLD_EXCHANGE","GOLF_COURSE","GOVERNMENT_OFFICE","GURUDWARA","HEALTH_CARE_SERVICE","HELIPAD_HELICOPTER_LANDING","HILL","HOLIDAY_RENTAL","HOSPITAL","HOSPITAL_POLYCLINIC","HOTEL_MOTEL","ICE_SKATING_RINK","IMPORTANT_TOURIST_ATTRACTION","INDUSTRIAL_BUILDING","LEISURE_CENTER","LIBRARY","MANUFACTURING_FACILITY","MARINA","MARKET","MEDIA_FACILITY","MILITARY_INSTALLATION","MOSQUE","MOTORING_ORGANIZATION_OFFICE","MOUNTAIN_PASS","MOUNTAIN_PEAK","MOVIE_THEATER","MUSEUM","NATIVE_RESERVATION","NIGHTLIFE","NON_GOVERNMENTAL_ORGANIZATION","OPEN_PARKING_AREA","OPERA_HOUSE","PAGODA","PARK_RECREATION_AREA","PARKING_GARAGE","PETROL_STATION","PHARMACY","PLACE_OF_WORSHIP","POLICE_STATION","PORT_WAREHOUSE_FACILITY","POST_OFFICE","PRIMARY_RESOURCE_UTILITY","PRISON_CORRECTIONAL_FACILITY","PUBLIC_AMENITY","PUBLIC_TRANSPORT_STOP","RAILWAY_STATION","RENT_A_CAR_FACILITY","RENT_A_CAR_PARKING","REPAIR_FACILITY","RESEARCH_FACILITY","RESIDENTIAL_ACCOMMODATION","REST_AREA","RESTAURANT","RESTAURANT_AREA","SCENIC_PANORAMIC_VIEW","SCHOOL","SHOP","SHOPPING_CENTER","SPORTS_CENTER","STADIUM","SUPERMARKETS_HYPERMARKETS","SWIMMING_POOL","SYNAGOG","TAXI_STAND","TEMPLE","TENNIS_COURT","THEATER","TOLL_GATE","TOURIST_INFORMATION_OFFICE","TRAFFIC_CONTROL_DEPARTMENT","TRAFFIC_SERVICE_CENTER","TRAIL_SYSTEM","TRAILS","TRANSPORT_AUTHORITY_VEHICLE_REGISTRATION","TRUCK_STOP","VACATION_RENTAL","VETERINARIAN","WATER_SPORT","WEIGH_STATION","WELFARE_ORGANIZATION","WINERY","ZOOS_ARBORETA_BOTANICAL_GARDEN","completeMapDisplayPoiCategoryMappings","SECURED_ENTRANCE","AGRICULTURAL_BUSINESS","FARM","HORTICULTURE","PRIMARY_PRODUCER","AIRFIELD","AIRLINE_ACCESS","MILITARY_AIRPORT","PRIVATE_AIRPORT","PUBLIC_AIRPORT","ATV_DEALER","BOAT_DEALER","BUS_DEALER","CAR_DEALER","MOTORCYCLE_DEALER","RECREATIONAL_VEHICLE_DEALER","TRUCK_DEALER","VAN_DEALER","DIVERSIFIED_FINANCIALS","SAVINGS_INSTITUTION","BEACH_CLUB","BAR","CAFE","COCKTAIL_BAR","COFFEE_SHOP","INTERNET_CAFE","PUB","TEA_HOUSE","WINE_BAR","CARAVAN_SITE","RECREATIONAL_CAMPING_GROUND","REST_CAMP","TRUCK_WASH","DRIVE_IN_MOVIES","CHILDRENS_CLOTHES","MENS_CLOTHING","SPECIALTY_CLOTHING_SHOP","WOMENS_CLOTHING","PRIVATE_CLUB","JUNIOR_COLLEGE_COMMUNITY_COLLEGE","BUILDING","ADVERTISING_COMPANY","AGRICULTURAL_TECHNOLOGY","AIRLINE_COMPANY","AUTOMOBILE_COMPANY","BUSINESS_SERVICES","BUS_CHARTER_COMPANY","CABLE_TELEPHONE_COMPANY","CLEANING_SERVICES","COMPUTER_DATA_SERVICES","CONSTRUCTION_COMPANY","DELIVERY_SERVICE","ELECTRONICS_COMPANY","EQUIPMENT_RENTAL","FUNERAL_SERVICE_MORTUARIES","IMPORT_EXPORT_AND_DISTRIBUTION","INSURANCE_COMPANY","INVESTMENT_ADVISOR","LEGAL_SERVICES","MINING_COMPANY","MOVING_STORAGE_COMPANY","OIL_NATURAL_GAS","PHARMACEUTICAL_COMPANY","PUBLIC_HEALTH_TECHNOLOGY_COMPANY","PUBLISHING_TECHNOLOGIES","REAL_ESTATE_AGENT","REAL_ESTATE_COMPANY","SERVICE_COMPANY","SOFTWARE_COMPANY","TAX_SERVICES","TELECOMMUNICATIONS","TRANSPORT_COMPANY","TRAVEL_AGENT","WEDDING_SERVICES","GENERAL_PRACTITIONER","SPECIALIST","RIDGE","AMBULANCE_UNIT","ROAD_RESCUE","AMUSEMENT_ARCADE","AMUSEMENT_PLACE","BETTING_STATION","FAIRGROUND","MUSIC_CENTER","CHECKPOINT","BAY","BRIDGE","BRIDGE_TUNNEL_OPERATIONS","CAPE","COVE","DAM","DUNE","ISLAND","LAGOON","LAKESHORE","LOCALE","MARSH","OASIS","PAN","PARKWAY","PLAIN_FLAT","PLATEAU","RAPIDS","REEF","RESERVOIR","RIVER_CROSSING","RIVER_SCENIC_AREA","ROCKS","SEASHORE","TUNNEL","VALLEY","WATER_HOLE","WELL","BUNGALOW_RENTAL","CABINS_LODGES","CHALET_RENTAL","COTTAGE_RENTAL","VILLA_RENTAL","BLOOD_BANK","HOSPITAL_FOR_WOMEN_AND_CHILDREN","HOSPITAL_OF_CHINESE_MEDICINE","SPECIAL_HOSPITAL","B_B_GUEST_HOUSE","HOSTEL","HOTEL","MOTEL","RESORT","QUARRY","AUTOMOBILE_MANUFACTURING","CHEMICAL_COMPANY","MANUFACTURING_COMPANY","MECHANICAL_ENGINEERING","MICROBREWERY","OEM","BOAT_LAUNCHING_RAMP","HARBOR","YACHT_BASIN","FARMERS_MARKET","FOOD_MARKET","INFORMAL_MARKET","PUBLIC_MARKET","PLANETARIUM","CABARET_THEATER","COMEDY_CLUB","DISCO_CLUB","JAZZ_CLUB","KARAOKE_CLUB","FISHING_HUNTING_AREA","FOREST_AREA","HISTORICAL_PARK","NATURAL_RECREATION_ATTRACTION","PARK","PICNIC_AREA","PRESERVE","RECREATION_AREA","WILDERNESS_AREA","OPEN_CAR_PARKING_AREA","DRUG_STORE","MARIJUANA_DISPENSARY","MEDICINAL_MARIJUANA_DISPENSARY","RECREATIONAL_MARIJUANA_DISPENSARY","COURIER_DROP_BOX","LOCAL_POST_OFFICE","PUBLIC_CALL_BOX","PUBLIC_TOILET","BUS_LINES","COACH_STOP","PASSENGER_TRANSPORT_TICKET_OFFICE","PEDESTRIAN_SUBWAY","STREETCAR_STOP","SUBWAY_STATION","INTERNATIONAL_RAILROAD_STATION","NATIONAL_RAILROAD_STATION","RAILROAD_SIDING","STATION_ACCESS","URBAN_STATION","BODYSHOP","CAR_GLASS_REPLACEMENT_SHOP","CAR_REPAIR_AND_SERVICE","HOME_APPLIANCE_REPAIR","MOTORCYCLE_REPAIR","OTHER_REPAIR_SHOPS","REPAIR_SHOP","TIRE_SERVICE","TRUCK_REPAIR_AND_SERVICE","AFGHAN_RESTAURANT","AFRICAN_RESTAURANT","ALGERIAN_RESTAURANT","AMERICAN_RESTAURANT","ARABIAN_RESTAURANT","ARGENTINIAN_RESTAURANT","ARMENIAN_RESTAURANT","ASIAN_RESTAURANT","AUSTRALIAN_RESTAURANT","AUSTRIAN_RESTAURANT","BANQUET_ROOMS","BARBECUE_RESTAURANT","BASQUE_RESTAURANT","BELGIAN_RESTAURANT","BISTRO","BOLIVIAN_RESTAURANT","BOSNIAN_RESTAURANT","BRAZILIAN_RESTAURANT","BRITISH_RESTAURANT","BUFFET_RESTAURANT","BULGARIAN_RESTAURANT","BURMESE_RESTAURANT","CAFETERIA","CALIFORNIAN_RESTAURANT","CAMBODIAN_RESTAURANT","CANADIAN_RESTAURANT","CARIBBEAN_RESTAURANT","CATERING_SERVICES","CHICKEN_RESTAURANT","CHILEAN_RESTAURANT","CHINESE_RESTAURANT","COLOMBIAN_RESTAURANT","CORSICAN_RESTAURANT","CREOLE_RESTAURANT","CREPERIE","CUBAN_RESTAURANT","CYPRIOT_RESTAURANT","CZECH_RESTAURANT","DANISH_RESTAURANT","DINNER_THEATER","DOMINICAN_RESTAURANT","DONGBEI_RESTAURANT","DOUGHNUT_RESTAURANT","DUTCH_RESTAURANT","EGYPTIAN_RESTAURANT","ENGLISH_RESTAURANT","EROTIC_RESTAURANT","ETHIOPIAN_RESTAURANT","EXOTIC_RESTAURANT","FAST_FOOD","FINNISH_RESTAURANT","FONDUE_RESTAURANT","FRENCH_RESTAURANT","FUSION_RESTAURANT","GERMAN_RESTAURANT","GREEK_RESTAURANT","GRILL_RESTAURANT","GUANGDONG_RESTAURANT","HAMBURGER_RESTAURANT","HAWAIIAN_RESTAURANT","HOT_POT_RESTAURANT","HUNAN_RESTAURANT","HUNGARIAN_RESTAURANT","ICE_CREAM_PARLOR","INDIAN_RESTAURANT","INDONESIAN_RESTAURANT","INTERNATIONAL_RESTAURANT","IRANIAN_RESTAURANT","IRISH_RESTAURANT","ISRAELI_RESTAURANT","ITALIAN_RESTAURANT","JAMAICAN_RESTAURANT","JAPANESE_RESTAURANT","JEWISH_RESTAURANT","KOREAN_RESTAURANT","KOSHER_RESTAURANT","LATIN_AMERICAN_RESTAURANT","LEBANESE_RESTAURANT","LUXEMBOURGIAN_RESTAURANT","MACROBIOTIC_RESTAURANT","MAGHRIB_RESTAURANT","MALTESE_RESTAURANT","MAURITIAN_RESTAURANT","MEDITERRANEAN_RESTAURANT","MEXICAN_RESTAURANT","MIDDLE_EASTERN_RESTAURANT","MONGOLIAN_RESTAURANT","MOROCCAN_RESTAURANT","MUSSELS_RESTAURANT","NEPALESE_RESTAURANT","NORWEGIAN_RESTAURANT","ORGANIC_FOOD_RESTAURANT","ORIENTAL_RESTAURANT","PAKISTANI_RESTAURANT","PERUVIAN_RESTAURANT","PHILIPPINE_RESTAURANT","PIZZERIA","POLISH_RESTAURANT","POLYNESIAN_RESTAURANT","PORTUGUESE_RESTAURANT","PROVENCAL_RESTAURANT","PUB_FOOD","ROADSIDE_RESTAURANT","ROMANIAN_RESTAURANT","RUSSIAN_RESTAURANT","SALAD_BAR","SANDWICH_RESTAURANT","SAVOY_RESTAURANT","SCANDINAVIAN_RESTAURANT","SCOTTISH_RESTAURANT","SEAFOOD","SHANDONG_RESTAURANT","SHANGHAI_RESTAURANT","SICHUAN_RESTAURANT","SICILIAN_RESTAURANT","SLAVIC_RESTAURANT","SLOVAK_RESTAURANT","SNACKS_RESTAURANT","SOUL_FOOD","SOUP_RESTAURANT","SPANISH_RESTAURANT","STEAK_HOUSE","SUDANESE_RESTAURANT","SURINAMESE_RESTAURANT","SUSHI_RESTAURANT","SWEDISH_RESTAURANT","SWISS_RESTAURANT","SYRIAN_RESTAURANT","TAIWANESE_RESTAURANT","TAKEOUT_FOOD","TAPAS_RESTAURANT","TEPPANYAKI_RESTAURANT","THAI_RESTAURANT","TIBETAN_RESTAURANT","TUNISIAN_RESTAURANT","TURKISH_RESTAURANT","URUGUAYAN_RESTAURANT","VEGETARIAN_RESTAURANT","VENEZUELAN_RESTAURANT","VIETNAMESE_RESTAURANT","WELSH_RESTAURANT","WESTERN_RESTAURANT","YOGURT_JUICE_BAR","ART_SCHOOL","CHILD_CARE_FACILITY","CULINARY_SCHOOL","DANCE_STUDIO_SCHOOL","DRIVING_SCHOOL","HIGH_SCHOOL","LANGUAGE_SCHOOL","MIDDLE_SCHOOL","PRE_SCHOOL","PRIMARY_SCHOOL","SENIOR_HIGH_SCHOOL","SPECIAL_SCHOOL","SPORT_SCHOOL","TECHNICAL_SCHOOL","VOCATIONAL_SCHOOL","AGRICULTURAL_SUPPLIES","ANTIQUE_ART_SHOP","BAGS_LEATHERWEAR","BAKERY","BEAUTY_SALON","BEAUTY_SUPPLIES","BOATING_EQUIPMENT_ACCESSORIES","BOOK_SHOP","BUTCHER","CAMERAS_PHOTOGRAPHY","CARPET_FLOOR_COVERINGS","CAR_ACCESSORIES","CHRISTMAS_HOLIDAY_SHOP","COMPUTER_COMPUTER_SUPPLIES","CONSTRUCTION_MATERIAL_EQUIPMENT","CONSUMER_ELECTRONICS","CONVENIENCE_STORE","CURTAINS_TEXTILES","C_DS_DVD_VIDEOS","DELICATESSEN","DO_IT_YOURSELF_CENTERS","DRIVE_THROUGH_BOTTLE_SHOP","DRY_CLEANER","ELECTRICAL_APPLIANCES_SHOP","FACTORY_OUTLET","FISHMONGER","FLORISTS","FOOTWEAR_SHOE_REPAIRS","FURNITURE_HOME_FURNISHINGS","GARDEN_CENTERS_SERVICES","GIFTS_CARDS_NOVELTIES_SOUVENIRS","GLASSWARE_CERAMIC_SHOP","GLASS_WINDOWS_STORE","GREENGROCER","GROCERY_STORE","HAIRDRESSER","HARDWARE_STORE","HOBBY_SHOP","HOUSE_GARDEN_FURNITURE_FITTINGS","JEWELRY_CLOCKS_WATCHES","KITCHENS_BATHROOMS","LAUNDRY","LIGHTING_SHOPS","LOCAL_SPECIALITIES_SHOP","LOTTERY_SHOP","MARINE_ELECTRONIC_EQUIPMENT","MEDICAL_SUPPLIES_EQUIPMENT","MOBILE_PHONE_SHOP","MUSIC_INSTRUMENTS_STORE","NAIL_SALON","NEWSAGENTS_TOBACCONISTS","OFFICE_EQUIPMENT","OPTICIAN","OTHER_FOOD_SHOPS","PAINTING_DECORATING","PAWN_SHOP","PERSONAL_CARE_FACILITY","PERSONAL_SERVICE","PET_SUPPLIES","PHOTOCOPY_SHOP","PHOTO_LAB_DEVELOPMENT","RECYCLING_SHOP","RETAIL_OUTLET","SAUNA_SOLARIUM_MASSAGE","SECURITY_PRODUCTS","SHOPPING_SERVICE","SPECIALTY_FOODS","SPORTS_EQUIPMENT_CLOTHING","STAMP_SHOP","TAILOR_SHOP","TOYS_GAMES_SHOP","VARIETY_STORE","VIDEO_RENTAL_SHOP","WHOLESALE_CLUB","WINE_SPIRITS","ATHLETICS_TRACK","BASEBALL_PARK","BASKETBALL_ARENA","BOWLING_CENTER","CRICKET_GROUND","FITNESS_CLUB_CENTER","FLYING_CLUB","HOCKEY_CLUB","HORSE_RACING_TRACK","HORSE_RIDING_CENTER","ICE_HOCKEY_ARENA","OTHER_WINTER_SPORT","RACE_TRACK","RUGBY_GROUND","SKI_RESORT","SNOOKER_POOL_BILLIARD","THEMATIC_SPORT_CENTER","FOOTBALL_STADIUM","MOTOR_RACING_STADIUM","MULTI_PURPOSE_STADIUM","NETBALL_STADIUM","SOCCER_STADIUM","STOCK_EXCHANGE","TAXI_LIMOUSINE_SHUTTLE_SERVICE","AMPHITHEATER","ARCH","BATTLEFIELD","CAVE","CEMETERY","HISTORIC_SITE","MAUSOLEUM_GRAVE","MEMORIAL","MINERAL_HOT_SPRINGS","MONUMENT","NATURAL_TOURIST_ATTRACTION","OBSERVATORY","STATUE","TOURIST_ATTRACTION","TOWER","ROAD_TRAFFIC_CONTROL_CENTER","ADVENTURE_VEHICLE_TRAIL","HIKING_TRAIL","HORSE_RIDING_TRAIL","MOUNTAIN_BIKE_TRAIL","ROCK_CLIMBING_TRAIL","APARTMENT_RENTAL","CONDOMINIUM_COMPLEX","FLATS_APARTMENT_COMPLEX","RESIDENTIAL_ESTATE","RETIREMENT_COMMUNITY","TOWNHOUSE_COMPLEX","ANIMAL_SERVICES","ANIMAL_SHELTER","WEIGH_SCALES","WILDLIFE_PARK","ZOO","toBaseMapPOICategory","MAP_BOLD_FONT","DEFAULT_MAX_PIN_SCALE","TITLE","ICON_ID","DEFAULT_TEXT_OFFSET_Y","DEFAULT_TEXT_OFFSET_X","DEFAULT_PLACE_ICON_ID","pinIconBaseLayout","pinIconBasePaint","pinTextBaseLayout","pinTextBasePaint","pinLayerBaseSpec","suffixNumber","text","numberToSuffix","calculateIconScale","image","theme","dimensions","svgElement","viewBox","getAttribute","parts","split","width","Number","parseFloat","height","naturalHeight","extractImageDimensions","heightScale","widthScale","hasChargingAvailability","chargingPark","availability","isEVStationWithAvailability","place","classifications","getChargingPointAvailability","chargingPointAvailability","available","statusCounts","Available","availableCount","totalCount","count","ratio","defaultFormatAvailabilityText","total","getAvailabilityRatio","getAvailabilityColorExpression","threshold","buildAnchorOffsets","topOffset","sideOffset","pinVerticalAdjustment","customTextOffset","hasCustomOffset","top","left","right","getTextOffset","iconSizeExpression","iconTextOffsetScales","iconScaleMultiplier","expression","Array","isArray","lastValue","at","extractMaxIconScale","isBaseMapTheme","fallbackSideOffset","fallbackOffsets","fallbackAnchorOffset","size","offsetCaseExpression","iconId","scales","offsets","buildLayoutConfig","layerName","textField","textConfig","customLayer","hasCustomIcons","baseLayout","offset","font","iconSize","Map","assign","buildPaintConfig","lightDark","textColor","baseTextColor","haloColor","baseHaloColor","getThemeAdaptiveTextColors","color","haloWidth","isClickEventState","getTextSizeSpec","textSize","replaceAll","hasEventState","SELECTED_COLOR","pinLayerSpec","selectedPinLayerSpec","withConfig","textFieldExpression","evAvailabilityEnabled","evAvailability","buildTextFieldExpression","title","buildPlacesLayerSpecs","styleLightDarkTheme","instanceIndex","customIcons","icon","categoryIcons","suffixedIconId","set","availabilityLevel","availabilitySuffixedId","buildCustomIconScalesMap","main","selected","poiLikeLayerSpec","poiLayer","buildPoiLikeLayerSpec","additional","defaultPin","supportedPinSubcategories","buildPlaceTitle","address","freeformAddress","toImageID","poiCategory","iconTheme","defaultPlaceIconID","categoryID","substring","toPinImageID","poiCategoriesToID","imageID","getIconIDForPlace","imageMapping","to","evAvailabilityIconID","requiredLevel","customIconWithAvailability","customIcon","getEVAvailabilityIconID","matchingCustomIcon","getPOILayerCategoryForPlace","toPlaces","places","mergeEVAvailabilityProps","extraFeatureProps","evAvailabilityConfig","hasEVStations","hasEVStationsWithAvailability","evAvailabilityText","formatText","buildAvailabilityText","evAvailabilityRatio","preparePlacesForDisplay","placesInput","mergedExtraFeatureProps","prop","bbox","iconID","_PlacesModule","get","lastInstanceIndex","layerIDPrefix","buildLayerSpecs","layerSpecTemplates","updateLayersAndData","previousShownFeatures","applyTheme","applyConfigPart","applyIconConfig","iconConfig","applyTextConfig","partialConfig","applyExtraFeatureProps","updateData","setupImages","newLayerSpecs","newLayerSpecsArray","oldLayerSpecsArray","pixelRatio","default","getShown","events","PlacesModule","isExpressionFilter","slice","getMergedAnyFilter","filters","legacy","getMergedAllFilter","filterToAdd","originalFilter","buildMappedValuesFilter","propName","showMode","comparator","filterArrayNew","buildValuesFilter","valuesMapping","poiCategoryGroups","FOOD_DRINKS_GROUP","SHOPPING_GROUP","TRANSPORTATION_GROUP","HEALTH_GROUP","PARKING_GROUP","HOLIDAY_TOURISM_GROUP","EV_CHARGING_STATIONS_GROUP","GAS_STATIONS_GROUP","ACCOMMODATION_GROUP","ENTERTAINMENT_GROUP","SPORTS_LEISURE_GROUP","EDUCATION_GROUP","GOVERNMENT_GROUP","getStyleCategories","categories","categoryIds","POIsModule","poiRuntimeSource","mainLayer","getFilter","setVisible","isVisible","filterCategories","categoriesFilter","poiFilter","layerGroupMappings","land","layerIDMatches","layerTypes","borders","water","buildings2D","buildings3D","houseNumbers","roadLines","roadLabels","roadShields","placeLabels","smallerTownLabels","cityLabels","capitalLabels","stateLabels","countryLabels","isMatching","group","part","toLowerCase","buildBaseMapLayerGroupFilter","layerGroupsFilter","layerGroups","groups","names","filterLayerByGroups","BaseMapModule","vectorTiles","layerGroupsVisibility","buildLayerGroupFilter","baseMapLayerGroupNames","DEFAULT_COLOR","OUTLINE_THEME_LINE_COLOR","TITLE_COLOR","TITLE_HALO_COLOR","geometryFillSpec","geometryOutlineSpec","buildGeometryLayerSpecs","fillLayerId","outlineLayerId","colorConfig","lineConfig","fillOpacity","lineColor","lineWidth","lineOpacity","buildGeometryTitleLayerSpec","buildGeometryLineLabelLayerSpec","lineLabelConfig","minZoom","symbolSpacing","textHaloColor","textHaloWidth","colorPalettes","warm","browns","cold","fadedBlues","blues","greens","fadedGreenToBlue","blueToRed","greenToYellow","pastel","retro","contrastRetro","fadedRainbow","pastelRainbow","colorPaletteIDs","prepareGeometryForDisplay","effectiveTheme","processedFeature","masked","mask","invertFeature","processedProps","buildTitle","palette","buildColor","prepareTitleForDisplay","geometries","coordinates","placeCoordinates","biggestPolygon","flat","reduce","result","coord","getLongestArray","bboxFromCoordsArray","bboxCenter","_GeometriesModule","lineLabelLayerSpecs","lastRawInput","titleSourceID","layerIdPrefix","fillLayerID","outlineLayerID","lineLabelLayerID","titleLayerID","titleLayerSpec","titleLayerSpecs","geometryFillLayerSpecs","geometryOutlineLayerSpecs","lineLabelSpec","geometryLabel","updateLayerAndData","beforeLayerConfig","moveBeforeLayer","moveBeforeLayerID","beforeLayerId","moveLayer","layerConfig","newTitleLayerSpecs","newLineLabelSpec","previousInput","transformFeaturesForDisplay","transformed","GeometriesModule","BUDGET_UNITS","timeMinutes","distanceKM","remainingChargeCPT","spentChargePCT","spentFuelLiters","buildReachableRangeFeatures","labels","labelled","donutGeo","difference","buildDonutFeatures","prepareReachableRangesForDisplay","label","budget","String","themedGeometryConfig","reachableRangeGeometryConfig","geometryThemes","HillshadeModule","hillshadeSource","hillshade","standardStyleIDs","styleModules","ROUTE_LINE_FOREGROUND_COLOR","ROUTE_LINE_OUTLINE_COLOR","DESELECTED_FOREGROUND_COLOR","DESELECTED_OUTLINE_COLOR","DESELECTED_SECONDARY_COLOR","ROUTE_LINE_FOREGROUND_WIDTH","SELECTED_ROUTE_FILTER","DESELECTED_ROUTE_FILTER","MAJOR_DELAY_COLOR","MODERATE_DELAY_COLOR","MINOR_DELAY_LABEL_COLOR","UNKNOWN_DELAY_COLOR","chargingStopTextField","chargingStopSymbol","commonProps","commonLineProps","instructionOutline","instructionLine","INSTRUCTION_ARROW_IMAGE_ID","instructionArrow","routeFerriesLine","routeFerriesSymbol","routeLineBaseTemplate","outlineLineWidth","routeDeselectedOutline","routeDeselectedLine","routeOutline","routeLineArrows","SELECTED_SUMMARY_POPUP_IMAGE_ID","DESELECTED_SUMMARY_POPUP_IMAGE_ID","routeTollRoadsOutline","routeTollRoadsSymbol","EXTRA_FOREGROUND_LINE_WIDTH","routeIncidentsBGLine","routeIncidentsDashedLine","magnitudeOfDelayTextColor","routeIncidentsSymbolBase","routeIncidentsJamSymbol","routeIncidentsCauseSymbol","routeTunnelsLine","routeVehicleRestrictedBackgroundLine","routeVehicleRestrictedDottedLine","TRAFFIC_CLEAR_IMAGE_ID","TRAFFIC_MAJOR_IMAGE_ID","TRAFFIC_MODERATE_IMAGE_ID","TRAFFIC_MINOR_IMAGE_ID","hasFormattedTraffic","buildSummaryBubbleSymbolPoint","selectedImageID","deselectedImageID","trafficClearID","trafficMajorID","trafficModerateID","trafficMinorID","summaryBubbleSymbolPoint","START_INDEX","MIDDLE_INDEX","FINISH_INDEX","INDEX_TYPE","STOP_DISPLAY_INDEX","WAYPOINT_START_IMAGE_ID","WAYPOINT_STOP_IMAGE_ID","WAYPOINT_SOFT_IMAGE_ID","WAYPOINT_FINISH_IMAGE_ID","pinIndexLabelLayout","waypointSymbols","waypointLabels","prefixBeforeID","startsWith","prefixBeforeIDs","suffixImageID","buildRoutingLayers","configLayers","configSectionLayers","sections","mainColor","mainLines","routeLine","props","waypoints","routeWaypointSymbol","routeWaypointLabel","chargingStops","routeChargingStopSymbol","incident","routeIncidentJamSymbol","routeIncidentCauseSymbol","routeIncidentBackgroundLine","routeIncidentDashedLine","ferry","routeFerryLine","routeFerrySymbol","tollRoad","routeTollRoadOutline","routeTollRoadSymbol","tunnel","routeTunnelLine","vehicleRestricted","routeVehicleRestrictedForegroundLine","instructionLines","routeInstructionLine","routeInstructionOutline","instructionArrows","routeInstructionArrowSymbol","summaryBubbles","routeSummaryBubbleSymbol","defaultRoutingLayers","instructionArrowIconImg","summaryMapBubbleImg","svg","summaryBubbleImageOptions","stretchX","stretchY","content","trafficImg","waypointIcon","foregroundSvg","svgOptions","appendChild","mapLayerSpecs","createLayersSpecs","layerConfigs","ferries","incidents","tollRoads","tunnels","routeModuleConfigWithDefaults","globalDisplayUnits","TomTomConfig","instance","displayUnits","getIconID","chargingStop","basedOn","chargingConnectionInfo","chargingSpeed","formatTitle","chargingParkName","chargingParkOperatorName","toDisplayChargingStops","routes","displayChargingStops","route","leg","summary","chargingInformationAtEndOfLeg","chargingParkId","chargingPower","chargingPowerInkW","chargingDuration","formatDuration","chargingTimeInSeconds","time","routeState","hasJam","sectionProps","toDisplayTrafficSectionProps","jamIconID","toTrafficJamIconSuffix","toJamIconID","causeIconID","toCauseIconID","getImageIDForWaypoint","waypoint","indexType","radiusMeters","baseImageID","toDisplayWaypoints","hardWaypointIndex","waypointInput","asWaypoint","arrayLength","indexTypeFor","hardWaypoint","isHardWaypoint","placeProperties","buildWaypointTitle","entryPoints","getPosition","useEntryPoint","stopDisplayIndex","toDisplayRouteSections","sectionType","displaySectionPropsBuilder","startPointIndex","endPointIndex","routeIndex","buildRouteSectionsFromRoute","showFeaturesWithRouteSelection","routesWithSelection","toDisplayRoutes","selectedIndex","routesCollection","hasMagnitude","magnitude","section","toDisplayRouteSummaries","routeCoordinates","formattedTraffic","trafficDelayInSeconds","trafficSections","traffic","summaryDelayMagnitude","Math","round","formattedDistance","formatDistance","lengthInMeters","distance","formattedDuration","travelTimeInSeconds","_RoutingModule","createSourcesWithLayers","layersSpecs","sourcePrefix","routingSourcesWithLayers","svgIconOptions","waypointStartImageId","waypointStopImageId","waypointSoftImageId","waypointFinishImageId","instructionArrowImageId","selectedSummaryPopupImageId","deselectedSummaryPopupImageId","trafficClearImageId","trafficMajorImageId","trafficModerateImageId","trafficMinorImageId","addImageIfNotExisting","waypointStartIcon","softWaypointIcon","waypointFinishIcon","customChargingStopIcon","mergedConfig","newLayersSpecs","layersSpecID","oldLayersSpecs","newLayersMap","acc","cur","oldLayersMap","layersToRemove","newLayersToUpdate","oldLayersToUpdate","removeLayer","toBeAddedLayerSpec","updateLayersAndSource","listOfSources","summaryBubblesVisible","visibilityChanged","isEqual","displayUnitsChanged","hasSummaryBubbles","hasRoutes","previouslyShown","entry","item","showRoutes","displayRoutes","guidance","instructions","instruction","routePath","pathPoint","toDisplayInstructions","instructionLastSegment","lastPointBearingDegrees","bearing","toDisplayInstructionArrows","clearRoutes","selectRoute","updatedRoutes","showWaypoints","displayWaypoints","clearWaypoints","getLayerToRenderLinesUnder","RoutingModule","DEFAULT_STANDARD_STYLE_ID","standardStyleModulesValues","standardLight","trafficIncidents","trafficFlow","standardDark","drivingLight","drivingDark","monoLight","monoDark","satellite","baseMapStyleUrlTemplate","suffix","baseMapStyleUrlTemplates","buildStandardStyleUrl","baseUrl","apiKey","styleURL","URL","replace","version","module","searchParams","append","buildStyleInput","mapParams","commonBaseURL","url","givenUrl","withApiKey","json","TomTomMap","tomtomMapParams","params","styleChangeHandlers","keepState","e","effectiveStyle","previousStyle","withPreviousStyleParts","_params","handleStyleData","mergeFromGlobal","ensureMapLibreCSSLoaded","setWorkerCount","validateStyle","maxTileCacheZoomLevels","cancelPendingTileRequestsWhileZooming","mapLibre","attributionControl","compact","transformRequest","resourceType","parsedUrl","pathname","join","headers","generateTomTomHeaders","loadRTLTextPlugin","getRTLTextPluginStatus","setRTLTextPlugin","catch","from","querySelectorAll","textContent","link","rel","href","maplibreVersion","head","_setLanguage","language","mapLanguage","isLayerLocalizable","textFieldValue","setLanguage","getBBox","getBounds","toArray","setSprite","addPinCategoriesSpriteToStyle","toMultiSyntaxAllFilter","newSyntaxExpressions","legacySyntaxExpressions","addFilter","addValuesFilter","valuesFilter","addCommonFilterExpressions","sdkFilter","roadCategories","roadSubCategories","buildMapLibreIncidentsFilter","incidentCategories","incidentCategoryFilter","magnitudes","magnitudesFilter","indexOf","delays","delayFilter","mustHaveDelay","minDelayMinutes","delaySeconds","delayFilterToMapLibre","buildMapLibreIncidentFilters","incidentFilters","any","mapLibreFilters","mapLibreFilter","buildMapLibreFlowFilter","showRoadClosures","operator","applyFilter","originalFilters","TrafficFlowModule","flowSource","getLayers","_filter","updateConfig","filterExpression","flowFilters","buildMapLibreFlowFilters","omitBy","TrafficIncidentsModule","incidentsSource","_setVisible","icons","setIconsVisible","iconFilters","incidentFilterExpression","getNonSymbolLayers","iconFilterExpression","getSymbolLayers","anyIconLayersVisible","streetRoadSubCategories","serviceRoadSubCategories","getRelativeElementBounds","elementRect","containerRect","bottom","isElementOutsideContainer","bounds","containerWidth","containerHeight","adjustForHorizontalBar","currentVisible","max","min","adjustForVerticalBar","adjustVisibleBoundsForElement","mapWidthPX","mapHeightPX","clampedBounds","clampBoundsToContainer","spansFullWidth","spansFullHeight","touchesLeft","touchesRight","touchesTop","touchesBottom","distanceFromLeft","distanceFromRight","distanceFromTop","distanceFromBottom","minDistance","adjustForFloatingElement","toElement","ref","getMapLibreMap","calculateVisibleAreaBounds","surroundingElements","paddingPX","getContainer","getBoundingClientRect","visibleAreaBounds","elementRef","elementBounds","calculatePaddedBBox","sw","unproject","ne","lng","lat","calculatePaddedCenter","west","south","east","north","calculateFittingBBox","toBeContainedBBox","leftRatio","rightRatio","topRatio","widthRatio","heightRatio","fullWidth","fullHeight","expandedWest","expandedNorth"],"mappings":"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"}