UNPKG

map-gl-indoor

Version:

A MapGL plugin to visualize multi-level buildings

1,823 lines (1,822 loc) 43.6 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class IndoorControl { constructor() { __publicField(this, "_map"); __publicField(this, "_indoor"); __publicField(this, "_indoorMap"); __publicField(this, "_container"); __publicField(this, "_levelsButtons"); __publicField(this, "_selectedButton"); __publicField(this, "_onMapLoaded", ({ indoorMap }) => { this._indoorMap = indoorMap; this._updateNavigationBar(); this._setSelected(this._indoor.getLevel()); }); __publicField(this, "_onMapUnLoaded", () => { this._indoorMap = null; this._updateNavigationBar(); }); __publicField(this, "_onLevelChanged", ({ level }) => this._setSelected(level)); this._levelsButtons = []; this._selectedButton = null; this._indoorMap = null; } onAdd(map) { if (map.indoor === void 0) { throw Error("call addIndoorTo(map) before creating the IndoorControl"); } this._map = map; this._indoor = this._map.indoor; const container = this._container = document.createElement("div"); container.classList.add("mapboxgl-ctrl"); container.classList.add("mapboxgl-ctrl-group"); container.style.display = "none"; container.addEventListener("contextmenu", this._onContextMenu); this._indoorMap = this._indoor.getSelectedMap(); if (this._indoor.getSelectedMap() !== null) { this._updateNavigationBar(); this._setSelected(this._indoor.getLevel()); } this._map.on("indoor.map.loaded", this._onMapLoaded); this._map.on("indoor.map.unloaded", this._onMapUnLoaded); this._map.on("indoor.level.changed", this._onLevelChanged); return container; } onRemove() { var _a, _b, _c, _d, _e; (_a = this._container) == null ? void 0 : _a.removeEventListener("contextmenu", this._onContextMenu); (_b = this._container) == null ? void 0 : _b.remove(); delete this._container; (_c = this._map) == null ? void 0 : _c.off("indoor.map.loaded", this._onMapLoaded); (_d = this._map) == null ? void 0 : _d.off("indoor.map.unloaded", this._onMapUnLoaded); (_e = this._map) == null ? void 0 : _e.off("indoor.level.changed", this._onLevelChanged); delete this._map; } _updateNavigationBar() { if (!this._container) { return; } if (this._indoorMap === null) { this._container.style.display = "none"; return; } this._container.style.display = "block"; this._levelsButtons = []; while (this._container.firstChild) { this._container.removeChild(this._container.firstChild); } const range = this._indoorMap.levelsRange; for (let i = range.max; i >= range.min; i--) { this._levelsButtons[i] = this._createLevelButton(this._container, i); } } _setSelected(level) { if (this._levelsButtons.length === 0) { return; } if (this._selectedButton) { this._selectedButton.style.fontWeight = "normal"; } if (level !== null && this._levelsButtons[level]) { this._levelsButtons[level].style.fontWeight = "bold"; this._selectedButton = this._levelsButtons[level]; } } _createLevelButton(container, level) { const a = document.createElement("button"); a.innerHTML = level.toString(); a.classList.add("mapboxgl-ctrl-icon"); container.appendChild(a); a.addEventListener("click", () => { var _a; (_a = this._map) == null ? void 0 : _a.fire("indoor.control.clicked", { level }); if (this._indoor.getLevel() === level) return; this._indoor.setLevel(level); }); return a; } _onContextMenu(e) { e.preventDefault(); } } var earthRadius = 63710088e-1; var factors = { centimeters: earthRadius * 100, centimetres: earthRadius * 100, degrees: earthRadius / 111325, feet: earthRadius * 3.28084, inches: earthRadius * 39.37, kilometers: earthRadius / 1e3, kilometres: earthRadius / 1e3, meters: earthRadius, metres: earthRadius, miles: earthRadius / 1609.344, millimeters: earthRadius * 1e3, millimetres: earthRadius * 1e3, nauticalmiles: earthRadius / 1852, radians: 1, yards: earthRadius * 1.0936 }; function feature(geom, properties, options) { if (options === void 0) { options = {}; } var feat = { type: "Feature" }; if (options.id === 0 || options.id) { feat.id = options.id; } if (options.bbox) { feat.bbox = options.bbox; } feat.properties = properties || {}; feat.geometry = geom; return feat; } function point(coordinates, properties, options) { if (options === void 0) { options = {}; } if (!coordinates) { throw new Error("coordinates is required"); } if (!Array.isArray(coordinates)) { throw new Error("coordinates must be an Array"); } if (coordinates.length < 2) { throw new Error("coordinates must be at least 2 numbers long"); } if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) { throw new Error("coordinates must contain numbers"); } var geom = { type: "Point", coordinates }; return feature(geom, properties, options); } function radiansToLength(radians, units) { if (units === void 0) { units = "kilometers"; } var factor = factors[units]; if (!factor) { throw new Error(units + " units is invalid"); } return radians * factor; } function lengthToRadians(distance2, units) { if (units === void 0) { units = "kilometers"; } var factor = factors[units]; if (!factor) { throw new Error(units + " units is invalid"); } return distance2 / factor; } function radiansToDegrees(radians) { var degrees = radians % (2 * Math.PI); return degrees * 180 / Math.PI; } function degreesToRadians(degrees) { var radians = degrees % 360; return radians * Math.PI / 180; } function isNumber(num) { return !isNaN(num) && num !== null && !Array.isArray(num); } function getCoord(coord) { if (!coord) { throw new Error("coord is required"); } if (!Array.isArray(coord)) { if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") { return coord.geometry.coordinates; } if (coord.type === "Point") { return coord.coordinates; } } if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) { return coord; } throw new Error("coord must be GeoJSON Point or an Array of numbers"); } function distance(from, to, options) { if (options === void 0) { options = {}; } var coordinates1 = getCoord(from); var coordinates2 = getCoord(to); var dLat = degreesToRadians(coordinates2[1] - coordinates1[1]); var dLon = degreesToRadians(coordinates2[0] - coordinates1[0]); var lat1 = degreesToRadians(coordinates1[1]); var lat2 = degreesToRadians(coordinates2[1]); var a = Math.pow(Math.sin(dLat / 2), 2) + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2); return radiansToLength(2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)), options.units); } function overlap(bounds1, bounds2) { if (bounds1[0] > bounds2[2] || bounds2[0] > bounds1[2]) { return false; } if (bounds1[3] < bounds2[1] || bounds2[3] < bounds1[1]) { return false; } return true; } function filterWithLevel(initialFilter, level, showFeaturesWithEmptyLevel = false) { return [ "all", initialFilter, [ "any", showFeaturesWithEmptyLevel ? ["!", ["has", "level"]] : false, [ "all", [ "has", "level" ], [ "any", [ "==", ["get", "level"], level.toString() ], [ "all", [ "!=", [ "index-of", ";", ["get", "level"] ], -1 ], [ ">=", level, [ "to-number", [ "slice", ["get", "level"], 0, [ "index-of", ";", ["get", "level"] ] ] ] ], [ "<=", level, [ "to-number", [ "slice", ["get", "level"], [ "+", [ "index-of", ";", ["get", "level"] ], 1 ] ] ] ] ] ] ] ] ]; } function bboxCenter(bbox2) { const [west, south, east, north] = bbox2; return [(west + east) / 2, (south + north) / 2]; } function bboxContains(bbox2, point2) { const [west, south, east, north] = bbox2; const [lng, lat] = point2; const containsLatitude = south <= lat && lat <= north; let containsLongitude = west <= lng && lng <= east; if (west > east) { containsLongitude = west >= lng && lng >= east; } return containsLatitude && containsLongitude; } const SOURCE_ID = "indoor"; class IndoorLayer { constructor(map) { __publicField(this, "_map"); __publicField(this, "_level"); __publicField(this, "_indoorMaps"); __publicField(this, "_selectedMap"); __publicField(this, "_previousSelectedMap"); __publicField(this, "_previousSelectedLevel"); __publicField(this, "_savedFilters"); __publicField(this, "_mapLoadedPromise"); __publicField(this, "_updateMapPromise"); this._map = map; this._level = null; this._indoorMaps = []; this._savedFilters = []; this._selectedMap = null; this._previousSelectedMap = null; this._previousSelectedLevel = null; this._updateMapPromise = Promise.resolve(); if (this._map.loaded()) { this._mapLoadedPromise = Promise.resolve(); } else { this._mapLoadedPromise = new Promise((resolve) => this._map.on("load", resolve)); } this._map.on("moveend", () => this._updateSelectedMapIfNeeded()); } getSelectedMap() { return this._selectedMap; } getLevel() { return this._level; } setLevel(level, fireEvent = true) { if (this._selectedMap === null) { throw new Error("Cannot set level, no map has been selected"); } this._level = level; this._updateFiltering(); if (fireEvent) { this._map.fire("indoor.level.changed", { level }); } } _addLayerForFiltering(layer, beforeLayerId) { this._map.addLayer(layer, beforeLayerId); this._savedFilters.push({ layerId: layer.id, filter: this._map.getFilter(layer.id) || ["all"] }); } addLayerForFiltering(layer, beforeLayerId) { this._addLayerForFiltering(layer, beforeLayerId); this._updateFiltering(); } _removeLayerForFiltering(layerId) { this._savedFilters = this._savedFilters.filter(({ layerId: id }) => layerId !== id); this._map.removeLayer(layerId); } removeLayerForFiltering(layerId) { this._removeLayerForFiltering(layerId); this._updateFiltering(); } _updateFiltering() { const level = this._level; let filterFn; if (level !== null) { const showFeaturesWithEmptyLevel = this._selectedMap ? this._selectedMap.showFeaturesWithEmptyLevel : false; filterFn = (filter) => filterWithLevel(filter, level, showFeaturesWithEmptyLevel); } else { filterFn = (filter) => filter; } this._savedFilters.forEach(({ layerId, filter }) => this._map.setFilter(layerId, filterFn(filter))); } async addMap(map) { this._indoorMaps.push(map); await this._updateSelectedMapIfNeeded(); } async removeMap(map) { this._indoorMaps = this._indoorMaps.filter((_indoorMap) => _indoorMap !== map); await this._updateSelectedMapIfNeeded(); } async _updateSelectedMapIfNeeded() { await this._mapLoadedPromise; await this._updateMapPromise; this._updateMapPromise = (async () => { const closestMap = this._closestMap(); if (closestMap !== this._selectedMap) { this._updateSelectedMap(closestMap); } })(); await this._updateMapPromise; } _updateSelectedMap(indoorMap) { const previousMap = this._selectedMap; if (previousMap !== null) { previousMap.layersToHide.forEach((layerId) => this._map.setLayoutProperty(layerId, "visibility", "visible")); previousMap.layers.forEach(({ id }) => this._removeLayerForFiltering(id)); this._map.removeSource(SOURCE_ID); if (!indoorMap) { this._previousSelectedLevel = this._level; this._previousSelectedMap = previousMap; } this.setLevel(null, false); this._map.fire("indoor.map.unloaded", { indoorMap: previousMap }); } this._selectedMap = indoorMap; if (!indoorMap) { return; } const { geojson, layers: layers2, levelsRange, beforeLayerId } = indoorMap; this._map.addSource(SOURCE_ID, { type: "geojson", data: geojson }); layers2.forEach((layer) => this._addLayerForFiltering(layer, beforeLayerId)); indoorMap.layersToHide.forEach((layerId) => this._map.setLayoutProperty(layerId, "visibility", "none")); const level = this._previousSelectedMap === indoorMap ? this._previousSelectedLevel : Math.max(Math.min(indoorMap.defaultLevel, levelsRange.max), levelsRange.min); this.setLevel(level, false); this._map.fire("indoor.map.loaded", { indoorMap }); } _closestMap() { if (this._map.getZoom() < 17) { return null; } const cameraBounds = this._map.getBounds(); const cameraBoundsTurf = [ cameraBounds.getWest(), cameraBounds.getSouth(), cameraBounds.getEast(), cameraBounds.getNorth() ]; const mapsInBounds = this._indoorMaps.filter((indoorMap) => overlap(indoorMap.bounds, cameraBoundsTurf)); if (mapsInBounds.length === 0) { return null; } if (mapsInBounds.length === 1) { return mapsInBounds[0]; } let minDist = Number.POSITIVE_INFINITY; let closestMap = mapsInBounds[0]; for (const map of mapsInBounds) { const _dist = distance(bboxCenter(map.bounds), bboxCenter(cameraBoundsTurf)); if (_dist < minDist) { closestMap = map; minDist = _dist; } } return closestMap; } } var defaultLayers = [ { filter: [ "any", [ "has", "building" ], [ "has", "building:part" ] ], id: "buildings-background", type: "fill", source: "indoor", paint: { "fill-color": "#E6E4E0", "fill-opacity": { base: 1, stops: [ [ 16.5, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "indoor", "level" ], id: "level-background", type: "fill", source: "indoor", paint: { "fill-color": "#E6E4E0", "fill-opacity": { base: 1, stops: [ [ 16.5, 0 ], [ 18, 1 ] ] } } }, { id: "indoor-gardens", type: "fill", source: "indoor", filter: [ "filter-==", "leisure", "garden" ], layout: { visibility: "visible" }, paint: { "fill-color": "#cde8a2", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "amenity", "parking" ], id: "indoor-parkings", type: "fill", source: "indoor", paint: { "fill-color": "#D7CCC8", "fill-outline-color": "#000000", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "amenity", "parking" ], id: "indoor-parkings-patterns", type: "fill", source: "indoor", paint: { "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 0.1 ] ] }, "fill-pattern": "si-main-3", "fill-translate-anchor": "viewport" } }, { filter: [ "filter-==", "indoor", "corridor" ], id: "indoor-corridors", type: "fill", source: "indoor", paint: { "fill-color": "#D7CCC8", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "any", [ "filter-in-small", "indoor", [ "literal", [ "room", "area" ] ] ], [ "filter-==", "railway", "platform" ] ], id: "indoor-rooms", type: "fill", source: "indoor", paint: { "fill-color": "#A1887F", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "any", [ "filter-==", "indoor", "room" ] ], id: "indoor-rooms-borders", type: "line", source: "indoor", paint: { "line-color": "#000", "line-width": 1, "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "indoor", "area" ], id: "indoor-areas", type: "fill", source: "indoor", paint: { "fill-color": "#D7CCC8", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "all", [ "filter-==", "highway", "pedestrian" ], [ "has", "level" ] ], id: "indoor-highways-area", type: "fill", source: "indoor", paint: { "fill-color": { base: 1, stops: [ [ 16, "hsl(230, 16%, 94%)" ], [ 16.25, "hsl(230, 50%, 98%)" ] ] }, "fill-outline-color": "hsl(230, 26%, 88%)", "fill-opacity": 1 } }, { filter: [ "all", [ "filter-==", "highway", "pedestrian" ], [ "has", "level" ] ], id: "indoor-highways-area-pattern", type: "fill", source: "indoor", paint: { "fill-color": "hsl(0, 0%, 100%)", "fill-outline-color": "hsl(35, 10%, 83%)", "fill-pattern": "pedestrian-polygon", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "all", [ "filter-==", "indoor", "area" ], [ "filter-==", "balcony", "yes" ] ], id: "indoor-balcony", type: "fill", source: "indoor", paint: { "fill-color": "#BDBDBD", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "any", [ "filter-==", "stairs", "yes" ], [ "filter-==", "elevator", "yes" ], [ "filter-==", "highway", "elevator" ] ], id: "indoor-stairs", type: "fill", source: "indoor", paint: { "fill-color": "#7B635A", "fill-outline-color": "#000000", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "indoor", "wall" ], id: "indoor-walls", type: "line", source: "indoor", paint: { "line-color": "#000000", "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "has", "barrier" ], id: "indoor-barriers", type: "line", source: "indoor", paint: { "line-color": "#000000", "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "indoor", "block" ], id: "indoor-blocks", type: "fill", source: "indoor", paint: { "fill-color": "#000000", "fill-opacity": { base: 1, stops: [ [ 17, 0 ], [ 18, 1 ] ] } } }, { filter: [ "filter-==", "handrail", "yes" ], id: "indoor-handrail", type: "line", source: "indoor", paint: { "line-color": "#000000", "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 19, 1 ] ] } } }, { filter: [ "filter-==", "railway", "rail" ], id: "indoor-rails", type: "line", source: "indoor", paint: { "line-color": "hsl(230, 10%, 74%)", "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 19, 1 ] ] } } }, { filter: [ "filter-==", "railway", "rail" ], id: "indoor-rails-tracks", type: "line", source: "indoor", paint: { "line-color": "hsl(230, 10%, 74%)", "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 19, 1 ] ] }, "line-width": { base: 1.5, stops: [ [ 14, 4 ], [ 20, 8 ] ] }, "line-dasharray": [ 0.1, 15 ] } }, { filter: [ "any", [ "filter-in-small", "indoor", [ "literal", [ "table", "cupboard", "chair", "kitchen", "sofa", "tv", "shelf", "furniture-item" ] ] ], [ "filter-==", "trashcan", "yes" ], [ "filter-==", "copier", "yes" ], [ "filter-==", "amenity", "vending_machine" ] ], id: "indoor-furniture", type: "fill", source: "indoor", paint: { "fill-color": "#000", "fill-outline-color": "#000", "fill-opacity": { base: 1, stops: [ [ 18, 0 ], [ 19, 0.2 ] ] } } }, { id: "indoor-steps", paint: { "line-width": { base: 1.5, stops: [ [ 17, 1 ], [ 18, 1.6 ], [ 19, 6 ] ] }, "line-color": "hsl(0, 0%, 100%)", "line-dasharray": { base: 1, stops: [ [ 17, [ 1, 0 ] ], [ 17.5, [ 1.75, 1 ] ], [ 18, [ 1, 0.75 ] ], [ 19, [ 0.3, 0.3 ] ] ] }, "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 17.25, 1 ] ] } }, type: "line", source: "indoor", filter: [ "all", [ "filter-==", "highway", "steps" ], [ "!", [ "has", "conveying" ] ] ], layout: { "line-join": "round" } }, { id: "indoor-conveying", paint: { "line-width": { base: 1.5, stops: [ [ 17, 1 ], [ 18, 1.6 ], [ 19, 6 ] ] }, "line-color": "#FF0000", "line-dasharray": { base: 1, stops: [ [ 17, [ 1, 0 ] ], [ 17.5, [ 1.75, 1 ] ], [ 18, [ 1, 0.75 ] ], [ 19, [ 0.3, 0.3 ] ] ] }, "line-opacity": { base: 1, stops: [ [ 17, 0 ], [ 17.25, 1 ] ] } }, type: "line", source: "indoor", filter: [ "all", [ "filter-==", "highway", "steps" ], [ "has", "conveying" ] ], layout: { "line-join": "round" } }, { interactive: true, minzoom: 17, layout: { "text-line-height": 1.2, "text-size": { base: 1, stops: [ [ 17, 10 ], [ 20, 12 ] ] }, "text-allow-overlap": false, "text-ignore-placement": false, "text-max-angle": 38, "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], "symbol-placement": "point", "text-padding": 2, visibility: "visible", "text-rotation-alignment": "viewport", "text-anchor": "center", "text-field": "{name}", "text-letter-spacing": 0.02, "text-max-width": 8 }, filter: [ "filter-==", "indoor", "room" ], type: "symbol", source: "indoor", id: "poi-indoor-text-ref", paint: { "text-color": "#65513d", "text-halo-color": "#ffffff", "text-halo-width": 1, "text-opacity": { base: 1, stops: [ [ 18, 0 ], [ 18.5, 0.5 ], [ 19, 1 ] ] } } }, { interactive: true, minzoom: 17, layout: { "text-line-height": 1.2, "icon-size": { base: 1, stops: [ [ 17, 0.5 ], [ 20, 1 ] ] }, "text-size": { base: 1, stops: [ [ 17, 11 ], [ 20, 13 ] ] }, "text-allow-overlap": false, "icon-image": "{maki}-15", "icon-anchor": "center", "text-ignore-placement": false, "text-max-angle": 38, "symbol-spacing": 250, "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], "symbol-placement": "point", "text-padding": 2, visibility: "visible", "text-offset": [ 0, 1 ], "icon-optional": false, "text-rotation-alignment": "viewport", "text-anchor": "top", "text-field": "{name}", "text-letter-spacing": 0.02, "text-max-width": 8, "icon-allow-overlap": true }, filter: [ "boolean", false ], type: "symbol", source: "indoor", id: "poi-indoor", paint: { "text-color": "#65513d", "text-halo-color": "#ffffff", "text-halo-width": 1, "text-opacity": { base: 1, stops: [ [ 17, 0 ], [ 17.5, 0.5 ], [ 19, 1 ] ] }, "icon-opacity": { base: 1, stops: [ [ 17, 0 ], [ 17.5, 0.5 ], [ 19, 1 ] ] } } } ]; let layers = defaultLayers; const POI_LAYER_ID = "poi-indoor"; const OSM_FILTER_MAPBOX_MAKI_LIST = [ { filter: ["filter-==", "amenity", "fast_food"], maki: "fast-food" }, { filter: ["filter-==", "amenity", "restaurant"], maki: "restaurant" }, { filter: ["filter-==", "amenity", "cafe"], maki: "cafe" }, { filter: ["filter-in-small", "amenity", ["literal", ["bank", "vending_machine"]]], maki: "bank" }, { filter: ["filter-==", "amenity", "toilets"], maki: "toilet" }, { filter: ["any", ["filter-==", "highway", "elevator"], ["has", "elevator"]], maki: "triangle-stroked" }, { filter: ["filter-==", "natural", "tree"], maki: "park" }, { filter: ["filter-==", "shop", "travel_agency"], maki: "suitcase" }, { filter: ["filter-==", "shop", "convenience"], maki: "grocery" }, { filter: ["filter-==", "shop", "bakery"], maki: "bakery" }, { filter: ["filter-==", "shop", "chemist"], maki: "pharmacy" }, { filter: ["filter-==", "shop", "clothes"], maki: "clothing-store" }, { filter: ["filter-==", "highway", "steps"], maki: "entrance" } ]; function createPoiLayers(metaLayer) { const otherShopsEntry = { filter: [ "all", ["has", "shop"], [ "!", [ "filter-in-small", "shop", [ "literal", OSM_FILTER_MAPBOX_MAKI_LIST.filter((val) => val.filter[1] === "shop").map((val) => val.filter[2]) ] ] ] ], maki: "shop" }; return OSM_FILTER_MAPBOX_MAKI_LIST.concat(otherShopsEntry).map((poi) => { const newLayer = Object.assign({}, metaLayer); newLayer.id += `-${poi.maki}`; newLayer.filter = poi.filter; newLayer.layout = Object.assign({}, metaLayer.layout); newLayer.layout["icon-image"] = `${poi.maki}-15`; return newLayer; }); } const poiLayer = layers.find((layer) => layer.id === POI_LAYER_ID); if (poiLayer) { createPoiLayers(poiLayer).forEach((_layer) => layers.push(_layer)); layers = layers.filter((layer) => layer.id !== POI_LAYER_ID); } var DefaultLayers = layers; var Style = { DefaultLayers }; function coordEach(geojson, callback, excludeWrapCoord) { if (geojson === null) return; var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1; for (var featureIndex = 0; featureIndex < stop; featureIndex++) { geometryMaybeCollection = isFeatureCollection ? geojson.features[featureIndex].geometry : isFeature ? geojson.geometry : geojson; isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false; stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1; for (var geomIndex = 0; geomIndex < stopG; geomIndex++) { var multiFeatureIndex = 0; var geometryIndex = 0; geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection; if (geometry === null) continue; coords = geometry.coordinates; var geomType = geometry.type; wrapShrink = excludeWrapCoord && (geomType === "Polygon" || geomType === "MultiPolygon") ? 1 : 0; switch (geomType) { case null: break; case "Point": if (callback(coords, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; coordIndex++; multiFeatureIndex++; break; case "LineString": case "MultiPoint": for (j = 0; j < coords.length; j++) { if (callback(coords[j], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; coordIndex++; if (geomType === "MultiPoint") multiFeatureIndex++; } if (geomType === "LineString") multiFeatureIndex++; break; case "Polygon": case "MultiLineString": for (j = 0; j < coords.length; j++) { for (k = 0; k < coords[j].length - wrapShrink; k++) { if (callback(coords[j][k], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; coordIndex++; } if (geomType === "MultiLineString") multiFeatureIndex++; if (geomType === "Polygon") geometryIndex++; } if (geomType === "Polygon") multiFeatureIndex++; break; case "MultiPolygon": for (j = 0; j < coords.length; j++) { geometryIndex = 0; for (k = 0; k < coords[j].length; k++) { for (l = 0; l < coords[j][k].length - wrapShrink; l++) { if (callback(coords[j][k][l], coordIndex, featureIndex, multiFeatureIndex, geometryIndex) === false) return false; coordIndex++; } geometryIndex++; } multiFeatureIndex++; } break; case "GeometryCollection": for (j = 0; j < geometry.geometries.length; j++) if (coordEach(geometry.geometries[j], callback, excludeWrapCoord) === false) return false; break; default: throw new Error("Unknown Geometry Type"); } } } } function bbox(geojson) { var result = [Infinity, Infinity, -Infinity, -Infinity]; coordEach(geojson, function(coord) { if (result[0] > coord[0]) { result[0] = coord[0]; } if (result[1] > coord[1]) { result[1] = coord[1]; } if (result[2] < coord[0]) { result[2] = coord[0]; } if (result[3] < coord[1]) { result[3] = coord[1]; } }); return result; } bbox["default"] = bbox; class GeoJsonHelper { static extractLevelFromFeature(feature2) { if (!!feature2.properties && feature2.properties.level !== null) { const propertyLevel = feature2.properties["level"]; if (typeof propertyLevel === "string") { const splitLevel = propertyLevel.split(";"); if (splitLevel.length === 1) { const level = parseFloat(propertyLevel); if (!isNaN(level)) { return level; } } else if (splitLevel.length === 2) { const level1 = parseFloat(splitLevel[0]); const level2 = parseFloat(splitLevel[1]); if (!isNaN(level1) && !isNaN(level2)) { return { min: Math.min(level1, level2), max: Math.max(level1, level2) }; } } } } return null; } static extractLevelsRangeAndBounds(geojson) { let minLevel = Infinity; let maxLevel = -Infinity; const bounds = bbox(geojson); const parseFeature = (feature2) => { const level = this.extractLevelFromFeature(feature2); if (level === null) { return; } if (typeof level === "number") { minLevel = Math.min(minLevel, level); maxLevel = Math.max(maxLevel, level); } else if (typeof level === "object") { minLevel = Math.min(minLevel, level.min); maxLevel = Math.max(maxLevel, level.max); } }; if (geojson.type === "FeatureCollection") { geojson.features.forEach(parseFeature); } if (minLevel === Infinity || maxLevel === -Infinity) { throw new Error("No level found"); } return { levelsRange: { min: minLevel, max: maxLevel }, bounds }; } } class IndoorMap { constructor(bounds, geojson, layers2, levelsRange, layersToHide, defaultLevel, showFeaturesWithEmptyLevel, beforeLayerId) { __publicField(this, "bounds"); __publicField(this, "geojson"); __publicField(this, "layers"); __publicField(this, "levelsRange"); __publicField(this, "beforeLayerId"); __publicField(this, "layersToHide"); __publicField(this, "defaultLevel"); __publicField(this, "showFeaturesWithEmptyLevel"); this.bounds = bounds; this.geojson = geojson; this.layers = layers2; this.levelsRange = levelsRange; this.layersToHide = layersToHide; this.defaultLevel = defaultLevel; this.showFeaturesWithEmptyLevel = showFeaturesWithEmptyLevel; this.beforeLayerId = beforeLayerId; } static fromGeojson(geojson, options = {}) { const { bounds, levelsRange } = GeoJsonHelper.extractLevelsRangeAndBounds(geojson); const map = new IndoorMap(bounds, geojson, options.layers ? options.layers : Style.DefaultLayers, levelsRange, options.layersToHide ? options.layersToHide : [], options.defaultLevel ? options.defaultLevel : 0, options.showFeaturesWithEmptyLevel ? options.showFeaturesWithEmptyLevel : false, options.beforeLayerId); return map; } } function destination(origin, distance2, bearing, options) { if (options === void 0) { options = {}; } var coordinates1 = getCoord(origin); var longitude1 = degreesToRadians(coordinates1[0]); var latitude1 = degreesToRadians(coordinates1[1]); var bearingRad = degreesToRadians(bearing); var radians = lengthToRadians(distance2, options.units); var latitude2 = Math.asin(Math.sin(latitude1) * Math.cos(radians) + Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearingRad)); var longitude2 = longitude1 + Math.atan2(Math.sin(bearingRad) * Math.sin(radians) * Math.cos(latitude1), Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2)); var lng = radiansToDegrees(longitude2); var lat = radiansToDegrees(latitude2); return point([lng, lat], options.properties); } function addIndoorTo(map) { Object.defineProperty(map, "indoor", { get: function() { if (!this._indoor) { this._indoor = new IndoorLayer(this); } return this._indoor; } }); return map; } const MIN_ZOOM_TO_DOWNLOAD = 17; const AREA_TO_DOWNLOAD = 1e3; class MapServerHandler { constructor(serverUrl, map, indoorMapOptions) { __publicField(this, "serverUrl"); __publicField(this, "map"); __publicField(this, "remoteMapsDownloaded"); __publicField(this, "downloadedBounds"); __publicField(this, "loadMapsPromise", Promise.resolve()); __publicField(this, "indoorMapOptions"); __publicField(this, "loadMapsIfNecessary", async () => { if (this.map.getZoom() < MIN_ZOOM_TO_DOWNLOAD) { return; } const viewPort = this.map.getBounds(); if (this.downloadedBounds !== null) { if (bboxContains(this.downloadedBounds, viewPort.getNorthEast().toArray()) && bboxContains(this.downloadedBounds, viewPort.getSouthWest().toArray())) { return; } } const distanceEastWest = distance(viewPort.getNorthEast().toArray(), viewPort.getNorthWest().toArray()); const distanceNorthSouth = distance(viewPort.getNorthEast().toArray(), viewPort.getSouthEast().toArray()); const maxDistanceOnScreen = Math.max(distanceEastWest, distanceNorthSouth); const bestSizeOfAreaToDownload = Math.max(AREA_TO_DOWNLOAD, maxDistanceOnScreen * 2); const center = this.map.getCenter(); const dist = bestSizeOfAreaToDownload * Math.sqrt(2); const northEast = destination(center.toArray(), dist, Math.PI / 4).geometry.coordinates; const southWest = destination(center.toArray(), dist, -3 * Math.PI / 4).geometry.coordinates; const boundsToDownload = [southWest[1], southWest[0], northEast[1], northEast[0]]; this.downloadedBounds = boundsToDownload; await this.loadMapsPromise; this.loadMapsPromise = this.loadMapsInBounds(boundsToDownload); }); __publicField(this, "loadMapsInBounds", async (bounds) => { const url = this.serverUrl + `/maps-in-bounds/${bounds[0]},${bounds[1]},${bounds[2]},${bounds[3]}`; const maps = await (await fetch(url)).json(); const mapsToRemove = this.remoteMapsDownloaded.reduce((acc, map) => { if (!maps.find((_map) => _map.path === map.path)) { acc.push(map); } return acc; }, []); const mapsToAdd = maps.reduce((acc, map) => { if (!this.remoteMapsDownloaded.find((_map) => _map.path === map.path)) { acc.push(map); } return acc; }, []); mapsToAdd.forEach(this.addCustomMap); mapsToRemove.forEach(this.removeCustomMap); }); __publicField(this, "addCustomMap", async (map) => { const geojson = await (await fetch(this.serverUrl + map.path)).json(); map.indoorMap = IndoorMap.fromGeojson(geojson, this.indoorMapOptions); this.map.indoor.addMap(map.indoorMap); this.remoteMapsDownloaded.push(map); }); __publicField(this, "removeCustomMap", async (map) => { this.map.indoor.removeMap(map.indoorMap); this.remoteMapsDownloaded.splice(this.remoteMapsDownloaded.indexOf(map), 1); }); this.serverUrl = serverUrl; this.map = map; this.indoorMapOptions = indoorMapOptions; this.remoteMapsDownloaded = []; this.downloadedBounds = null; if (map.loaded()) { this.loadMapsIfNecessary(); } else { map.on("load", () => this.loadMapsIfNecessary()); } map.on("move", () => this.loadMapsIfNecessary()); } static manage(server, map, indoorMapOptions) { return new MapServerHandler(server, addIndoorTo(map), indoorMapOptions); } } export { Style as DefaultStyle, IndoorControl, IndoorLayer, IndoorMap, MapServerHandler, addIndoorTo };