map-gl-indoor
Version:
A MapGL plugin to visualize multi-level buildings
1,823 lines (1,822 loc) • 43.6 kB
JavaScript
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 };