@geospatial-sdk/core 0.0.5-dev.46 → 0.0.5-dev.48
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/model/map-context-diff.d.ts +9 -1
- package/dist/model/map-context-diff.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/map-context-diff.d.ts.map +1 -1
- package/dist/utils/map-context-diff.js +2 -2
- 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 +27 -18
- package/dist/utils/map-context.d.ts.map +1 -1
- package/dist/utils/map-context.js +38 -33
- package/lib/model/map-context-diff.ts +10 -1
- package/lib/utils/index.ts +2 -0
- package/lib/utils/map-context-diff.test.ts +3 -0
- package/lib/utils/map-context-diff.ts +4 -3
- 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 +76 -305
- package/lib/utils/map-context.ts +44 -47
- package/package.json +2 -2
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { describe } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
getLayerHash,
|
|
4
|
+
isLayerSame,
|
|
5
|
+
isLayerSameAndUnchanged,
|
|
6
|
+
updateLayer,
|
|
7
|
+
} from "./map-context-layer.js";
|
|
8
|
+
import {
|
|
9
|
+
SAMPLE_LAYER1,
|
|
10
|
+
SAMPLE_LAYER2,
|
|
11
|
+
} from "../../fixtures/map-context.fixtures.js";
|
|
12
|
+
|
|
13
|
+
describe("Map context layer utils", () => {
|
|
14
|
+
describe("isLayerSame", () => {
|
|
15
|
+
describe("layers with id", () => {
|
|
16
|
+
it("compares non-strictly by id", () => {
|
|
17
|
+
expect(
|
|
18
|
+
isLayerSame(
|
|
19
|
+
{ ...SAMPLE_LAYER1, id: "a" },
|
|
20
|
+
{ ...SAMPLE_LAYER1, id: "b" },
|
|
21
|
+
),
|
|
22
|
+
).toBe(false);
|
|
23
|
+
expect(
|
|
24
|
+
isLayerSame(
|
|
25
|
+
{ ...SAMPLE_LAYER1, id: "ab" },
|
|
26
|
+
{ ...SAMPLE_LAYER2, id: "ab" },
|
|
27
|
+
),
|
|
28
|
+
).toBe(true);
|
|
29
|
+
expect(
|
|
30
|
+
isLayerSame(
|
|
31
|
+
{ ...SAMPLE_LAYER1, id: 1 },
|
|
32
|
+
{ ...SAMPLE_LAYER2, id: "01" },
|
|
33
|
+
),
|
|
34
|
+
).toBe(true);
|
|
35
|
+
expect(
|
|
36
|
+
isLayerSame({ ...SAMPLE_LAYER1, id: 1 }, { ...SAMPLE_LAYER2, id: 1 }),
|
|
37
|
+
).toBe(true);
|
|
38
|
+
expect(
|
|
39
|
+
isLayerSame({ ...SAMPLE_LAYER1, id: 1 }, { ...SAMPLE_LAYER1, id: 2 }),
|
|
40
|
+
).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("layers without id", () => {
|
|
44
|
+
it("compares by properties", () => {
|
|
45
|
+
expect(isLayerSame(SAMPLE_LAYER1, SAMPLE_LAYER1)).toBe(true);
|
|
46
|
+
expect(
|
|
47
|
+
isLayerSame(SAMPLE_LAYER1, { ...SAMPLE_LAYER1, name: "abc" }),
|
|
48
|
+
).toBe(false);
|
|
49
|
+
expect(
|
|
50
|
+
isLayerSame(
|
|
51
|
+
{
|
|
52
|
+
...SAMPLE_LAYER1,
|
|
53
|
+
url: "http://abc.org",
|
|
54
|
+
name: SAMPLE_LAYER1.name,
|
|
55
|
+
},
|
|
56
|
+
{ ...SAMPLE_LAYER1, url: "http://abc.org" },
|
|
57
|
+
),
|
|
58
|
+
).toBe(true);
|
|
59
|
+
expect(isLayerSame(SAMPLE_LAYER1, SAMPLE_LAYER2)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it("ignores extras prop", () => {
|
|
62
|
+
expect(
|
|
63
|
+
isLayerSame(SAMPLE_LAYER1, {
|
|
64
|
+
...SAMPLE_LAYER1,
|
|
65
|
+
extras: { otherProp: "abc" },
|
|
66
|
+
}),
|
|
67
|
+
).toBe(true);
|
|
68
|
+
expect(
|
|
69
|
+
isLayerSame(SAMPLE_LAYER1, {
|
|
70
|
+
...SAMPLE_LAYER1,
|
|
71
|
+
extras: undefined,
|
|
72
|
+
}),
|
|
73
|
+
).toBe(true);
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
75
|
+
const { extras, ...layer } = SAMPLE_LAYER1;
|
|
76
|
+
expect(isLayerSame(SAMPLE_LAYER1, layer)).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("layers with and without id", () => {
|
|
80
|
+
it("compares by properties", () => {
|
|
81
|
+
expect(
|
|
82
|
+
isLayerSame(SAMPLE_LAYER1, { ...SAMPLE_LAYER1, id: "123" }),
|
|
83
|
+
).toBe(false);
|
|
84
|
+
expect(
|
|
85
|
+
isLayerSame(
|
|
86
|
+
{ ...SAMPLE_LAYER1, id: "123" },
|
|
87
|
+
{ id: "123", ...SAMPLE_LAYER1, name: SAMPLE_LAYER1.name },
|
|
88
|
+
),
|
|
89
|
+
).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("isLayerSameAndUnchanged", () => {
|
|
95
|
+
describe("layers with id and version", () => {
|
|
96
|
+
it("compares non-strictly by id and version field", () => {
|
|
97
|
+
expect(
|
|
98
|
+
isLayerSameAndUnchanged(
|
|
99
|
+
{ ...SAMPLE_LAYER1, id: "ab", version: 2 },
|
|
100
|
+
{ ...SAMPLE_LAYER2, id: "ab", version: 2 },
|
|
101
|
+
),
|
|
102
|
+
).toBe(true);
|
|
103
|
+
expect(
|
|
104
|
+
isLayerSameAndUnchanged(
|
|
105
|
+
{ ...SAMPLE_LAYER1, id: "ab", version: 2 },
|
|
106
|
+
{ ...SAMPLE_LAYER1, id: "ab", version: 1 },
|
|
107
|
+
),
|
|
108
|
+
).toBe(false);
|
|
109
|
+
expect(
|
|
110
|
+
isLayerSameAndUnchanged(
|
|
111
|
+
{ ...SAMPLE_LAYER1, id: 1 },
|
|
112
|
+
{ ...SAMPLE_LAYER1, id: "01", version: 3 },
|
|
113
|
+
),
|
|
114
|
+
).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe("layers with id and both without version", () => {
|
|
118
|
+
it("compares by properties, including extras", () => {
|
|
119
|
+
expect(
|
|
120
|
+
isLayerSameAndUnchanged(
|
|
121
|
+
{ ...SAMPLE_LAYER1, id: "a" },
|
|
122
|
+
{ ...SAMPLE_LAYER1, id: "b" },
|
|
123
|
+
),
|
|
124
|
+
).toBe(false);
|
|
125
|
+
expect(
|
|
126
|
+
isLayerSameAndUnchanged(
|
|
127
|
+
{ ...SAMPLE_LAYER1, id: "a" },
|
|
128
|
+
{ ...SAMPLE_LAYER2, id: "a" },
|
|
129
|
+
),
|
|
130
|
+
).toBe(false);
|
|
131
|
+
expect(
|
|
132
|
+
isLayerSameAndUnchanged(
|
|
133
|
+
{ ...SAMPLE_LAYER1, id: "a" },
|
|
134
|
+
{ ...SAMPLE_LAYER1, id: "a" },
|
|
135
|
+
),
|
|
136
|
+
).toBe(true);
|
|
137
|
+
expect(
|
|
138
|
+
isLayerSameAndUnchanged(
|
|
139
|
+
{ ...SAMPLE_LAYER2, id: "b", extras: { hello: "world" } },
|
|
140
|
+
{ ...SAMPLE_LAYER2, id: "b", extras: { hello: "world" } },
|
|
141
|
+
),
|
|
142
|
+
).toBe(true);
|
|
143
|
+
expect(
|
|
144
|
+
isLayerSameAndUnchanged(
|
|
145
|
+
{ ...SAMPLE_LAYER2, id: "b", extras: { hello: "world" } },
|
|
146
|
+
{ ...SAMPLE_LAYER2, id: "b", extras: { foo: "bar" } },
|
|
147
|
+
),
|
|
148
|
+
).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe("layers without id", () => {
|
|
152
|
+
it("compares by properties, including extras", () => {
|
|
153
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, SAMPLE_LAYER1)).toBe(
|
|
154
|
+
true,
|
|
155
|
+
);
|
|
156
|
+
expect(
|
|
157
|
+
isLayerSameAndUnchanged(SAMPLE_LAYER1, {
|
|
158
|
+
...SAMPLE_LAYER1,
|
|
159
|
+
name: "abc",
|
|
160
|
+
}),
|
|
161
|
+
).toBe(false);
|
|
162
|
+
expect(
|
|
163
|
+
isLayerSameAndUnchanged(
|
|
164
|
+
{
|
|
165
|
+
...SAMPLE_LAYER1,
|
|
166
|
+
url: "http://abc.org",
|
|
167
|
+
name: SAMPLE_LAYER1.name,
|
|
168
|
+
},
|
|
169
|
+
{ ...SAMPLE_LAYER1, url: "http://abc.org" },
|
|
170
|
+
),
|
|
171
|
+
).toBe(true);
|
|
172
|
+
expect(
|
|
173
|
+
isLayerSameAndUnchanged(
|
|
174
|
+
{
|
|
175
|
+
...SAMPLE_LAYER1,
|
|
176
|
+
opacity: 1,
|
|
177
|
+
},
|
|
178
|
+
SAMPLE_LAYER1,
|
|
179
|
+
),
|
|
180
|
+
).toBe(false);
|
|
181
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, SAMPLE_LAYER2)).toBe(
|
|
182
|
+
false,
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
it("takes into account extras prop", () => {
|
|
186
|
+
expect(
|
|
187
|
+
isLayerSameAndUnchanged(SAMPLE_LAYER1, {
|
|
188
|
+
...SAMPLE_LAYER1,
|
|
189
|
+
extras: { otherProp: "abc" },
|
|
190
|
+
}),
|
|
191
|
+
).toBe(false);
|
|
192
|
+
expect(
|
|
193
|
+
isLayerSameAndUnchanged(SAMPLE_LAYER1, {
|
|
194
|
+
...SAMPLE_LAYER1,
|
|
195
|
+
extras: undefined,
|
|
196
|
+
}),
|
|
197
|
+
).toBe(false);
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
199
|
+
const { extras, ...layer } = SAMPLE_LAYER1;
|
|
200
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, layer)).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe("layers with and without id", () => {
|
|
204
|
+
it("compares by properties", () => {
|
|
205
|
+
expect(
|
|
206
|
+
isLayerSameAndUnchanged(SAMPLE_LAYER1, {
|
|
207
|
+
...SAMPLE_LAYER1,
|
|
208
|
+
id: "123",
|
|
209
|
+
}),
|
|
210
|
+
).toBe(false);
|
|
211
|
+
expect(
|
|
212
|
+
isLayerSameAndUnchanged(
|
|
213
|
+
{ ...SAMPLE_LAYER1, id: "123" },
|
|
214
|
+
{ id: "123", ...SAMPLE_LAYER1, name: SAMPLE_LAYER1.name },
|
|
215
|
+
),
|
|
216
|
+
).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("getLayerHash", () => {
|
|
222
|
+
it("works with serializable entities", () => {
|
|
223
|
+
expect(
|
|
224
|
+
getLayerHash({
|
|
225
|
+
...SAMPLE_LAYER1,
|
|
226
|
+
extras: {
|
|
227
|
+
array: [11, 22, null, undefined],
|
|
228
|
+
object: {},
|
|
229
|
+
undef: undefined,
|
|
230
|
+
},
|
|
231
|
+
}),
|
|
232
|
+
).toBeTypeOf("string");
|
|
233
|
+
});
|
|
234
|
+
it("ignores extra property by default", () => {
|
|
235
|
+
expect(
|
|
236
|
+
getLayerHash({
|
|
237
|
+
...SAMPLE_LAYER1,
|
|
238
|
+
extras: {
|
|
239
|
+
array: [11, 22, null, undefined],
|
|
240
|
+
object: {},
|
|
241
|
+
},
|
|
242
|
+
}),
|
|
243
|
+
).toEqual(
|
|
244
|
+
getLayerHash({
|
|
245
|
+
...SAMPLE_LAYER1,
|
|
246
|
+
extras: { hello: "world" },
|
|
247
|
+
}),
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
it("works with non-serializable entities (but they are ignored)", () => {
|
|
251
|
+
const hash = getLayerHash(
|
|
252
|
+
{
|
|
253
|
+
...SAMPLE_LAYER1,
|
|
254
|
+
extras: {
|
|
255
|
+
func: () => 123,
|
|
256
|
+
canvas: document.createElement("canvas"),
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
true,
|
|
260
|
+
);
|
|
261
|
+
expect(hash).toBeTypeOf("string");
|
|
262
|
+
expect(hash).toEqual(
|
|
263
|
+
getLayerHash(
|
|
264
|
+
{
|
|
265
|
+
...SAMPLE_LAYER1,
|
|
266
|
+
extras: {
|
|
267
|
+
func: () => 456,
|
|
268
|
+
canvas: document.createElement("div"),
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
true,
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
expect(hash).not.toEqual(
|
|
275
|
+
getLayerHash(
|
|
276
|
+
{
|
|
277
|
+
...SAMPLE_LAYER1,
|
|
278
|
+
extras: {},
|
|
279
|
+
},
|
|
280
|
+
true,
|
|
281
|
+
),
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
it("does not take into account properties order", () => {
|
|
285
|
+
expect(
|
|
286
|
+
getLayerHash(
|
|
287
|
+
{
|
|
288
|
+
type: "wms",
|
|
289
|
+
url: "http://abc.org/wms",
|
|
290
|
+
name: "myLayer",
|
|
291
|
+
extras: {
|
|
292
|
+
array: [1, 2, 3],
|
|
293
|
+
object: {},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
true,
|
|
297
|
+
),
|
|
298
|
+
).toEqual(
|
|
299
|
+
getLayerHash(
|
|
300
|
+
{
|
|
301
|
+
extras: {
|
|
302
|
+
array: [1, 2, 3],
|
|
303
|
+
object: {},
|
|
304
|
+
},
|
|
305
|
+
url: "http://abc.org/wms",
|
|
306
|
+
type: "wms",
|
|
307
|
+
name: "myLayer",
|
|
308
|
+
},
|
|
309
|
+
true,
|
|
310
|
+
),
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("updateLayer", () => {
|
|
316
|
+
it("applies updates to a layer", () => {
|
|
317
|
+
const updated = updateLayer(SAMPLE_LAYER1, {
|
|
318
|
+
opacity: 0.9,
|
|
319
|
+
visibility: false,
|
|
320
|
+
});
|
|
321
|
+
expect(updated).toMatchObject({
|
|
322
|
+
...SAMPLE_LAYER1,
|
|
323
|
+
opacity: 0.9,
|
|
324
|
+
visibility: false,
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("does not modify the original layer", () => {
|
|
329
|
+
const updated = updateLayer(SAMPLE_LAYER1, { opacity: 0.5 });
|
|
330
|
+
expect(updated).not.toBe(SAMPLE_LAYER1);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("removes properties set to undefined", () => {
|
|
334
|
+
const layerWithOpacity = { ...SAMPLE_LAYER1, opacity: 0.8 };
|
|
335
|
+
const updated = updateLayer(layerWithOpacity, { opacity: undefined });
|
|
336
|
+
expect(updated).not.toHaveProperty("opacity");
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("increments version when layer has both id and version", () => {
|
|
340
|
+
const layer = { ...SAMPLE_LAYER1, id: "123", version: 0 };
|
|
341
|
+
const updated = updateLayer(layer, { opacity: 0.6 });
|
|
342
|
+
expect(updated.version).toBe(1);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("does not write version when layer only has id", () => {
|
|
346
|
+
const layer = { ...SAMPLE_LAYER1, id: "123" };
|
|
347
|
+
const updated = updateLayer(layer, { opacity: 0.6 });
|
|
348
|
+
expect(updated).not.toHaveProperty("version");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("does not change the version if it is explicitly specified in the partial changes", () => {
|
|
352
|
+
const layer = { ...SAMPLE_LAYER1, id: "123", version: 3 };
|
|
353
|
+
const updated = updateLayer(layer, { opacity: 0.6, version: 12 });
|
|
354
|
+
expect(updated.version).toBe(12);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { MapContextLayer } from "../model/index.js";
|
|
2
|
+
import { getHash } from "./hash.js";
|
|
3
|
+
|
|
4
|
+
export function getLayerHash(
|
|
5
|
+
layer: MapContextLayer,
|
|
6
|
+
includeExtras = false,
|
|
7
|
+
): string {
|
|
8
|
+
return getHash(layer, includeExtras ? [] : ["extras"]);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isLayerSame(
|
|
12
|
+
layerA: MapContextLayer,
|
|
13
|
+
layerB: MapContextLayer,
|
|
14
|
+
): boolean {
|
|
15
|
+
if ("id" in layerA && "id" in layerB) {
|
|
16
|
+
return layerA.id == layerB.id;
|
|
17
|
+
}
|
|
18
|
+
return getLayerHash(layerA) === getLayerHash(layerB);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isLayerSameAndUnchanged(
|
|
22
|
+
layerA: MapContextLayer,
|
|
23
|
+
layerB: MapContextLayer,
|
|
24
|
+
): boolean {
|
|
25
|
+
if (
|
|
26
|
+
"id" in layerA &&
|
|
27
|
+
"id" in layerB &&
|
|
28
|
+
("version" in layerA || "version" in layerB)
|
|
29
|
+
) {
|
|
30
|
+
return layerA.id == layerB.id && layerA.version == layerB.version;
|
|
31
|
+
}
|
|
32
|
+
return getLayerHash(layerA, true) === getLayerHash(layerB, true);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Applies the `updates` partial to the given layer. The layer will also be
|
|
37
|
+
* adjusted depending on the presence of the `id` and `version` fields.
|
|
38
|
+
* Note: any property set to `undefined` in `updates` will be removed from the resulting layer.
|
|
39
|
+
* @param layer
|
|
40
|
+
* @param updates
|
|
41
|
+
*/
|
|
42
|
+
export function updateLayer(
|
|
43
|
+
layer: MapContextLayer,
|
|
44
|
+
updates: Partial<MapContextLayer>,
|
|
45
|
+
): MapContextLayer {
|
|
46
|
+
const updatedLayer: MapContextLayer = {
|
|
47
|
+
...layer,
|
|
48
|
+
...updates,
|
|
49
|
+
} as MapContextLayer;
|
|
50
|
+
const versionExplicitlyUpdated =
|
|
51
|
+
"version" in updates && updates.version !== undefined;
|
|
52
|
+
if (
|
|
53
|
+
"id" in updatedLayer &&
|
|
54
|
+
"version" in updatedLayer &&
|
|
55
|
+
typeof updatedLayer.version === "number" &&
|
|
56
|
+
!versionExplicitlyUpdated
|
|
57
|
+
) {
|
|
58
|
+
updatedLayer.version = updatedLayer.version + 1;
|
|
59
|
+
}
|
|
60
|
+
for (const key in updatedLayer) {
|
|
61
|
+
if (updatedLayer[key as keyof MapContextLayer] === undefined) {
|
|
62
|
+
delete updatedLayer[key as keyof MapContextLayer];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return updatedLayer;
|
|
66
|
+
}
|