@geospatial-sdk/maplibre 0.0.5-dev.49 → 0.0.5-dev.50
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/dist/helpers/map.helpers.d.ts +46 -12
- package/dist/helpers/map.helpers.d.ts.map +1 -1
- package/dist/helpers/map.helpers.js +153 -36
- package/dist/map/apply-context-diff.d.ts +12 -3
- package/dist/map/apply-context-diff.d.ts.map +1 -1
- package/dist/map/apply-context-diff.js +67 -13
- package/dist/map/create-map.d.ts +7 -3
- package/dist/map/create-map.d.ts.map +1 -1
- package/dist/map/create-map.js +63 -22
- package/dist/maplibre.models.d.ts +2 -1
- package/dist/maplibre.models.d.ts.map +1 -1
- package/lib/helpers/map.helpers.ts +188 -50
- package/lib/map/apply-context-diff.test.ts +476 -58
- package/lib/map/apply-context-diff.ts +95 -22
- package/lib/map/create-map.test.ts +158 -63
- package/lib/map/create-map.ts +70 -34
- package/lib/maplibre.models.ts +2 -1
- package/package.json +3 -3
|
@@ -1,21 +1,55 @@
|
|
|
1
|
-
import { Map } from "maplibre-gl";
|
|
2
|
-
import { Dataset, LayerContextWithStyle, LayerSpecificationWithSource } from "../maplibre.models.js";
|
|
1
|
+
import type { LayerSpecification, Map } from "maplibre-gl";
|
|
2
|
+
import { Dataset, LayerContextWithStyle, LayerMetadataSpecification, LayerSpecificationWithSource } from "../maplibre.models.js";
|
|
3
3
|
import { FeatureCollection, Geometry } from "geojson";
|
|
4
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
5
|
/**
|
|
12
6
|
* Create a Maplibre source and layers from a GeoJSON MapContextLayer and its style.
|
|
13
7
|
* @param layerModel
|
|
14
8
|
* @param geojson
|
|
15
|
-
* @param
|
|
9
|
+
* @param metadata
|
|
10
|
+
*/
|
|
11
|
+
export declare function createDatasetFromGeoJsonLayer(layerModel: LayerContextWithStyle, geojson: FeatureCollection<Geometry | null> | string, metadata: LayerMetadataSpecification): Dataset;
|
|
12
|
+
export declare function getLayersFromContextLayer(map: Map, layerModel: MapContextLayer): LayerSpecificationWithSource[];
|
|
13
|
+
/**
|
|
14
|
+
* This returns all MapLibre layers that correspond to a position in a MapContext
|
|
15
|
+
* @param map
|
|
16
|
+
* @param position
|
|
16
17
|
*/
|
|
17
|
-
export declare function createDatasetFromGeoJsonLayer(layerModel: LayerContextWithStyle, geojson: FeatureCollection<Geometry | null> | string, sourcePosition: number): Dataset;
|
|
18
18
|
export declare function getLayersAtPosition(map: Map, position: number): LayerSpecificationWithSource[];
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* This returns the id of the first MapLibre layer that corresponds to the given MapContext position;
|
|
21
|
+
* used as a beforeId for adding/moving layers
|
|
22
|
+
* @param map
|
|
23
|
+
* @param position
|
|
24
|
+
*/
|
|
25
|
+
export declare function getFirstLayerIdAtPosition(map: Map, position: number): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* This generates a layer hash that stays consistent even if updatable properties change.
|
|
28
|
+
* @param layerModel
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateLayerHashWithoutUpdatableProps(layerModel: MapContextLayer): string;
|
|
31
|
+
/**
|
|
32
|
+
* Incremental update is possible only if certain properties are changed: opacity,
|
|
33
|
+
* visibility, zIndex, etc.
|
|
34
|
+
*
|
|
35
|
+
* Note: we assume that both layers are different versions of the same layer (this
|
|
36
|
+
* will not be checked again)
|
|
37
|
+
* @param oldLayer
|
|
38
|
+
* @param newLayer
|
|
39
|
+
* @return Returns `true` if the only properties changed are the updatable ones
|
|
40
|
+
*/
|
|
41
|
+
export declare function canDoIncrementalUpdate(oldLayer: MapContextLayer, newLayer: MapContextLayer): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* This simply generates a unique id
|
|
44
|
+
*/
|
|
45
|
+
export declare function generateLayerId(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Will apply generic properties to the layer; only changes the necessary
|
|
48
|
+
* properties to avoid updating the map too often
|
|
49
|
+
* @param map
|
|
50
|
+
* @param layer
|
|
51
|
+
* @param layerModel
|
|
52
|
+
* @param previousLayerModel
|
|
53
|
+
*/
|
|
54
|
+
export declare function updateLayerProperties(map: Map, layer: LayerSpecification, layerModel: MapContextLayer, previousLayerModel: MapContextLayer): void;
|
|
21
55
|
//# sourceMappingURL=map.helpers.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"map.helpers.d.ts","sourceRoot":"","sources":["../../lib/helpers/map.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAsB,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,OAAO,EACP,qBAAqB,EACrB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAuB,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAW5E;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,iBAAiB,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,MAAM,EACpD,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CA+BT;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,eAAe,GAC1B,4BAA4B,EAAE,CAkBhC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,4BAA4B,EAAE,CAoBhC;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAoBpB;AAYD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,eAAe,UAG5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAKT;AAED;;GAEG;AACH,wBAAgB,eAAe,WAE9B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,kBAAkB,EACzB,UAAU,EAAE,eAAe,EAC3B,kBAAkB,EAAE,eAAe,QA+BpC"}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { contextStyleToMaplibreLayers } from "./style.helpers.js";
|
|
2
2
|
import { getHash } from "@geospatial-sdk/core/dist/utils/hash.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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));
|
|
3
|
+
function getOpacityPaintPropNames(layerType) {
|
|
4
|
+
switch (layerType) {
|
|
5
|
+
case "circle":
|
|
6
|
+
return ["circle-opacity", "circle-stroke-opacity"];
|
|
7
|
+
default:
|
|
8
|
+
return [`${layerType}-opacity`];
|
|
9
|
+
}
|
|
15
10
|
}
|
|
16
11
|
/**
|
|
17
12
|
* Create a Maplibre source and layers from a GeoJSON MapContextLayer and its style.
|
|
18
13
|
* @param layerModel
|
|
19
14
|
* @param geojson
|
|
20
|
-
* @param
|
|
15
|
+
* @param metadata
|
|
21
16
|
*/
|
|
22
|
-
export function createDatasetFromGeoJsonLayer(layerModel, geojson,
|
|
23
|
-
const sourceId = generateLayerId(
|
|
17
|
+
export function createDatasetFromGeoJsonLayer(layerModel, geojson, metadata) {
|
|
18
|
+
const sourceId = generateLayerId();
|
|
24
19
|
const partialLayers = contextStyleToMaplibreLayers(layerModel.style);
|
|
25
20
|
const layers = partialLayers.map((layer) => ({
|
|
26
21
|
...layer,
|
|
27
22
|
id: `${sourceId}-${layer.type}`,
|
|
28
23
|
source: sourceId,
|
|
24
|
+
paint: {
|
|
25
|
+
...layer.paint,
|
|
26
|
+
...getOpacityPaintPropNames(layer.type).reduce((acc, prop) => ({
|
|
27
|
+
...acc,
|
|
28
|
+
[prop]: layerModel.opacity ?? 1,
|
|
29
|
+
}), {}),
|
|
30
|
+
},
|
|
29
31
|
layout: {
|
|
30
32
|
visibility: layerModel.visibility === false ? "none" : "visible",
|
|
31
33
|
},
|
|
32
|
-
metadata
|
|
33
|
-
sourcePosition,
|
|
34
|
-
},
|
|
34
|
+
metadata,
|
|
35
35
|
}));
|
|
36
36
|
return {
|
|
37
37
|
sources: {
|
|
@@ -43,26 +43,143 @@ export function createDatasetFromGeoJsonLayer(layerModel, geojson, sourcePositio
|
|
|
43
43
|
layers,
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
+
export function getLayersFromContextLayer(map, layerModel) {
|
|
47
|
+
const layerId = layerModel.id;
|
|
48
|
+
const layerHash = generateLayerHashWithoutUpdatableProps(layerModel);
|
|
49
|
+
const layers = map.getStyle().layers;
|
|
50
|
+
const result = [];
|
|
51
|
+
for (let i = 0; i < layers.length; i++) {
|
|
52
|
+
const layer = layers[i];
|
|
53
|
+
const metadata = layer.metadata;
|
|
54
|
+
if (layerId !== undefined) {
|
|
55
|
+
if (metadata?.layerId === layerId) {
|
|
56
|
+
result.push(layer);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (metadata?.layerHash === layerHash) {
|
|
60
|
+
result.push(layer);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* This returns all MapLibre layers that correspond to a position in a MapContext
|
|
67
|
+
* @param map
|
|
68
|
+
* @param position
|
|
69
|
+
*/
|
|
46
70
|
export function getLayersAtPosition(map, position) {
|
|
71
|
+
let layerId = undefined;
|
|
72
|
+
let layerHash = undefined;
|
|
73
|
+
const result = [];
|
|
47
74
|
const layers = map.getStyle().layers;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
75
|
+
let currentPosition = -1;
|
|
76
|
+
for (let i = 0; i < layers.length; i++) {
|
|
77
|
+
const layer = layers[i];
|
|
78
|
+
const metadata = layer.metadata;
|
|
79
|
+
if (metadata?.layerId !== layerId || metadata?.layerHash !== layerHash) {
|
|
80
|
+
currentPosition++;
|
|
81
|
+
layerId = metadata?.layerId;
|
|
82
|
+
layerHash = metadata?.layerHash;
|
|
83
|
+
}
|
|
84
|
+
if (currentPosition === position) {
|
|
85
|
+
result.push(layer);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
51
89
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
/**
|
|
91
|
+
* This returns the id of the first MapLibre layer that corresponds to the given MapContext position;
|
|
92
|
+
* used as a beforeId for adding/moving layers
|
|
93
|
+
* @param map
|
|
94
|
+
* @param position
|
|
95
|
+
*/
|
|
96
|
+
export function getFirstLayerIdAtPosition(map, position) {
|
|
97
|
+
let layerId = undefined;
|
|
98
|
+
let layerHash = undefined;
|
|
99
|
+
const layers = map.getStyle().layers;
|
|
100
|
+
let currentPosition = -1;
|
|
101
|
+
for (let i = 0; i < layers.length; i++) {
|
|
102
|
+
const layer = layers[i];
|
|
103
|
+
const metadata = layer.metadata;
|
|
104
|
+
if (metadata?.layerId !== layerId || metadata?.layerHash !== layerHash) {
|
|
105
|
+
currentPosition++;
|
|
106
|
+
if (currentPosition === position) {
|
|
107
|
+
return layer.id;
|
|
108
|
+
}
|
|
109
|
+
layerId = metadata?.layerId;
|
|
110
|
+
layerHash = metadata?.layerHash;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
58
114
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
115
|
+
const UPDATABLE_PROPERTIES = [
|
|
116
|
+
"opacity",
|
|
117
|
+
"visibility",
|
|
118
|
+
"label",
|
|
119
|
+
"extras",
|
|
120
|
+
"version",
|
|
121
|
+
// "attributions", // currently, updating the attribution means recreating the source & layer
|
|
122
|
+
// TODO (when available) "zIndex"
|
|
123
|
+
];
|
|
124
|
+
/**
|
|
125
|
+
* This generates a layer hash that stays consistent even if updatable properties change.
|
|
126
|
+
* @param layerModel
|
|
127
|
+
*/
|
|
128
|
+
export function generateLayerHashWithoutUpdatableProps(layerModel) {
|
|
129
|
+
return getHash(layerModel, UPDATABLE_PROPERTIES);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Incremental update is possible only if certain properties are changed: opacity,
|
|
133
|
+
* visibility, zIndex, etc.
|
|
134
|
+
*
|
|
135
|
+
* Note: we assume that both layers are different versions of the same layer (this
|
|
136
|
+
* will not be checked again)
|
|
137
|
+
* @param oldLayer
|
|
138
|
+
* @param newLayer
|
|
139
|
+
* @return Returns `true` if the only properties changed are the updatable ones
|
|
140
|
+
*/
|
|
141
|
+
export function canDoIncrementalUpdate(oldLayer, newLayer) {
|
|
142
|
+
const oldHash = generateLayerHashWithoutUpdatableProps(oldLayer);
|
|
143
|
+
const newHash = generateLayerHashWithoutUpdatableProps(newLayer);
|
|
144
|
+
// true if only updatable props have changed between the two versions of the layer
|
|
145
|
+
return oldHash === newHash;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* This simply generates a unique id
|
|
149
|
+
*/
|
|
150
|
+
export function generateLayerId() {
|
|
151
|
+
return Math.floor(Math.random() * 1000000).toString(10);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Will apply generic properties to the layer; only changes the necessary
|
|
155
|
+
* properties to avoid updating the map too often
|
|
156
|
+
* @param map
|
|
157
|
+
* @param layer
|
|
158
|
+
* @param layerModel
|
|
159
|
+
* @param previousLayerModel
|
|
160
|
+
*/
|
|
161
|
+
export function updateLayerProperties(map, layer, layerModel, previousLayerModel) {
|
|
162
|
+
function shouldApplyProperty(prop) {
|
|
163
|
+
// if the new layer model does not define that property, skip it
|
|
164
|
+
// (setting or resetting it to a default value would be counter-intuitive)
|
|
165
|
+
if (!(prop in layerModel) || typeof layerModel[prop] === "undefined")
|
|
166
|
+
return false;
|
|
167
|
+
// if the value did not change in the new layer model, skip it
|
|
168
|
+
if (layerModel[prop] === previousLayerModel[prop]) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
// any other case: apply the property
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
const layerId = layer.id;
|
|
175
|
+
const layerType = layer.type;
|
|
176
|
+
if (shouldApplyProperty("visibility")) {
|
|
177
|
+
map.setLayoutProperty(layerId, "visibility", layerModel.visibility === false ? "none" : "visible");
|
|
178
|
+
}
|
|
179
|
+
if (shouldApplyProperty("opacity")) {
|
|
180
|
+
getOpacityPaintPropNames(layerType).forEach((paintProp) => {
|
|
181
|
+
map.setPaintProperty(layerId, paintProp, layerModel.opacity ?? 1);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// TODO: z-index
|
|
68
185
|
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import { MapContextDiff } from "@geospatial-sdk/core";
|
|
2
|
-
import { Map } from "maplibre-gl";
|
|
1
|
+
import { MapContextDiff, MapContextLayer } from "@geospatial-sdk/core";
|
|
2
|
+
import type { Map } from "maplibre-gl";
|
|
3
|
+
/**
|
|
4
|
+
* This will either update the layers in the map or recreate them;
|
|
5
|
+
* the returned promise resolves when the update is done
|
|
6
|
+
* @param map
|
|
7
|
+
* @param layerModel
|
|
8
|
+
* @param previousLayerModel
|
|
9
|
+
* @param layerPosition
|
|
10
|
+
*/
|
|
11
|
+
export declare function updateLayerInMap(map: Map, layerModel: MapContextLayer, previousLayerModel: MapContextLayer, layerPosition: number): Promise<void>;
|
|
3
12
|
/**
|
|
4
13
|
* Apply a context diff to an MapLibre map
|
|
5
14
|
* @param map
|
|
6
15
|
* @param contextDiff
|
|
7
16
|
*/
|
|
8
|
-
export declare function applyContextDiffToMap(map: Map, contextDiff: MapContextDiff): Promise<
|
|
17
|
+
export declare function applyContextDiffToMap(map: Map, contextDiff: MapContextDiff): Promise<Map>;
|
|
9
18
|
//# sourceMappingURL=apply-context-diff.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"apply-context-diff.d.ts","sourceRoot":"","sources":["../../lib/map/apply-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAUvC;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,eAAe,EAC3B,kBAAkB,EAAE,eAAe,EACnC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,GAAG,CAAC,CAgGd"}
|
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
import { createLayer } from "./create-map.js";
|
|
2
|
-
import {
|
|
2
|
+
import { canDoIncrementalUpdate, getFirstLayerIdAtPosition, getLayersAtPosition, getLayersFromContextLayer, updateLayerProperties, } from "../helpers/map.helpers.js";
|
|
3
|
+
/**
|
|
4
|
+
* This will either update the layers in the map or recreate them;
|
|
5
|
+
* the returned promise resolves when the update is done
|
|
6
|
+
* @param map
|
|
7
|
+
* @param layerModel
|
|
8
|
+
* @param previousLayerModel
|
|
9
|
+
* @param layerPosition
|
|
10
|
+
*/
|
|
11
|
+
export async function updateLayerInMap(map, layerModel, previousLayerModel, layerPosition) {
|
|
12
|
+
// if an incremental update is possible, do it to avoid costly layer recreation
|
|
13
|
+
if (canDoIncrementalUpdate(previousLayerModel, layerModel)) {
|
|
14
|
+
// we can find the existing layers by using the hash or id of the layerModel
|
|
15
|
+
const mlUpdatedLayers = getLayersFromContextLayer(map, layerModel);
|
|
16
|
+
for (const layer of mlUpdatedLayers) {
|
|
17
|
+
updateLayerProperties(map, layer, layerModel, previousLayerModel);
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const mlLayersToRemove = getLayersAtPosition(map, layerPosition);
|
|
22
|
+
const sourcesToRemove = [];
|
|
23
|
+
for (const layer of mlLayersToRemove) {
|
|
24
|
+
if (layer.source && !sourcesToRemove.includes(layer.source)) {
|
|
25
|
+
sourcesToRemove.push(layer.source);
|
|
26
|
+
}
|
|
27
|
+
map.removeLayer(layer.id);
|
|
28
|
+
}
|
|
29
|
+
for (const sourceId of sourcesToRemove) {
|
|
30
|
+
map.removeSource(sourceId);
|
|
31
|
+
}
|
|
32
|
+
const styleDiff = await createLayer(layerModel);
|
|
33
|
+
if (!styleDiff)
|
|
34
|
+
return;
|
|
35
|
+
const beforeId = getFirstLayerIdAtPosition(map, layerPosition);
|
|
36
|
+
Object.keys(styleDiff.sources).forEach((sourceId) => map.addSource(sourceId, styleDiff.sources[sourceId]));
|
|
37
|
+
styleDiff.layers.map((layer) => {
|
|
38
|
+
map.addLayer(layer, beforeId);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
3
41
|
/**
|
|
4
42
|
* Apply a context diff to an MapLibre map
|
|
5
43
|
* @param map
|
|
@@ -10,7 +48,7 @@ export async function applyContextDiffToMap(map, contextDiff) {
|
|
|
10
48
|
if (contextDiff.layersRemoved.length > 0) {
|
|
11
49
|
const removed = contextDiff.layersRemoved.sort((a, b) => b.position - a.position);
|
|
12
50
|
for (const layerRemoved of removed) {
|
|
13
|
-
const mlLayers =
|
|
51
|
+
const mlLayers = getLayersFromContextLayer(map, layerRemoved.layer);
|
|
14
52
|
if (mlLayers.length === 0) {
|
|
15
53
|
console.warn(`[Warning] applyContextDiffToMap: no layer found at position ${layerRemoved.position} to remove.`);
|
|
16
54
|
continue;
|
|
@@ -23,26 +61,41 @@ export async function applyContextDiffToMap(map, contextDiff) {
|
|
|
23
61
|
}
|
|
24
62
|
}
|
|
25
63
|
// insert added layers
|
|
26
|
-
const newLayers = await Promise.all(contextDiff.layersAdded.map((layerAdded) => createLayer(layerAdded.layer
|
|
64
|
+
const newLayers = await Promise.all(contextDiff.layersAdded.map((layerAdded) => createLayer(layerAdded.layer)));
|
|
27
65
|
newLayers.forEach((style, index) => {
|
|
66
|
+
if (!style)
|
|
67
|
+
return;
|
|
28
68
|
const position = contextDiff.layersAdded[index].position;
|
|
29
|
-
const beforeId =
|
|
69
|
+
const beforeId = getFirstLayerIdAtPosition(map, position);
|
|
30
70
|
Object.keys(style.sources).forEach((sourceId) => map.addSource(sourceId, style.sources[sourceId]));
|
|
31
71
|
style.layers.map((layer) => {
|
|
32
72
|
map.addLayer(layer, beforeId);
|
|
33
73
|
});
|
|
34
74
|
});
|
|
75
|
+
// handle reordered layers (sorted by ascending new position)
|
|
76
|
+
if (contextDiff.layersReordered.length > 0) {
|
|
77
|
+
const reordered = contextDiff.layersReordered.sort((a, b) => a.newPosition - b.newPosition);
|
|
78
|
+
// collect all layers to be moved
|
|
79
|
+
const mlLayersToMove = reordered.map((layerReordered) => getLayersFromContextLayer(map, layerReordered.layer));
|
|
80
|
+
// move layers
|
|
81
|
+
for (let i = 0; i < reordered.length; i++) {
|
|
82
|
+
const layerReordered = reordered[i];
|
|
83
|
+
const mlLayers = mlLayersToMove[i];
|
|
84
|
+
const beforeId = getFirstLayerIdAtPosition(map, layerReordered.newPosition + 1);
|
|
85
|
+
if (mlLayers[0].id === beforeId) {
|
|
86
|
+
// layer is already at the right position
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// then we add the moved the layer to its new position
|
|
90
|
+
for (const layer of mlLayers) {
|
|
91
|
+
map.moveLayer(layer.id, beforeId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
35
95
|
// recreate changed layers
|
|
36
96
|
for (const layerChanged of contextDiff.layersChanged) {
|
|
37
|
-
const { layer, position } = layerChanged;
|
|
38
|
-
|
|
39
|
-
removeLayersFromSource(map, sourceId);
|
|
40
|
-
const beforeId = getBeforeId(map, position);
|
|
41
|
-
createLayer(layer, position).then((styleDiff) => {
|
|
42
|
-
styleDiff.layers.map((layer) => {
|
|
43
|
-
map.addLayer(layer, beforeId);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
97
|
+
const { layer, previousLayer, position } = layerChanged;
|
|
98
|
+
await updateLayerInMap(map, layer, previousLayer, position);
|
|
46
99
|
}
|
|
47
100
|
if (typeof contextDiff.viewChanges !== "undefined") {
|
|
48
101
|
const { viewChanges } = contextDiff;
|
|
@@ -57,4 +110,5 @@ export async function applyContextDiffToMap(map, contextDiff) {
|
|
|
57
110
|
});
|
|
58
111
|
}
|
|
59
112
|
}
|
|
113
|
+
return map;
|
|
60
114
|
}
|
package/dist/map/create-map.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { MapContext, MapContextLayer } from "@geospatial-sdk/core";
|
|
2
2
|
import { Map, MapOptions } from "maplibre-gl";
|
|
3
3
|
import { PartialStyleSpecification } from "../maplibre.models.js";
|
|
4
|
-
export declare function createLayer(layerModel: MapContextLayer, sourcePosition: number): Promise<PartialStyleSpecification>;
|
|
5
4
|
/**
|
|
6
|
-
* Create
|
|
5
|
+
* Create a Maplibre layer from a MapContextLayer. Returns null if the layer could not be created.
|
|
6
|
+
* @param layerModel
|
|
7
|
+
*/
|
|
8
|
+
export declare function createLayer(layerModel: MapContextLayer): Promise<PartialStyleSpecification | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a Maplibre map from a context; map options need to be provided
|
|
7
11
|
* @param context
|
|
8
|
-
* @param
|
|
12
|
+
* @param mapOptions
|
|
9
13
|
*/
|
|
10
14
|
export declare function createMapFromContext(context: MapContext, mapOptions: MapOptions): Promise<Map>;
|
|
11
15
|
/**
|
|
@@ -1 +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,
|
|
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,EAAsB,GAAG,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAYlE,OAAO,EAEL,yBAAyB,EAC1B,MAAM,uBAAuB,CAAC;AAO/B;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAiI3C;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,CAqBd"}
|
package/dist/map/create-map.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { removeSearchParams, } from "@geospatial-sdk/core";
|
|
2
|
-
import { Map
|
|
2
|
+
import { Map } from "maplibre-gl";
|
|
3
3
|
import { OgcApiEndpoint, WfsEndpoint, WmsEndpoint, } from "@camptocamp/ogc-client";
|
|
4
|
-
import { createDatasetFromGeoJsonLayer, generateLayerId, } from "../helpers/map.helpers.js";
|
|
4
|
+
import { createDatasetFromGeoJsonLayer, generateLayerHashWithoutUpdatableProps, generateLayerId, } from "../helpers/map.helpers.js";
|
|
5
5
|
const featureCollection = {
|
|
6
6
|
type: "FeatureCollection",
|
|
7
7
|
features: [],
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Create a Maplibre layer from a MapContextLayer. Returns null if the layer could not be created.
|
|
11
|
+
* @param layerModel
|
|
12
|
+
*/
|
|
13
|
+
export async function createLayer(layerModel) {
|
|
10
14
|
const { type } = layerModel;
|
|
15
|
+
const metadata = "id" in layerModel
|
|
16
|
+
? { layerId: layerModel.id }
|
|
17
|
+
: { layerHash: generateLayerHashWithoutUpdatableProps(layerModel) };
|
|
18
|
+
const layerId = generateLayerId();
|
|
11
19
|
switch (type) {
|
|
12
20
|
case "wms": {
|
|
13
|
-
const layerId = generateLayerId(layerModel);
|
|
14
21
|
const sourceId = layerId;
|
|
15
22
|
const endpoint = await new WmsEndpoint(layerModel.url).isReady();
|
|
16
23
|
let url = endpoint.getMapUrl([layerModel.name], {
|
|
@@ -22,7 +29,7 @@ export async function createLayer(layerModel, sourcePosition) {
|
|
|
22
29
|
});
|
|
23
30
|
url = removeSearchParams(url, ["bbox"]);
|
|
24
31
|
url = `${url.toString()}&BBOX={bbox-epsg-3857}`;
|
|
25
|
-
|
|
32
|
+
return {
|
|
26
33
|
sources: {
|
|
27
34
|
[sourceId]: {
|
|
28
35
|
type: "raster",
|
|
@@ -35,14 +42,16 @@ export async function createLayer(layerModel, sourcePosition) {
|
|
|
35
42
|
id: layerId,
|
|
36
43
|
type: "raster",
|
|
37
44
|
source: sourceId,
|
|
38
|
-
paint: {
|
|
39
|
-
|
|
40
|
-
sourcePosition,
|
|
45
|
+
paint: {
|
|
46
|
+
"raster-opacity": layerModel.opacity ?? 1,
|
|
41
47
|
},
|
|
48
|
+
layout: {
|
|
49
|
+
visibility: layerModel.visibility === false ? "none" : "visible",
|
|
50
|
+
},
|
|
51
|
+
metadata,
|
|
42
52
|
},
|
|
43
53
|
],
|
|
44
54
|
};
|
|
45
|
-
return dataset;
|
|
46
55
|
}
|
|
47
56
|
case "wfs": {
|
|
48
57
|
const entryPoint = await new WfsEndpoint(layerModel.url).isReady();
|
|
@@ -50,7 +59,7 @@ export async function createLayer(layerModel, sourcePosition) {
|
|
|
50
59
|
asJson: true,
|
|
51
60
|
outputCrs: "EPSG:4326",
|
|
52
61
|
});
|
|
53
|
-
return createDatasetFromGeoJsonLayer(layerModel, url,
|
|
62
|
+
return createDatasetFromGeoJsonLayer(layerModel, url, metadata);
|
|
54
63
|
}
|
|
55
64
|
case "geojson": {
|
|
56
65
|
let geojson;
|
|
@@ -72,33 +81,63 @@ export async function createLayer(layerModel, sourcePosition) {
|
|
|
72
81
|
geojson = data;
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
|
-
return createDatasetFromGeoJsonLayer(layerModel, geojson,
|
|
84
|
+
return createDatasetFromGeoJsonLayer(layerModel, geojson, metadata);
|
|
76
85
|
}
|
|
77
86
|
case "ogcapi": {
|
|
78
87
|
const ogcEndpoint = new OgcApiEndpoint(layerModel.url);
|
|
79
|
-
let layerUrl;
|
|
80
88
|
if (layerModel.useTiles) {
|
|
81
89
|
console.warn("[Warning] OGC API - Tiles not yet implemented.");
|
|
90
|
+
return null;
|
|
82
91
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return createDatasetFromGeoJsonLayer(layerModel, layerUrl, sourcePosition);
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
92
|
+
const layerUrl = await ogcEndpoint.getCollectionItemsUrl(layerModel.collection, { ...layerModel.options, asJson: true });
|
|
93
|
+
return createDatasetFromGeoJsonLayer(layerModel, layerUrl, metadata);
|
|
88
94
|
}
|
|
89
95
|
case "maplibre-style": {
|
|
90
96
|
console.warn("[Warning] Maplibre style - Not yet fully implemented.");
|
|
91
97
|
const style = await fetch(layerModel.styleUrl).then((res) => res.json());
|
|
92
|
-
style.layers?.forEach((layer) => (layer.metadata =
|
|
98
|
+
style.layers?.forEach((layer) => (layer.metadata = metadata));
|
|
93
99
|
return style;
|
|
94
100
|
}
|
|
101
|
+
case "xyz": {
|
|
102
|
+
const sourceId = layerId;
|
|
103
|
+
return {
|
|
104
|
+
sources: {
|
|
105
|
+
[sourceId]: {
|
|
106
|
+
type: "raster",
|
|
107
|
+
tiles: [layerModel.url],
|
|
108
|
+
tileSize: 256,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
layers: [
|
|
112
|
+
{
|
|
113
|
+
id: layerId,
|
|
114
|
+
type: "raster",
|
|
115
|
+
source: sourceId,
|
|
116
|
+
paint: {
|
|
117
|
+
"raster-opacity": layerModel.opacity ?? 1,
|
|
118
|
+
},
|
|
119
|
+
layout: {
|
|
120
|
+
visibility: layerModel.visibility === false ? "none" : "visible",
|
|
121
|
+
},
|
|
122
|
+
metadata,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
case "wmts": {
|
|
128
|
+
console.warn(`WMTS layers are not yet supported in Maplibre`, layerModel);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
default: {
|
|
132
|
+
console.error(`Layer could not be created`, layerModel);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
95
135
|
}
|
|
96
|
-
return {};
|
|
97
136
|
}
|
|
98
137
|
/**
|
|
99
|
-
* Create
|
|
138
|
+
* Create a Maplibre map from a context; map options need to be provided
|
|
100
139
|
* @param context
|
|
101
|
-
* @param
|
|
140
|
+
* @param mapOptions
|
|
102
141
|
*/
|
|
103
142
|
export async function createMapFromContext(context, mapOptions) {
|
|
104
143
|
const map = new Map(mapOptions);
|
|
@@ -114,7 +153,9 @@ export async function resetMapFromContext(map, context) {
|
|
|
114
153
|
map.setCenter(context.view.center);
|
|
115
154
|
for (let i = 0; i < context.layers.length; i++) {
|
|
116
155
|
const layerModel = context.layers[i];
|
|
117
|
-
const partialMLStyle = await createLayer(layerModel
|
|
156
|
+
const partialMLStyle = await createLayer(layerModel);
|
|
157
|
+
if (!partialMLStyle)
|
|
158
|
+
continue;
|
|
118
159
|
if (partialMLStyle.glyphs) {
|
|
119
160
|
map.setGlyphs(partialMLStyle.glyphs);
|
|
120
161
|
}
|
|
@@ -2,7 +2,8 @@ import { BackgroundLayerSpecification, LayerSpecification, StyleSpecification }
|
|
|
2
2
|
import { MapContextLayerGeojson, MapContextLayerOgcApi, MapContextLayerWfs } from "@geospatial-sdk/core";
|
|
3
3
|
export type LayerSpecificationWithSource = Exclude<LayerSpecification, BackgroundLayerSpecification>;
|
|
4
4
|
export interface LayerMetadataSpecification {
|
|
5
|
-
|
|
5
|
+
layerId?: number | string;
|
|
6
|
+
layerHash?: string;
|
|
6
7
|
}
|
|
7
8
|
export type LayerContextWithStyle = MapContextLayerWfs | MapContextLayerOgcApi | MapContextLayerGeojson;
|
|
8
9
|
export type Dataset = Pick<StyleSpecification, "sources" | "layers">;
|
|
@@ -1 +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,
|
|
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,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;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"}
|