@geospatial-sdk/maplibre 0.0.5-dev.41
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.
- package/LICENSE +28 -0
- package/README.md +11 -0
- package/dist/helpers/map.helpers.d.ts +21 -0
- package/dist/helpers/map.helpers.d.ts.map +1 -0
- package/dist/helpers/map.helpers.js +65 -0
- package/dist/helpers/style.helpers.d.ts +4 -0
- package/dist/helpers/style.helpers.d.ts.map +1 -0
- package/dist/helpers/style.helpers.js +54 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/map/apply-context-diff.d.ts +9 -0
- package/dist/map/apply-context-diff.d.ts.map +1 -0
- package/dist/map/apply-context-diff.js +71 -0
- package/dist/map/create-map.d.ts +17 -0
- package/dist/map/create-map.d.ts.map +1 -0
- package/dist/map/create-map.js +144 -0
- package/dist/map/index.d.ts +3 -0
- package/dist/map/index.d.ts.map +1 -0
- package/dist/map/index.js +2 -0
- package/dist/maplibre.models.d.ts +13 -0
- package/dist/maplibre.models.d.ts.map +1 -0
- package/dist/maplibre.models.js +1 -0
- package/lib/helpers/map.helpers.ts +101 -0
- package/lib/helpers/style.helpers.test.ts +160 -0
- package/lib/helpers/style.helpers.ts +68 -0
- package/lib/index.ts +2 -0
- package/lib/map/apply-context-diff.test.ts +152 -0
- package/lib/map/apply-context-diff.ts +89 -0
- package/lib/map/create-map.test.ts +287 -0
- package/lib/map/create-map.ts +173 -0
- package/lib/map/index.ts +2 -0
- package/lib/maplibre.models.ts +31 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Camptocamp
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Map } from "maplibre-gl";
|
|
2
|
+
import { Dataset, LayerContextWithStyle, LayerSpecificationWithSource } from "../maplibre.models";
|
|
3
|
+
import { FeatureCollection, Geometry } from "geojson";
|
|
4
|
+
import { MapContextLayer } from "@geospatial-sdk/core";
|
|
5
|
+
/**
|
|
6
|
+
* Remove all layers from a given source in the map.
|
|
7
|
+
* @param map
|
|
8
|
+
* @param sourceId
|
|
9
|
+
*/
|
|
10
|
+
export declare function removeLayersFromSource(map: Map, sourceId: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Create a Maplibre source and layers from a GeoJSON MapContextLayer and its style.
|
|
13
|
+
* @param layerModel
|
|
14
|
+
* @param geojson
|
|
15
|
+
* @param sourcePosition
|
|
16
|
+
*/
|
|
17
|
+
export declare function createDatasetFromGeoJsonLayer(layerModel: LayerContextWithStyle, geojson: FeatureCollection<Geometry | null> | string, sourcePosition: number): Dataset;
|
|
18
|
+
export declare function getLayersAtPosition(map: Map, position: number): LayerSpecificationWithSource[];
|
|
19
|
+
export declare function getBeforeId(map: Map, position: number): string | undefined;
|
|
20
|
+
export declare function generateLayerId(layerModel: MapContextLayer): string;
|
|
21
|
+
//# sourceMappingURL=map.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map.helpers.d.ts","sourceRoot":"","sources":["../../lib/helpers/map.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAsB,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,OAAO,EACP,qBAAqB,EAErB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,QAWhE;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,iBAAiB,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,MAAM,EACpD,cAAc,EAAE,MAAM,GACrB,OAAO,CAuBT;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,4BAA4B,EAAE,CAUhC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS1E;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,UAS1D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { contextStyleToMaplibreLayers } from "./style.helpers";
|
|
2
|
+
import { getHash } from "@geospatial-sdk/core/dist/utils/hash";
|
|
3
|
+
/**
|
|
4
|
+
* Remove all layers from a given source in the map.
|
|
5
|
+
* @param map
|
|
6
|
+
* @param sourceId
|
|
7
|
+
*/
|
|
8
|
+
export function removeLayersFromSource(map, sourceId) {
|
|
9
|
+
const layers = map.getStyle().layers;
|
|
10
|
+
const layersWithSource = layers.filter((layer) => layer.type !== "background");
|
|
11
|
+
const layerIds = layersWithSource
|
|
12
|
+
.filter((layer) => layer.hasOwnProperty("source") && layer.source === sourceId)
|
|
13
|
+
.map((layer) => layer.id);
|
|
14
|
+
layerIds.forEach((layer) => map.removeLayer(layer));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a Maplibre source and layers from a GeoJSON MapContextLayer and its style.
|
|
18
|
+
* @param layerModel
|
|
19
|
+
* @param geojson
|
|
20
|
+
* @param sourcePosition
|
|
21
|
+
*/
|
|
22
|
+
export function createDatasetFromGeoJsonLayer(layerModel, geojson, sourcePosition) {
|
|
23
|
+
const sourceId = generateLayerId(layerModel);
|
|
24
|
+
const partialLayers = contextStyleToMaplibreLayers(layerModel.style);
|
|
25
|
+
const layers = partialLayers.map((layer) => (Object.assign(Object.assign({}, layer), { id: `${sourceId}-${layer.type}`, source: sourceId, layout: {
|
|
26
|
+
visibility: layerModel.visibility === false ? "none" : "visible",
|
|
27
|
+
}, metadata: {
|
|
28
|
+
sourcePosition,
|
|
29
|
+
} })));
|
|
30
|
+
return {
|
|
31
|
+
sources: {
|
|
32
|
+
[sourceId]: {
|
|
33
|
+
type: "geojson",
|
|
34
|
+
data: geojson,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
layers,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function getLayersAtPosition(map, position) {
|
|
41
|
+
const layers = map.getStyle().layers;
|
|
42
|
+
const layersWithSource = layers.filter((layer) => layer.type !== "background");
|
|
43
|
+
return layersWithSource.filter((layer) => {
|
|
44
|
+
var _a;
|
|
45
|
+
return ((_a = layer.metadata) === null || _a === void 0 ? void 0 : _a.sourcePosition) ===
|
|
46
|
+
position;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function getBeforeId(map, position) {
|
|
50
|
+
const beforeLayer = map
|
|
51
|
+
.getStyle()
|
|
52
|
+
.layers.find((layer) => layer.metadata.sourcePosition ===
|
|
53
|
+
position + 1);
|
|
54
|
+
return beforeLayer ? beforeLayer.id : undefined;
|
|
55
|
+
}
|
|
56
|
+
export function generateLayerId(layerModel) {
|
|
57
|
+
return getHash(layerModel, [
|
|
58
|
+
"name",
|
|
59
|
+
"style",
|
|
60
|
+
"visibility",
|
|
61
|
+
"opacity",
|
|
62
|
+
"version",
|
|
63
|
+
"extras",
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { VectorStyle } from "@geospatial-sdk/core/dist/model/style";
|
|
2
|
+
import { LayerSpecification } from "maplibre-gl";
|
|
3
|
+
export declare function contextStyleToMaplibreLayers(style?: VectorStyle): Partial<LayerSpecification>[];
|
|
4
|
+
//# sourceMappingURL=style.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.helpers.d.ts","sourceRoot":"","sources":["../../lib/helpers/style.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAajD,wBAAgB,4BAA4B,CAC1C,KAAK,GAAE,WAAgB,GACtB,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAmD/B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const defaultOltStyle = {
|
|
2
|
+
"fill-color": "rgba(255,255,255,0.4)",
|
|
3
|
+
"stroke-color": "#3399CC",
|
|
4
|
+
"stroke-width": 1.25,
|
|
5
|
+
"circle-radius": 5,
|
|
6
|
+
"circle-fill-color": "rgba(255,255,255,0.4)",
|
|
7
|
+
"circle-stroke-width": 1.25,
|
|
8
|
+
"circle-stroke-color": "#3399CC",
|
|
9
|
+
};
|
|
10
|
+
export function contextStyleToMaplibreLayers(style = {}) {
|
|
11
|
+
const layers = [];
|
|
12
|
+
if (Array.isArray(style)) {
|
|
13
|
+
return style.flatMap((style_) => {
|
|
14
|
+
if (style_.hasOwnProperty("style")) {
|
|
15
|
+
console.warn("Rules in styles are not supported yet.");
|
|
16
|
+
return contextStyleToMaplibreLayers(style_.style);
|
|
17
|
+
}
|
|
18
|
+
return contextStyleToMaplibreLayers(style_);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
style = Object.assign(Object.assign({}, defaultOltStyle), style);
|
|
22
|
+
if (style["fill-color"]) {
|
|
23
|
+
layers.push({
|
|
24
|
+
type: "fill",
|
|
25
|
+
paint: {
|
|
26
|
+
"fill-color": style["fill-color"],
|
|
27
|
+
},
|
|
28
|
+
filter: ["==", "$type", "Polygon"],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (style["stroke-color"] || style["stroke-width"]) {
|
|
32
|
+
layers.push({
|
|
33
|
+
type: "line",
|
|
34
|
+
paint: Object.assign(Object.assign({ "line-color": style["stroke-color"] }, (style["stroke-width"] !== undefined && {
|
|
35
|
+
"line-width": style["stroke-width"],
|
|
36
|
+
})), (style["stroke-line-dash"] !== undefined && {
|
|
37
|
+
"line-dasharray": style["stroke-line-dash"],
|
|
38
|
+
})),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (style["circle-radius"]) {
|
|
42
|
+
layers.push({
|
|
43
|
+
type: "circle",
|
|
44
|
+
paint: {
|
|
45
|
+
"circle-radius": style["circle-radius"],
|
|
46
|
+
"circle-stroke-color": style["circle-stroke-color"],
|
|
47
|
+
"circle-color": style["circle-fill-color"],
|
|
48
|
+
"circle-stroke-width": style["circle-stroke-width"],
|
|
49
|
+
},
|
|
50
|
+
filter: ["==", "$type", "Point"],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return layers;
|
|
54
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MapContextDiff } from "@geospatial-sdk/core";
|
|
2
|
+
import { Map } from "maplibre-gl";
|
|
3
|
+
/**
|
|
4
|
+
* Apply a context diff to an MapLibre map
|
|
5
|
+
* @param map
|
|
6
|
+
* @param contextDiff
|
|
7
|
+
*/
|
|
8
|
+
export declare function applyContextDiffToMap(map: Map, contextDiff: MapContextDiff): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=apply-context-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-context-diff.d.ts","sourceRoot":"","sources":["../../lib/map/apply-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AASlC;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,IAAI,CAAC,CAsEf"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { createLayer } from "./create-map";
|
|
11
|
+
import { generateLayerId, getBeforeId, getLayersAtPosition, removeLayersFromSource, } from "../helpers/map.helpers";
|
|
12
|
+
/**
|
|
13
|
+
* Apply a context diff to an MapLibre map
|
|
14
|
+
* @param map
|
|
15
|
+
* @param contextDiff
|
|
16
|
+
*/
|
|
17
|
+
export function applyContextDiffToMap(map, contextDiff) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
// removed layers (sorted by descending position)
|
|
20
|
+
if (contextDiff.layersRemoved.length > 0) {
|
|
21
|
+
const removed = contextDiff.layersRemoved.sort((a, b) => b.position - a.position);
|
|
22
|
+
for (const layerRemoved of removed) {
|
|
23
|
+
const mlLayers = getLayersAtPosition(map, layerRemoved.position);
|
|
24
|
+
if (mlLayers.length === 0) {
|
|
25
|
+
console.warn(`[Warning] applyContextDiffToMap: no layer found at position ${layerRemoved.position} to remove.`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const sourceId = mlLayers[0].source;
|
|
29
|
+
mlLayers.forEach((layer) => {
|
|
30
|
+
map.removeLayer(layer.id);
|
|
31
|
+
});
|
|
32
|
+
map.removeSource(sourceId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// insert added layers
|
|
36
|
+
const newLayers = yield Promise.all(contextDiff.layersAdded.map((layerAdded) => createLayer(layerAdded.layer, layerAdded.position)));
|
|
37
|
+
newLayers.forEach((style, index) => {
|
|
38
|
+
const position = contextDiff.layersAdded[index].position;
|
|
39
|
+
const beforeId = getBeforeId(map, position);
|
|
40
|
+
Object.keys(style.sources).forEach((sourceId) => map.addSource(sourceId, style.sources[sourceId]));
|
|
41
|
+
style.layers.map((layer) => {
|
|
42
|
+
map.addLayer(layer, beforeId);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
// recreate changed layers
|
|
46
|
+
for (const layerChanged of contextDiff.layersChanged) {
|
|
47
|
+
const { layer, position } = layerChanged;
|
|
48
|
+
const sourceId = generateLayerId(layer);
|
|
49
|
+
removeLayersFromSource(map, sourceId);
|
|
50
|
+
const beforeId = getBeforeId(map, position);
|
|
51
|
+
createLayer(layer, position).then((styleDiff) => {
|
|
52
|
+
styleDiff.layers.map((layer) => {
|
|
53
|
+
map.addLayer(layer, beforeId);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (typeof contextDiff.viewChanges !== "undefined") {
|
|
58
|
+
const { viewChanges } = contextDiff;
|
|
59
|
+
if (viewChanges && "extent" in viewChanges) {
|
|
60
|
+
const { extent } = viewChanges;
|
|
61
|
+
map.fitBounds([
|
|
62
|
+
[extent[0], extent[1]],
|
|
63
|
+
[extent[2], extent[3]],
|
|
64
|
+
], {
|
|
65
|
+
padding: 20,
|
|
66
|
+
duration: 1000,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MapContext, MapContextLayer } from "@geospatial-sdk/core";
|
|
2
|
+
import { Map, MapOptions } from "maplibre-gl";
|
|
3
|
+
import { PartialStyleSpecification } from "../maplibre.models";
|
|
4
|
+
export declare function createLayer(layerModel: MapContextLayer, sourcePosition: number): Promise<PartialStyleSpecification>;
|
|
5
|
+
/**
|
|
6
|
+
* Create an Maplibre map from a context; optionally specify a target (root element) for the map
|
|
7
|
+
* @param context
|
|
8
|
+
* @param target
|
|
9
|
+
*/
|
|
10
|
+
export declare function createMapFromContext(context: MapContext, mapOptions: MapOptions): Promise<Map>;
|
|
11
|
+
/**
|
|
12
|
+
* Resets a Maplibre map from a context; existing content will be cleared
|
|
13
|
+
* @param map
|
|
14
|
+
* @param context
|
|
15
|
+
*/
|
|
16
|
+
export declare function resetMapFromContext(map: Map, context: MapContext): Promise<Map>;
|
|
17
|
+
//# sourceMappingURL=create-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-map.d.ts","sourceRoot":"","sources":["../../lib/map/create-map.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EAGhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,GAAG,EACH,UAAU,EAEX,MAAM,aAAa,CAAC;AAWrB,OAAO,EAAW,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAOxE,wBAAsB,WAAW,CAC/B,UAAU,EAAE,eAAe,EAC3B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,yBAAyB,CAAC,CAgGpC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC,CAGd;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,GAAG,CAAC,CAoBd"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { removeSearchParams, } from "@geospatial-sdk/core";
|
|
11
|
+
import { Map, } from "maplibre-gl";
|
|
12
|
+
import { OgcApiEndpoint, WfsEndpoint, WmsEndpoint, } from "@camptocamp/ogc-client";
|
|
13
|
+
import { createDatasetFromGeoJsonLayer, generateLayerId, } from "../helpers/map.helpers";
|
|
14
|
+
const featureCollection = {
|
|
15
|
+
type: "FeatureCollection",
|
|
16
|
+
features: [],
|
|
17
|
+
};
|
|
18
|
+
export function createLayer(layerModel, sourcePosition) {
|
|
19
|
+
var _a;
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const { type } = layerModel;
|
|
22
|
+
switch (type) {
|
|
23
|
+
case "wms": {
|
|
24
|
+
const layerId = generateLayerId(layerModel);
|
|
25
|
+
const sourceId = layerId;
|
|
26
|
+
const endpoint = yield new WmsEndpoint(layerModel.url).isReady();
|
|
27
|
+
let url = endpoint.getMapUrl([layerModel.name], {
|
|
28
|
+
widthPx: 256,
|
|
29
|
+
heightPx: 256,
|
|
30
|
+
extent: [0, 0, 0, 0], // will be replaced by maplibre-gl
|
|
31
|
+
outputFormat: "image/png",
|
|
32
|
+
crs: "EPSG:3857",
|
|
33
|
+
});
|
|
34
|
+
url = removeSearchParams(url, ["bbox"]);
|
|
35
|
+
url = `${url.toString()}&BBOX={bbox-epsg-3857}`;
|
|
36
|
+
const dataset = {
|
|
37
|
+
sources: {
|
|
38
|
+
[sourceId]: {
|
|
39
|
+
type: "raster",
|
|
40
|
+
tiles: [url],
|
|
41
|
+
tileSize: 256,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
layers: [
|
|
45
|
+
{
|
|
46
|
+
id: layerId,
|
|
47
|
+
type: "raster",
|
|
48
|
+
source: sourceId,
|
|
49
|
+
paint: {},
|
|
50
|
+
metadata: {
|
|
51
|
+
sourcePosition,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
return dataset;
|
|
57
|
+
}
|
|
58
|
+
case "wfs": {
|
|
59
|
+
const entryPoint = yield new WfsEndpoint(layerModel.url).isReady();
|
|
60
|
+
const url = entryPoint.getFeatureUrl(layerModel.featureType, {
|
|
61
|
+
asJson: true,
|
|
62
|
+
outputCrs: "EPSG:4326",
|
|
63
|
+
});
|
|
64
|
+
return createDatasetFromGeoJsonLayer(layerModel, url, sourcePosition);
|
|
65
|
+
}
|
|
66
|
+
case "geojson": {
|
|
67
|
+
let geojson;
|
|
68
|
+
if (layerModel.url !== undefined) {
|
|
69
|
+
geojson = layerModel.url;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const data = layerModel.data;
|
|
73
|
+
if (typeof data === "string") {
|
|
74
|
+
try {
|
|
75
|
+
geojson = JSON.parse(data);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
console.warn("A layer could not be created", layerModel, e);
|
|
79
|
+
geojson = featureCollection;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
geojson = data;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return createDatasetFromGeoJsonLayer(layerModel, geojson, sourcePosition);
|
|
87
|
+
}
|
|
88
|
+
case "ogcapi": {
|
|
89
|
+
const ogcEndpoint = new OgcApiEndpoint(layerModel.url);
|
|
90
|
+
let layerUrl;
|
|
91
|
+
if (layerModel.useTiles) {
|
|
92
|
+
console.warn("[Warning] OGC API - Tiles not yet implemented.");
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
layerUrl = yield ogcEndpoint.getCollectionItemsUrl(layerModel.collection, Object.assign(Object.assign({}, layerModel.options), { asJson: true }));
|
|
96
|
+
return createDatasetFromGeoJsonLayer(layerModel, layerUrl, sourcePosition);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case "maplibre-style": {
|
|
101
|
+
console.warn("[Warning] Maplibre style - Not yet fully implemented.");
|
|
102
|
+
const style = yield fetch(layerModel.styleUrl).then((res) => res.json());
|
|
103
|
+
(_a = style.layers) === null || _a === void 0 ? void 0 : _a.forEach((layer) => (layer.metadata = { sourcePosition }));
|
|
104
|
+
return style;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {};
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create an Maplibre map from a context; optionally specify a target (root element) for the map
|
|
112
|
+
* @param context
|
|
113
|
+
* @param target
|
|
114
|
+
*/
|
|
115
|
+
export function createMapFromContext(context, mapOptions) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const map = new Map(mapOptions);
|
|
118
|
+
return yield resetMapFromContext(map, context);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Resets a Maplibre map from a context; existing content will be cleared
|
|
123
|
+
* @param map
|
|
124
|
+
* @param context
|
|
125
|
+
*/
|
|
126
|
+
export function resetMapFromContext(map, context) {
|
|
127
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
map.setZoom(context.view.zoom);
|
|
129
|
+
map.setCenter(context.view.center);
|
|
130
|
+
for (let i = 0; i < context.layers.length; i++) {
|
|
131
|
+
const layerModel = context.layers[i];
|
|
132
|
+
const partialMLStyle = yield createLayer(layerModel, i);
|
|
133
|
+
if (partialMLStyle.glyphs) {
|
|
134
|
+
map.setGlyphs(partialMLStyle.glyphs);
|
|
135
|
+
}
|
|
136
|
+
if (partialMLStyle.sprite) {
|
|
137
|
+
map.setSprite(partialMLStyle.sprite);
|
|
138
|
+
}
|
|
139
|
+
Object.keys(partialMLStyle.sources).forEach((sourceId) => map.addSource(sourceId, partialMLStyle.sources[sourceId]));
|
|
140
|
+
partialMLStyle.layers.map((layer) => map.addLayer(layer));
|
|
141
|
+
}
|
|
142
|
+
return map;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/map/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BackgroundLayerSpecification, LayerSpecification, StyleSpecification } from "maplibre-gl";
|
|
2
|
+
import { MapContextLayerGeojson, MapContextLayerOgcApi, MapContextLayerWfs } from "@geospatial-sdk/core";
|
|
3
|
+
export type LayerSpecificationWithSource = Exclude<LayerSpecification, BackgroundLayerSpecification>;
|
|
4
|
+
export interface LayerMetadataSpecification {
|
|
5
|
+
sourcePosition: number;
|
|
6
|
+
}
|
|
7
|
+
export type LayerContextWithStyle = MapContextLayerWfs | MapContextLayerOgcApi | MapContextLayerGeojson;
|
|
8
|
+
export type Dataset = Pick<StyleSpecification, "sources" | "layers">;
|
|
9
|
+
export type PartialStyleSpecification = Dataset & {
|
|
10
|
+
glyphs?: StyleSpecification["glyphs"];
|
|
11
|
+
sprite?: StyleSpecification["sprite"];
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=maplibre.models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maplibre.models.d.ts","sourceRoot":"","sources":["../lib/maplibre.models.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAChD,kBAAkB,EAClB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,qBAAqB,GACrB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAC,CAAC;AAErE,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG;IAChD,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;CACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Map, StyleSpecification } from "maplibre-gl";
|
|
2
|
+
import {
|
|
3
|
+
Dataset,
|
|
4
|
+
LayerContextWithStyle,
|
|
5
|
+
LayerMetadataSpecification,
|
|
6
|
+
LayerSpecificationWithSource,
|
|
7
|
+
} from "../maplibre.models";
|
|
8
|
+
import { FeatureCollection, Geometry } from "geojson";
|
|
9
|
+
import { contextStyleToMaplibreLayers } from "./style.helpers";
|
|
10
|
+
import { getHash } from "@geospatial-sdk/core/dist/utils/hash";
|
|
11
|
+
import { MapContextLayer } from "@geospatial-sdk/core";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Remove all layers from a given source in the map.
|
|
15
|
+
* @param map
|
|
16
|
+
* @param sourceId
|
|
17
|
+
*/
|
|
18
|
+
export function removeLayersFromSource(map: Map, sourceId: string) {
|
|
19
|
+
const layers = map.getStyle().layers;
|
|
20
|
+
const layersWithSource = layers.filter(
|
|
21
|
+
(layer) => layer.type !== "background",
|
|
22
|
+
) as LayerSpecificationWithSource[];
|
|
23
|
+
const layerIds = layersWithSource
|
|
24
|
+
.filter(
|
|
25
|
+
(layer) => layer.hasOwnProperty("source") && layer.source === sourceId,
|
|
26
|
+
)
|
|
27
|
+
.map((layer) => layer.id);
|
|
28
|
+
layerIds.forEach((layer) => map.removeLayer(layer));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a Maplibre source and layers from a GeoJSON MapContextLayer and its style.
|
|
33
|
+
* @param layerModel
|
|
34
|
+
* @param geojson
|
|
35
|
+
* @param sourcePosition
|
|
36
|
+
*/
|
|
37
|
+
export function createDatasetFromGeoJsonLayer(
|
|
38
|
+
layerModel: LayerContextWithStyle,
|
|
39
|
+
geojson: FeatureCollection<Geometry | null> | string,
|
|
40
|
+
sourcePosition: number,
|
|
41
|
+
): Dataset {
|
|
42
|
+
const sourceId = generateLayerId(layerModel);
|
|
43
|
+
const partialLayers = contextStyleToMaplibreLayers(layerModel.style);
|
|
44
|
+
const layers = partialLayers.map((layer) => ({
|
|
45
|
+
...layer,
|
|
46
|
+
id: `${sourceId}-${layer.type}`,
|
|
47
|
+
source: sourceId,
|
|
48
|
+
layout: {
|
|
49
|
+
visibility: layerModel.visibility === false ? "none" : "visible",
|
|
50
|
+
},
|
|
51
|
+
metadata: {
|
|
52
|
+
sourcePosition,
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
return {
|
|
56
|
+
sources: {
|
|
57
|
+
[sourceId]: {
|
|
58
|
+
type: "geojson",
|
|
59
|
+
data: geojson,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
layers,
|
|
63
|
+
} as StyleSpecification;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getLayersAtPosition(
|
|
67
|
+
map: Map,
|
|
68
|
+
position: number,
|
|
69
|
+
): LayerSpecificationWithSource[] {
|
|
70
|
+
const layers = map.getStyle().layers;
|
|
71
|
+
const layersWithSource = layers.filter(
|
|
72
|
+
(layer) => layer.type !== "background", //TODO background layers is not managed
|
|
73
|
+
) as LayerSpecificationWithSource[];
|
|
74
|
+
return layersWithSource.filter(
|
|
75
|
+
(layer) =>
|
|
76
|
+
(layer.metadata as LayerMetadataSpecification)?.sourcePosition ===
|
|
77
|
+
position,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getBeforeId(map: Map, position: number): string | undefined {
|
|
82
|
+
const beforeLayer = map
|
|
83
|
+
.getStyle()
|
|
84
|
+
.layers.find(
|
|
85
|
+
(layer) =>
|
|
86
|
+
(layer.metadata as LayerMetadataSpecification).sourcePosition ===
|
|
87
|
+
position + 1,
|
|
88
|
+
);
|
|
89
|
+
return beforeLayer ? beforeLayer.id : undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function generateLayerId(layerModel: MapContextLayer) {
|
|
93
|
+
return getHash(layerModel, [
|
|
94
|
+
"name",
|
|
95
|
+
"style",
|
|
96
|
+
"visibility",
|
|
97
|
+
"opacity",
|
|
98
|
+
"version",
|
|
99
|
+
"extras",
|
|
100
|
+
]);
|
|
101
|
+
}
|