@geospatial-sdk/core 0.0.5-dev.0 → 0.0.5-dev.0.sha-bfaf14b

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.
Files changed (67) hide show
  1. package/README.md +38 -5
  2. package/dist/constant/projections.d.ts +2 -0
  3. package/dist/constant/projections.d.ts.map +1 -0
  4. package/dist/constant/projections.js +1 -0
  5. package/dist/index.d.ts +7 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +7 -3
  8. package/dist/model/events.d.ts +86 -0
  9. package/dist/model/events.d.ts.map +1 -0
  10. package/dist/model/events.js +37 -0
  11. package/dist/model/index.d.ts +9 -2
  12. package/dist/model/index.d.ts.map +1 -1
  13. package/dist/model/index.js +9 -2
  14. package/dist/model/map-context-diff.d.ts +11 -3
  15. package/dist/model/map-context-diff.d.ts.map +1 -1
  16. package/dist/model/map-context.d.ts +174 -22
  17. package/dist/model/map-context.d.ts.map +1 -1
  18. package/dist/model/resolved-map-state.d.ts +49 -0
  19. package/dist/model/resolved-map-state.d.ts.map +1 -0
  20. package/dist/model/resolved-map-state.js +1 -0
  21. package/dist/model/style.d.ts +3 -0
  22. package/dist/model/style.d.ts.map +1 -0
  23. package/dist/model/style.js +1 -0
  24. package/dist/utils/hash.d.ts +2 -0
  25. package/dist/utils/hash.d.ts.map +1 -0
  26. package/dist/utils/hash.js +28 -0
  27. package/dist/utils/index.d.ts +12 -4
  28. package/dist/utils/index.d.ts.map +1 -1
  29. package/dist/utils/index.js +12 -4
  30. package/dist/utils/map-context-diff.d.ts +1 -1
  31. package/dist/utils/map-context-diff.d.ts.map +1 -1
  32. package/dist/utils/map-context-diff.js +19 -11
  33. package/dist/utils/map-context-layer.d.ts +13 -0
  34. package/dist/utils/map-context-layer.d.ts.map +1 -0
  35. package/dist/utils/map-context-layer.js +44 -0
  36. package/dist/utils/map-context.d.ts +48 -4
  37. package/dist/utils/map-context.d.ts.map +1 -1
  38. package/dist/utils/map-context.js +80 -29
  39. package/dist/utils/style.d.ts +4 -0
  40. package/dist/utils/style.d.ts.map +1 -0
  41. package/dist/utils/style.js +68 -0
  42. package/dist/utils/view.d.ts +25 -0
  43. package/dist/utils/view.d.ts.map +1 -0
  44. package/dist/utils/view.js +126 -0
  45. package/lib/constant/projections.ts +1 -0
  46. package/lib/index.ts +8 -3
  47. package/lib/model/events.ts +113 -0
  48. package/lib/model/index.ts +10 -2
  49. package/lib/model/map-context-diff.ts +12 -3
  50. package/lib/model/map-context.ts +219 -26
  51. package/lib/model/resolved-map-state.ts +67 -0
  52. package/lib/model/style.ts +3 -0
  53. package/lib/utils/freeze.test.ts +1 -1
  54. package/lib/utils/hash.test.ts +180 -0
  55. package/lib/utils/hash.ts +28 -0
  56. package/lib/utils/index.ts +20 -4
  57. package/lib/utils/map-context-diff.test.ts +240 -10
  58. package/lib/utils/map-context-diff.ts +23 -14
  59. package/lib/utils/map-context-layer.test.ts +357 -0
  60. package/lib/utils/map-context-layer.ts +66 -0
  61. package/lib/utils/map-context.test.ts +136 -272
  62. package/lib/utils/map-context.ts +108 -39
  63. package/lib/utils/style.ts +71 -0
  64. package/lib/utils/url.test.ts +1 -1
  65. package/lib/utils/view.test.ts +146 -0
  66. package/lib/utils/view.ts +163 -0
  67. 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
+ }
@@ -1,5 +1,13 @@
1
- export * from "./url";
2
- export * from "./freeze";
3
- export { computeMapContextDiff } from "./map-context-diff";
4
- export { getLayerPosition } from "./map-context";
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,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,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"}
@@ -1,4 +1,12 @@
1
- export * from "./url";
2
- export * from "./freeze";
3
- export { computeMapContextDiff } from "./map-context-diff";
4
- export { getLayerPosition } from "./map-context";
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,UAAU,CAAC;AAGlB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,UAAU,EACvB,eAAe,EAAE,UAAU,GAC1B,cAAc,CAqEhB"}
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 prevLayersFiltered = previousContext.layers.filter((l) => !layersRemoved.find(({ layer }) => l === layer));
60
- const nextLayersFiltered = nextContext.layers.filter((l) => !layersAdded.find(({ layer }) => l === layer));
61
- for (let i = 0; i < nextLayersFiltered.length; i++) {
62
- const layer = nextLayersFiltered[i];
63
- const prevPosition = getLayerPosition(layer, prevLayersFiltered);
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: getLayerPosition(layer, previousContext.layers),
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,UAAU,CAAC;AAEvD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,EACtB,aAAa,UAAQ,GACpB,MAAM,CAkBR;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,CAKT;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,eAAe,GAC1B,MAAM,CAER"}
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
- export function getLayerHash(layer, includeExtras = false) {
2
- function getHash(input) {
3
- if (input instanceof Object) {
4
- const obj = {};
5
- const keys = Object.keys(input).sort();
6
- for (const key of keys) {
7
- if (!includeExtras && key === "extras")
8
- continue;
9
- obj[key] = getHash(input[key]);
10
- }
11
- const hash = JSON.stringify(obj)
12
- .split("")
13
- .reduce((prev, curr) => (prev << 5) - prev + curr.charCodeAt(0), 0);
14
- return (hash >>> 0).toString();
15
- }
16
- else {
17
- return JSON.stringify(input);
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 getHash(layer);
21
+ return newContext;
21
22
  }
22
- export function isLayerSame(layerA, layerB) {
23
- if ("id" in layerA && "id" in layerB) {
24
- return layerA.id == layerB.id;
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 getLayerHash(layerA) === getLayerHash(layerB);
36
+ return newContext;
27
37
  }
28
- export function isLayerSameAndUnchanged(layerA, layerB) {
29
- if ("id" in layerA && "id" in layerB) {
30
- return layerA.id == layerB.id && layerA.version == layerB.version;
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 getLayerHash(layerA, true) === getLayerHash(layerB, true);
52
+ return newContext;
33
53
  }
34
- export function getLayerPosition(context, layerModel) {
35
- return context.layers.findIndex((l) => isLayerSame(layerModel, l));
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,4 @@
1
+ import type { FlatStyleLike } from "ol/style/flat.js";
2
+ export declare const defaultStyle: FlatStyleLike;
3
+ export declare const defaultHighlightStyle: FlatStyleLike;
4
+ //# sourceMappingURL=style.d.ts.map
@@ -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"];
package/lib/index.ts CHANGED
@@ -1,3 +1,8 @@
1
- // PUBLIC API
2
- export * from "./utils";
3
- export * from "./model";
1
+ /**
2
+ * {@include ../README.md#body}
3
+ *
4
+ * @module 📦 core
5
+ */
6
+
7
+ export * from "./utils/index.js";
8
+ export * from "./model/index.js";