@geospatial-sdk/core 0.0.5-dev.0 → 0.0.5-dev.0.sha-5830a6a
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/README.md +38 -5
- package/dist/constant/projections.d.ts +2 -0
- package/dist/constant/projections.d.ts.map +1 -0
- package/dist/constant/projections.js +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/model/events.d.ts +86 -0
- package/dist/model/events.d.ts.map +1 -0
- package/dist/model/events.js +37 -0
- package/dist/model/index.d.ts +9 -2
- package/dist/model/index.d.ts.map +1 -1
- package/dist/model/index.js +9 -2
- package/dist/model/map-context-diff.d.ts +11 -3
- package/dist/model/map-context-diff.d.ts.map +1 -1
- package/dist/model/map-context.d.ts +174 -22
- package/dist/model/map-context.d.ts.map +1 -1
- package/dist/model/resolved-map-state.d.ts +49 -0
- package/dist/model/resolved-map-state.d.ts.map +1 -0
- package/dist/model/resolved-map-state.js +1 -0
- package/dist/model/style.d.ts +3 -0
- package/dist/model/style.d.ts.map +1 -0
- package/dist/model/style.js +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +28 -0
- package/dist/utils/index.d.ts +12 -4
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +12 -4
- package/dist/utils/map-context-diff.d.ts +1 -1
- package/dist/utils/map-context-diff.d.ts.map +1 -1
- package/dist/utils/map-context-diff.js +19 -11
- package/dist/utils/map-context-layer.d.ts +13 -0
- package/dist/utils/map-context-layer.d.ts.map +1 -0
- package/dist/utils/map-context-layer.js +44 -0
- package/dist/utils/map-context.d.ts +48 -4
- package/dist/utils/map-context.d.ts.map +1 -1
- package/dist/utils/map-context.js +80 -29
- package/dist/utils/style.d.ts +4 -0
- package/dist/utils/style.d.ts.map +1 -0
- package/dist/utils/style.js +68 -0
- package/dist/utils/view.d.ts +25 -0
- package/dist/utils/view.d.ts.map +1 -0
- package/dist/utils/view.js +126 -0
- package/lib/constant/projections.ts +1 -0
- package/lib/index.ts +8 -3
- package/lib/model/events.ts +113 -0
- package/lib/model/index.ts +10 -2
- package/lib/model/map-context-diff.ts +12 -3
- package/lib/model/map-context.ts +219 -26
- package/lib/model/resolved-map-state.ts +67 -0
- package/lib/model/style.ts +3 -0
- package/lib/utils/freeze.test.ts +1 -1
- package/lib/utils/hash.test.ts +180 -0
- package/lib/utils/hash.ts +28 -0
- package/lib/utils/index.ts +20 -4
- package/lib/utils/map-context-diff.test.ts +240 -10
- package/lib/utils/map-context-diff.ts +23 -14
- package/lib/utils/map-context-layer.test.ts +357 -0
- package/lib/utils/map-context-layer.ts +66 -0
- package/lib/utils/map-context.test.ts +136 -272
- package/lib/utils/map-context.ts +108 -39
- package/lib/utils/style.ts +71 -0
- package/lib/utils/url.test.ts +1 -1
- package/lib/utils/view.test.ts +146 -0
- package/lib/utils/view.ts +163 -0
- package/package.json +20 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function isGeoJsonGeometry(object) {
|
|
2
|
+
return "type" in object && ("coordinates" in object || "features" in object);
|
|
3
|
+
}
|
|
4
|
+
const bigintReplacer = (key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
5
|
+
export function getHash(input, ignoreKeys = []) {
|
|
6
|
+
if (input instanceof Date) {
|
|
7
|
+
return input.toISOString();
|
|
8
|
+
}
|
|
9
|
+
if (input instanceof Object && isGeoJsonGeometry(input)) {
|
|
10
|
+
return JSON.stringify(input, bigintReplacer); // do not compute an actual hash as it will take too long
|
|
11
|
+
}
|
|
12
|
+
else if (input instanceof Object) {
|
|
13
|
+
const obj = {};
|
|
14
|
+
const keys = Object.keys(input).sort();
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
if (ignoreKeys.includes(key))
|
|
17
|
+
continue;
|
|
18
|
+
obj[key] = getHash(input[key]);
|
|
19
|
+
}
|
|
20
|
+
const hash = JSON.stringify(obj, bigintReplacer)
|
|
21
|
+
.split("")
|
|
22
|
+
.reduce((prev, curr) => (prev << 5) - prev + curr.charCodeAt(0), 0);
|
|
23
|
+
return (hash >>> 0).toString();
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return JSON.stringify(input, bigintReplacer);
|
|
27
|
+
}
|
|
28
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @group Utils
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
export * from "./url.js";
|
|
6
|
+
export * from "./freeze.js";
|
|
7
|
+
export * from "./hash.js";
|
|
8
|
+
export { computeMapContextDiff } from "./map-context-diff.js";
|
|
9
|
+
export { getLayerPosition, addLayerToContext, removeLayerFromContext, replaceLayerInContext, changeLayerPositionInContext, updateLayerInContext, } from "./map-context.js";
|
|
10
|
+
export { updateLayer } from "./map-context-layer.js";
|
|
11
|
+
export { createViewFromLayer } from "./view.js";
|
|
12
|
+
export * from "./style.js";
|
|
5
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,cAAc,YAAY,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @group Utils
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
export * from "./url.js";
|
|
6
|
+
export * from "./freeze.js";
|
|
7
|
+
export * from "./hash.js";
|
|
8
|
+
export { computeMapContextDiff } from "./map-context-diff.js";
|
|
9
|
+
export { getLayerPosition, addLayerToContext, removeLayerFromContext, replaceLayerInContext, changeLayerPositionInContext, updateLayerInContext, } from "./map-context.js";
|
|
10
|
+
export { updateLayer } from "./map-context-layer.js";
|
|
11
|
+
export { createViewFromLayer } from "./view.js";
|
|
12
|
+
export * from "./style.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MapContext, MapContextDiff } from "../model";
|
|
1
|
+
import { MapContext, MapContextDiff } from "../model/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* The following logic is produced by identifying layers in both context
|
|
4
4
|
* and determining whether they have been added, removed, changed or reordered.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map-context-diff.d.ts","sourceRoot":"","sources":["../../lib/utils/map-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,cAAc,EAKf,MAAM,
|
|
1
|
+
{"version":3,"file":"map-context-diff.d.ts","sourceRoot":"","sources":["../../lib/utils/map-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,cAAc,EAKf,MAAM,mBAAmB,CAAC;AAG3B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,UAAU,EACvB,eAAe,EAAE,UAAU,GAC1B,cAAc,CA8EhB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isLayerSame, isLayerSameAndUnchanged } from "./map-context";
|
|
1
|
+
import { isLayerSame, isLayerSameAndUnchanged } from "./map-context-layer.js";
|
|
2
2
|
/**
|
|
3
3
|
* The following logic is produced by identifying layers in both context
|
|
4
4
|
* and determining whether they have been added, removed, changed or reordered.
|
|
@@ -31,7 +31,6 @@ export function computeMapContextDiff(nextContext, previousContext) {
|
|
|
31
31
|
const layersReordered = [];
|
|
32
32
|
const layersRemoved = [];
|
|
33
33
|
const layersAdded = [];
|
|
34
|
-
const viewChanges = {};
|
|
35
34
|
// loop on prev context layers (for removed layers)
|
|
36
35
|
for (let i = 0; i < previousContext.layers.length; i++) {
|
|
37
36
|
const layer = previousContext.layers[i];
|
|
@@ -51,29 +50,38 @@ export function computeMapContextDiff(nextContext, previousContext) {
|
|
|
51
50
|
else {
|
|
52
51
|
const prevLayer = previousContext.layers[prevPosition];
|
|
53
52
|
if (!isLayerSameAndUnchanged(layer, prevLayer)) {
|
|
54
|
-
layersChanged.push({ layer, position: i });
|
|
53
|
+
layersChanged.push({ layer, previousLayer: prevLayer, position: i });
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
|
-
// look for moved layers
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
for (let i = 0; i <
|
|
62
|
-
const layer =
|
|
63
|
-
|
|
57
|
+
// look for moved layers (ignore removed and added ones)
|
|
58
|
+
const movedLayersInPrevious = previousContext.layers.filter((l) => !layersRemoved.find(({ layer }) => l === layer));
|
|
59
|
+
const movedLayersInNext = nextContext.layers.filter((l) => !layersAdded.find(({ layer }) => l === layer));
|
|
60
|
+
for (let i = 0; i < movedLayersInNext.length; i++) {
|
|
61
|
+
const layer = movedLayersInNext[i];
|
|
62
|
+
let prevPosition = getLayerPosition(layer, movedLayersInPrevious);
|
|
64
63
|
if (i !== prevPosition) {
|
|
64
|
+
// shift prevPosition of a move layer when others layers were added before them
|
|
65
|
+
for (const { position: addedPos } of layersAdded) {
|
|
66
|
+
if (addedPos <= prevPosition)
|
|
67
|
+
prevPosition++;
|
|
68
|
+
}
|
|
65
69
|
layersReordered.push({
|
|
66
70
|
layer,
|
|
67
71
|
newPosition: getLayerPosition(layer, nextContext.layers),
|
|
68
|
-
previousPosition:
|
|
72
|
+
previousPosition: prevPosition,
|
|
69
73
|
});
|
|
70
74
|
}
|
|
71
75
|
}
|
|
76
|
+
let viewChanges = nextContext.view !== previousContext.view ? nextContext.view : undefined;
|
|
77
|
+
if (viewChanges !== null && viewChanges !== undefined) {
|
|
78
|
+
viewChanges = { ...viewChanges }; // copy the view to avoid unexpected mutations
|
|
79
|
+
}
|
|
72
80
|
return {
|
|
73
81
|
layersAdded,
|
|
74
82
|
layersChanged,
|
|
75
83
|
layersRemoved,
|
|
76
84
|
layersReordered,
|
|
77
|
-
viewChanges,
|
|
85
|
+
...(viewChanges !== undefined && { viewChanges }),
|
|
78
86
|
};
|
|
79
87
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MapContextLayer } from "../model/index.js";
|
|
2
|
+
export declare function getLayerHash(layer: MapContextLayer, includeExtras?: boolean): string;
|
|
3
|
+
export declare function isLayerSame(layerA: MapContextLayer, layerB: MapContextLayer): boolean;
|
|
4
|
+
export declare function isLayerSameAndUnchanged(layerA: MapContextLayer, layerB: MapContextLayer): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Applies the `updates` partial to the given layer. The layer will also be
|
|
7
|
+
* adjusted depending on the presence of the `id` and `version` fields.
|
|
8
|
+
* Note: any property set to `undefined` in `updates` will be removed from the resulting layer.
|
|
9
|
+
* @param layer
|
|
10
|
+
* @param updates
|
|
11
|
+
*/
|
|
12
|
+
export declare function updateLayer(layer: MapContextLayer, updates: Partial<MapContextLayer>): MapContextLayer;
|
|
13
|
+
//# sourceMappingURL=map-context-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-context-layer.d.ts","sourceRoot":"","sources":["../../lib/utils/map-context-layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,EACtB,aAAa,UAAQ,GACpB,MAAM,CAER;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,eAAe,GACtB,OAAO,CAKT;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,eAAe,GACtB,OAAO,CAST;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,eAAe,CAqBjB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getHash } from "./hash.js";
|
|
2
|
+
export function getLayerHash(layer, includeExtras = false) {
|
|
3
|
+
return getHash(layer, includeExtras ? [] : ["extras"]);
|
|
4
|
+
}
|
|
5
|
+
export function isLayerSame(layerA, layerB) {
|
|
6
|
+
if ("id" in layerA && "id" in layerB) {
|
|
7
|
+
return layerA.id == layerB.id;
|
|
8
|
+
}
|
|
9
|
+
return getLayerHash(layerA) === getLayerHash(layerB);
|
|
10
|
+
}
|
|
11
|
+
export function isLayerSameAndUnchanged(layerA, layerB) {
|
|
12
|
+
if ("id" in layerA &&
|
|
13
|
+
"id" in layerB &&
|
|
14
|
+
("version" in layerA || "version" in layerB)) {
|
|
15
|
+
return layerA.id == layerB.id && layerA.version == layerB.version;
|
|
16
|
+
}
|
|
17
|
+
return getLayerHash(layerA, true) === getLayerHash(layerB, true);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Applies the `updates` partial to the given layer. The layer will also be
|
|
21
|
+
* adjusted depending on the presence of the `id` and `version` fields.
|
|
22
|
+
* Note: any property set to `undefined` in `updates` will be removed from the resulting layer.
|
|
23
|
+
* @param layer
|
|
24
|
+
* @param updates
|
|
25
|
+
*/
|
|
26
|
+
export function updateLayer(layer, updates) {
|
|
27
|
+
const updatedLayer = {
|
|
28
|
+
...layer,
|
|
29
|
+
...updates,
|
|
30
|
+
};
|
|
31
|
+
const versionExplicitlyUpdated = "version" in updates && updates.version !== undefined;
|
|
32
|
+
if ("id" in updatedLayer &&
|
|
33
|
+
"version" in updatedLayer &&
|
|
34
|
+
typeof updatedLayer.version === "number" &&
|
|
35
|
+
!versionExplicitlyUpdated) {
|
|
36
|
+
updatedLayer.version = updatedLayer.version + 1;
|
|
37
|
+
}
|
|
38
|
+
for (const key in updatedLayer) {
|
|
39
|
+
if (updatedLayer[key] === undefined) {
|
|
40
|
+
delete updatedLayer[key];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return updatedLayer;
|
|
44
|
+
}
|
|
@@ -1,6 +1,50 @@
|
|
|
1
|
-
import { MapContext, MapContextLayer } from "../model";
|
|
2
|
-
export declare function getLayerHash(layer: MapContextLayer, includeExtras?: boolean): string;
|
|
3
|
-
export declare function isLayerSame(layerA: MapContextLayer, layerB: MapContextLayer): boolean;
|
|
4
|
-
export declare function isLayerSameAndUnchanged(layerA: MapContextLayer, layerB: MapContextLayer): boolean;
|
|
1
|
+
import { MapContext, MapContextLayer } from "../model/index.js";
|
|
5
2
|
export declare function getLayerPosition(context: MapContext, layerModel: MapContextLayer): number;
|
|
3
|
+
/**
|
|
4
|
+
* Adds a layer to the context at a specific position or at the end if no position is specified.
|
|
5
|
+
*
|
|
6
|
+
* @param context The current map context.
|
|
7
|
+
* @param layerModel The layer to be added.
|
|
8
|
+
* @param [position] The position at which to add the layer. If not specified, the layer is added at the end.
|
|
9
|
+
* @returns The new map context with the added layer.
|
|
10
|
+
*/
|
|
11
|
+
export declare function addLayerToContext(context: MapContext, layerModel: MapContextLayer, position?: number): MapContext;
|
|
12
|
+
/**
|
|
13
|
+
* Removes a layer from the context.
|
|
14
|
+
*
|
|
15
|
+
* @param context The current map context.
|
|
16
|
+
* @param layerModel The layer to be removed.
|
|
17
|
+
* @returns The new map context without the removed layer.
|
|
18
|
+
*/
|
|
19
|
+
export declare function removeLayerFromContext(context: MapContext, layerModel: MapContextLayer): MapContext;
|
|
20
|
+
/**
|
|
21
|
+
* Replaces a layer in the context with a new layer.
|
|
22
|
+
*
|
|
23
|
+
* @param context The current map context.
|
|
24
|
+
* @param layerModel The layer to be replaced.
|
|
25
|
+
* @param replacement The new layer that will replace the old one.
|
|
26
|
+
* @returns The new map context with the replaced layer.
|
|
27
|
+
*/
|
|
28
|
+
export declare function replaceLayerInContext(context: MapContext, layerModel: MapContextLayer, replacement: MapContextLayer): MapContext;
|
|
29
|
+
/**
|
|
30
|
+
* Updates an existing layer in the context by applying partial changes.
|
|
31
|
+
*
|
|
32
|
+
* Note: setting a property to `undefined` in the partial layer updates will remove that
|
|
33
|
+
* property from the existing layer.
|
|
34
|
+
*
|
|
35
|
+
* @param context The current map context.
|
|
36
|
+
* @param layerModel The layer to be updated.
|
|
37
|
+
* @param layerUpdates A partial layer object containing the properties to update on the existing layer.
|
|
38
|
+
* @returns The new map context with the updated layer.
|
|
39
|
+
*/
|
|
40
|
+
export declare function updateLayerInContext(context: MapContext, layerModel: MapContextLayer, layerUpdates: Partial<MapContextLayer>): MapContext;
|
|
41
|
+
/**
|
|
42
|
+
* Changes the position of a layer in the context.
|
|
43
|
+
*
|
|
44
|
+
* @param context The current map context.
|
|
45
|
+
* @param layerModel The layer whose position is to be changed.
|
|
46
|
+
* @param position The new position for the layer.
|
|
47
|
+
* @returns The new map context with the layer moved to the new position.
|
|
48
|
+
*/
|
|
49
|
+
export declare function changeLayerPositionInContext(context: MapContext, layerModel: MapContextLayer, position: number): MapContext;
|
|
6
50
|
//# sourceMappingURL=map-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map-context.d.ts","sourceRoot":"","sources":["../../lib/utils/map-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"map-context.d.ts","sourceRoot":"","sources":["../../lib/utils/map-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGhE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,GAC1B,MAAM,CAER;AAED;;;;;;;GAOG;AAEH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,CAQZ;AAED;;;;;;GAMG;AAEH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,GAC1B,UAAU,CAOZ;AAED;;;;;;;GAOG;AAEH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,eAAe,GAC3B,UAAU,CAOZ;AAED;;;;;;;;;;GAUG;AAEH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,EAC3B,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,GACrC,UAAU,CAUZ;AAED;;;;;;;GAOG;AAEH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,EAC3B,QAAQ,EAAE,MAAM,GACf,UAAU,CAGZ"}
|
|
@@ -1,36 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { isLayerSame, updateLayer } from "./map-context-layer.js";
|
|
2
|
+
export function getLayerPosition(context, layerModel) {
|
|
3
|
+
return context.layers.findIndex((l) => isLayerSame(layerModel, l));
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Adds a layer to the context at a specific position or at the end if no position is specified.
|
|
7
|
+
*
|
|
8
|
+
* @param context The current map context.
|
|
9
|
+
* @param layerModel The layer to be added.
|
|
10
|
+
* @param [position] The position at which to add the layer. If not specified, the layer is added at the end.
|
|
11
|
+
* @returns The new map context with the added layer.
|
|
12
|
+
*/
|
|
13
|
+
export function addLayerToContext(context, layerModel, position) {
|
|
14
|
+
const newContext = { ...context, layers: [...context.layers] };
|
|
15
|
+
if (position !== undefined) {
|
|
16
|
+
newContext.layers.splice(position, 0, layerModel);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
newContext.layers.push(layerModel);
|
|
19
20
|
}
|
|
20
|
-
return
|
|
21
|
+
return newContext;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Removes a layer from the context.
|
|
25
|
+
*
|
|
26
|
+
* @param context The current map context.
|
|
27
|
+
* @param layerModel The layer to be removed.
|
|
28
|
+
* @returns The new map context without the removed layer.
|
|
29
|
+
*/
|
|
30
|
+
export function removeLayerFromContext(context, layerModel) {
|
|
31
|
+
const newContext = { ...context, layers: [...context.layers] };
|
|
32
|
+
const position = getLayerPosition(context, layerModel);
|
|
33
|
+
if (position >= 0) {
|
|
34
|
+
newContext.layers.splice(position, 1);
|
|
25
35
|
}
|
|
26
|
-
return
|
|
36
|
+
return newContext;
|
|
27
37
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Replaces a layer in the context with a new layer.
|
|
40
|
+
*
|
|
41
|
+
* @param context The current map context.
|
|
42
|
+
* @param layerModel The layer to be replaced.
|
|
43
|
+
* @param replacement The new layer that will replace the old one.
|
|
44
|
+
* @returns The new map context with the replaced layer.
|
|
45
|
+
*/
|
|
46
|
+
export function replaceLayerInContext(context, layerModel, replacement) {
|
|
47
|
+
const newContext = { ...context, layers: [...context.layers] };
|
|
48
|
+
const position = getLayerPosition(context, layerModel);
|
|
49
|
+
if (position >= 0) {
|
|
50
|
+
newContext.layers.splice(position, 1, replacement);
|
|
31
51
|
}
|
|
32
|
-
return
|
|
52
|
+
return newContext;
|
|
33
53
|
}
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Updates an existing layer in the context by applying partial changes.
|
|
56
|
+
*
|
|
57
|
+
* Note: setting a property to `undefined` in the partial layer updates will remove that
|
|
58
|
+
* property from the existing layer.
|
|
59
|
+
*
|
|
60
|
+
* @param context The current map context.
|
|
61
|
+
* @param layerModel The layer to be updated.
|
|
62
|
+
* @param layerUpdates A partial layer object containing the properties to update on the existing layer.
|
|
63
|
+
* @returns The new map context with the updated layer.
|
|
64
|
+
*/
|
|
65
|
+
export function updateLayerInContext(context, layerModel, layerUpdates) {
|
|
66
|
+
const position = getLayerPosition(context, layerModel);
|
|
67
|
+
if (position >= 0) {
|
|
68
|
+
const existing = context.layers[position];
|
|
69
|
+
const updated = updateLayer(existing, layerUpdates);
|
|
70
|
+
return replaceLayerInContext(context, layerModel, updated);
|
|
71
|
+
}
|
|
72
|
+
// we're building a new context so that the reference is changed anyway; this is done to be
|
|
73
|
+
// consistent with other utilities that always change the context reference even if the layer wasn't found
|
|
74
|
+
return { ...context };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Changes the position of a layer in the context.
|
|
78
|
+
*
|
|
79
|
+
* @param context The current map context.
|
|
80
|
+
* @param layerModel The layer whose position is to be changed.
|
|
81
|
+
* @param position The new position for the layer.
|
|
82
|
+
* @returns The new map context with the layer moved to the new position.
|
|
83
|
+
*/
|
|
84
|
+
export function changeLayerPositionInContext(context, layerModel, position) {
|
|
85
|
+
const newContext = removeLayerFromContext(context, layerModel);
|
|
86
|
+
return addLayerToContext(newContext, layerModel, position);
|
|
36
87
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../lib/utils/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD,eAAO,MAAM,YAAY,EAAE,aA+B1B,CAAC;AAIF,eAAO,MAAM,qBAAqB,EAAE,aA+BnC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const color = "rgb(0, 0, 255)";
|
|
2
|
+
const fillColor = "rgba(0, 0, 255, 0.25)";
|
|
3
|
+
export const defaultStyle = [
|
|
4
|
+
{
|
|
5
|
+
filter: ["==", ["geometry-type"], "LineString"],
|
|
6
|
+
style: [
|
|
7
|
+
{
|
|
8
|
+
"stroke-color": "white",
|
|
9
|
+
"stroke-width": 6,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"stroke-color": color,
|
|
13
|
+
"stroke-width": 2,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
filter: ["==", ["geometry-type"], "Polygon"],
|
|
19
|
+
style: {
|
|
20
|
+
"stroke-color": "white",
|
|
21
|
+
"stroke-width": 2,
|
|
22
|
+
"fill-color": fillColor,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
filter: ["==", ["geometry-type"], "Point"],
|
|
27
|
+
style: {
|
|
28
|
+
"circle-fill-color": color,
|
|
29
|
+
"circle-radius": 7,
|
|
30
|
+
"circle-stroke-color": "white",
|
|
31
|
+
"circle-stroke-width": 2,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
const hlColor = "rgb(0, 0, 255)";
|
|
36
|
+
const hlFillColor = "rgba(0, 0, 255, 0.25)";
|
|
37
|
+
export const defaultHighlightStyle = [
|
|
38
|
+
{
|
|
39
|
+
filter: ["==", ["geometry-type"], "LineString"],
|
|
40
|
+
style: [
|
|
41
|
+
{
|
|
42
|
+
"stroke-color": "white",
|
|
43
|
+
"stroke-width": 8,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"stroke-color": hlColor,
|
|
47
|
+
"stroke-width": 3,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
filter: ["==", ["geometry-type"], "Polygon"],
|
|
53
|
+
style: {
|
|
54
|
+
"stroke-color": "white",
|
|
55
|
+
"stroke-width": 3,
|
|
56
|
+
"fill-color": hlFillColor,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
filter: ["==", ["geometry-type"], "Point"],
|
|
61
|
+
style: {
|
|
62
|
+
"circle-fill-color": hlColor,
|
|
63
|
+
"circle-radius": 8,
|
|
64
|
+
"circle-stroke-color": "white",
|
|
65
|
+
"circle-stroke-width": 3,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MapContextLayer, MapContextView } from "../model/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a view from a layer by extracting its geographic extent. The returned extent is
|
|
4
|
+
* always expressed in EPSG:4326 (longitude/latitude) coordinates.
|
|
5
|
+
*
|
|
6
|
+
* @param layer - The map context layer to extract the extent from
|
|
7
|
+
* @returns A Promise resolving to a `ViewByExtent` object, or `null` if the extent cannot be determined
|
|
8
|
+
* @throws {Error} If the layer type is not supported
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const wmsLayer: MapContextLayerWms = {
|
|
13
|
+
* type: 'wms',
|
|
14
|
+
* url: 'https://example.com/wms',
|
|
15
|
+
* name: 'myLayer'
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* const view = await createViewFromLayer(wmsLayer);
|
|
19
|
+
* if (view) {
|
|
20
|
+
* console.log('Extent:', view.extent); // [minX, minY, maxX, maxY] in EPSG:4326
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createViewFromLayer(layer: MapContextLayer): Promise<MapContextView | null>;
|
|
25
|
+
//# sourceMappingURL=view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../lib/utils/view.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,eAAe,EAKf,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAK3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAchC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { transformExtent } from "ol/proj.js";
|
|
2
|
+
import { WfsEndpoint, WmsEndpoint, WmtsEndpoint } from "@camptocamp/ogc-client";
|
|
3
|
+
import { LONLAT_CRS_CODES } from "../constant/projections.js";
|
|
4
|
+
import { fromEPSGCode } from "ol/proj/proj4.js";
|
|
5
|
+
import GeoJSON from "ol/format/GeoJSON.js";
|
|
6
|
+
import { extend } from "ol/extent.js";
|
|
7
|
+
import GeoTIFF from "ol/source/GeoTIFF.js";
|
|
8
|
+
const GEOJSON = new GeoJSON();
|
|
9
|
+
/**
|
|
10
|
+
* Creates a view from a layer by extracting its geographic extent. The returned extent is
|
|
11
|
+
* always expressed in EPSG:4326 (longitude/latitude) coordinates.
|
|
12
|
+
*
|
|
13
|
+
* @param layer - The map context layer to extract the extent from
|
|
14
|
+
* @returns A Promise resolving to a `ViewByExtent` object, or `null` if the extent cannot be determined
|
|
15
|
+
* @throws {Error} If the layer type is not supported
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const wmsLayer: MapContextLayerWms = {
|
|
20
|
+
* type: 'wms',
|
|
21
|
+
* url: 'https://example.com/wms',
|
|
22
|
+
* name: 'myLayer'
|
|
23
|
+
* };
|
|
24
|
+
*
|
|
25
|
+
* const view = await createViewFromLayer(wmsLayer);
|
|
26
|
+
* if (view) {
|
|
27
|
+
* console.log('Extent:', view.extent); // [minX, minY, maxX, maxY] in EPSG:4326
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export async function createViewFromLayer(layer) {
|
|
32
|
+
if (layer.type === "wms") {
|
|
33
|
+
return await getWmsLayerExtent(layer);
|
|
34
|
+
}
|
|
35
|
+
else if (layer.type === "wmts") {
|
|
36
|
+
return await getWmtsLayerExtent(layer);
|
|
37
|
+
}
|
|
38
|
+
else if (layer.type === "geojson" && layer.data) {
|
|
39
|
+
return computeExtentFromGeojson(layer.data);
|
|
40
|
+
}
|
|
41
|
+
else if (layer.type === "wfs") {
|
|
42
|
+
return await getWfsLayerExtent(layer);
|
|
43
|
+
}
|
|
44
|
+
else if (layer.type === "geotiff") {
|
|
45
|
+
return await getGeotiffLayerExtent(layer);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw new Error(`Unsupported layer type: ${layer.type}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function computeExtentFromGeojson(data) {
|
|
52
|
+
const geojson = typeof data === "string" ? JSON.parse(data) : data;
|
|
53
|
+
const features = GEOJSON.readFeatures(geojson);
|
|
54
|
+
const extent = features.reduce((prev, curr) => {
|
|
55
|
+
const geom = curr.getGeometry();
|
|
56
|
+
if (!geom)
|
|
57
|
+
return prev;
|
|
58
|
+
return extend(prev, geom.getExtent());
|
|
59
|
+
}, [Infinity, Infinity, -Infinity, -Infinity]);
|
|
60
|
+
return {
|
|
61
|
+
extent,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function getWmsLayerExtent(layer) {
|
|
65
|
+
const endpoint = await new WmsEndpoint(layer.url).isReady();
|
|
66
|
+
const { boundingBoxes } = endpoint.getLayerByName(layer.name);
|
|
67
|
+
if (!Object.keys(boundingBoxes).length) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const lonLatCRS = Object.keys(boundingBoxes).find((crs) => LONLAT_CRS_CODES.includes(crs));
|
|
71
|
+
if (lonLatCRS) {
|
|
72
|
+
return {
|
|
73
|
+
extent: boundingBoxes[lonLatCRS],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const availableEPSGCode = Object.keys(boundingBoxes)[0];
|
|
78
|
+
const proj = await fromEPSGCode(availableEPSGCode);
|
|
79
|
+
return {
|
|
80
|
+
extent: transformExtent(boundingBoxes[availableEPSGCode], proj, "EPSG:4326"),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function getWmtsLayerExtent(layer) {
|
|
85
|
+
const endpoint = await new WmtsEndpoint(layer.url).isReady();
|
|
86
|
+
const layerName = endpoint.getSingleLayerName() ?? layer.name;
|
|
87
|
+
const wmtsLayer = endpoint.getLayerByName(layerName);
|
|
88
|
+
return wmtsLayer.latLonBoundingBox
|
|
89
|
+
? {
|
|
90
|
+
extent: wmtsLayer.latLonBoundingBox,
|
|
91
|
+
}
|
|
92
|
+
: null;
|
|
93
|
+
}
|
|
94
|
+
async function getWfsLayerExtent(layer) {
|
|
95
|
+
const endpoint = await new WfsEndpoint(layer.url).isReady();
|
|
96
|
+
const featureTypeSummary = endpoint.getFeatureTypeSummary(layer.featureType);
|
|
97
|
+
const boundingBox = featureTypeSummary?.boundingBox;
|
|
98
|
+
if (!boundingBox) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
extent: boundingBox,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function getGeotiffLayerExtent(layer) {
|
|
106
|
+
const source = new GeoTIFF({
|
|
107
|
+
sources: [{ url: layer.url }],
|
|
108
|
+
});
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
source.on("change", () => {
|
|
111
|
+
if (source.getState() === "ready") {
|
|
112
|
+
const tileGrid = source.getTileGrid();
|
|
113
|
+
const projection = source.getProjection();
|
|
114
|
+
if (!tileGrid || !projection) {
|
|
115
|
+
resolve(null);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const extent = transformExtent(tileGrid.getExtent(), projection, "EPSG:4326");
|
|
119
|
+
resolve({ extent });
|
|
120
|
+
}
|
|
121
|
+
else if (source.getState() === "error") {
|
|
122
|
+
resolve(null);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LONLAT_CRS_CODES = ["EPSG:4326", "CRS:84"];
|