@geospatial-sdk/core 0.0.5-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/model/index.d.ts +3 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +2 -0
- package/dist/model/map-context-diff.d.ts +35 -0
- package/dist/model/map-context-diff.d.ts.map +1 -0
- package/dist/model/map-context-diff.js +1 -0
- package/dist/model/map-context.d.ts +78 -0
- package/dist/model/map-context.d.ts.map +1 -0
- package/dist/model/map-context.js +1 -0
- package/dist/utils/freeze.d.ts +2 -0
- package/dist/utils/freeze.d.ts.map +1 -0
- package/dist/utils/freeze.js +15 -0
- package/dist/utils/freeze.test.d.ts +2 -0
- package/dist/utils/freeze.test.d.ts.map +1 -0
- package/dist/utils/freeze.test.js +51 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/map-context-diff.d.ts +25 -0
- package/dist/utils/map-context-diff.d.ts.map +1 -0
- package/dist/utils/map-context-diff.js +111 -0
- package/dist/utils/map-context-diff.test.d.ts +2 -0
- package/dist/utils/map-context-diff.test.d.ts.map +1 -0
- package/dist/utils/map-context-diff.test.js +370 -0
- package/dist/utils/url.d.ts +7 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +17 -0
- package/dist/utils/url.test.d.ts +2 -0
- package/dist/utils/url.test.d.ts.map +1 -0
- package/dist/utils/url.test.js +8 -0
- package/lib/index.ts +3 -0
- package/lib/model/index.ts +2 -0
- package/lib/model/map-context-diff.ts +37 -0
- package/lib/model/map-context.ts +96 -0
- package/lib/utils/freeze.test.ts +52 -0
- package/lib/utils/freeze.ts +14 -0
- package/lib/utils/index.ts +3 -0
- package/lib/utils/map-context-diff.test.ts +577 -0
- package/lib/utils/map-context-diff.ts +144 -0
- package/lib/utils/url.test.ts +14 -0
- package/lib/utils/url.ts +20 -0
- package/package.json +26 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { deepFreeze } from "./freeze";
|
|
13
|
+
import { computeMapContextDiff, getLayerHash, isLayerSame, isLayerSameAndUnchanged, } from "./map-context-diff";
|
|
14
|
+
const SAMPLE_CONTEXT = deepFreeze({
|
|
15
|
+
view: {
|
|
16
|
+
center: [10, 20],
|
|
17
|
+
zoom: 3,
|
|
18
|
+
extent: [40, 50, 60, 70],
|
|
19
|
+
},
|
|
20
|
+
layers: [],
|
|
21
|
+
});
|
|
22
|
+
const SAMPLE_LAYER1 = deepFreeze({
|
|
23
|
+
type: "wms",
|
|
24
|
+
url: "http://abc.org/wms",
|
|
25
|
+
name: "myLayer",
|
|
26
|
+
extras: { myField: "abc" },
|
|
27
|
+
});
|
|
28
|
+
const SAMPLE_LAYER2 = deepFreeze({
|
|
29
|
+
type: "xyz",
|
|
30
|
+
url: "http://abc.org/tiles",
|
|
31
|
+
extras: { myField2: "123" },
|
|
32
|
+
});
|
|
33
|
+
const SAMPLE_LAYER3 = deepFreeze({
|
|
34
|
+
type: "geojson",
|
|
35
|
+
data: '{ "type": "Feature", "properties": {}}',
|
|
36
|
+
extras: { myField3: "000" },
|
|
37
|
+
});
|
|
38
|
+
const SAMPLE_LAYER4 = deepFreeze({
|
|
39
|
+
type: "wfs",
|
|
40
|
+
url: "http://abc.org/wfs",
|
|
41
|
+
featureType: "myFeatureType",
|
|
42
|
+
extras: { myField4: "aaa" },
|
|
43
|
+
});
|
|
44
|
+
const SAMPLE_LAYER5 = deepFreeze({
|
|
45
|
+
type: "xyz",
|
|
46
|
+
url: "http://my.tiles/server",
|
|
47
|
+
});
|
|
48
|
+
describe("Context diff utils", () => {
|
|
49
|
+
describe("isLayerSame", () => {
|
|
50
|
+
describe("layers with id", () => {
|
|
51
|
+
it("compares non-strictly by id", () => {
|
|
52
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "a" }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "b" }))).toBe(false);
|
|
53
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "ab" }), Object.assign(Object.assign({}, SAMPLE_LAYER2), { id: "ab" }))).toBe(true);
|
|
54
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 1 }), Object.assign(Object.assign({}, SAMPLE_LAYER2), { id: "01" }))).toBe(true);
|
|
55
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 1 }), Object.assign(Object.assign({}, SAMPLE_LAYER2), { id: 1 }))).toBe(true);
|
|
56
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 1 }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 2 }))).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("layers without id", () => {
|
|
60
|
+
it("compares by properties", () => {
|
|
61
|
+
expect(isLayerSame(SAMPLE_LAYER1, SAMPLE_LAYER1)).toBe(true);
|
|
62
|
+
expect(isLayerSame(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { name: "abc" }))).toBe(false);
|
|
63
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { url: "http://abc.org", name: SAMPLE_LAYER1.name }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { url: "http://abc.org" }))).toBe(true);
|
|
64
|
+
expect(isLayerSame(SAMPLE_LAYER1, SAMPLE_LAYER2)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it("ignores extras prop", () => {
|
|
67
|
+
expect(isLayerSame(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: { otherProp: "abc" } }))).toBe(true);
|
|
68
|
+
expect(isLayerSame(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: undefined }))).toBe(true);
|
|
69
|
+
const { extras } = SAMPLE_LAYER1, layer = __rest(SAMPLE_LAYER1, ["extras"]);
|
|
70
|
+
expect(isLayerSame(SAMPLE_LAYER1, layer)).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("layers with and without id", () => {
|
|
74
|
+
it("compares by properties", () => {
|
|
75
|
+
expect(isLayerSame(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "123" }))).toBe(false);
|
|
76
|
+
expect(isLayerSame(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "123" }), Object.assign(Object.assign({ id: "123" }, SAMPLE_LAYER1), { name: SAMPLE_LAYER1.name }))).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("isLayerSameAndUnchanged", () => {
|
|
81
|
+
describe("layers with id", () => {
|
|
82
|
+
it("compares non-strictly by id and version field", () => {
|
|
83
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "a" }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "b" }))).toBe(false);
|
|
84
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "a" }), Object.assign(Object.assign({}, SAMPLE_LAYER2), { id: "a" }))).toBe(true);
|
|
85
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "ab", version: 2 }), Object.assign(Object.assign({}, SAMPLE_LAYER2), { id: "ab", version: 2 }))).toBe(true);
|
|
86
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "ab", version: 2 }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "ab", version: 1 }))).toBe(false);
|
|
87
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 1 }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "01", version: 3 }))).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("layers without id", () => {
|
|
91
|
+
it("compares by properties, including extras", () => {
|
|
92
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, SAMPLE_LAYER1)).toBe(true);
|
|
93
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { name: "abc" }))).toBe(false);
|
|
94
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { url: "http://abc.org", name: SAMPLE_LAYER1.name }), Object.assign(Object.assign({}, SAMPLE_LAYER1), { url: "http://abc.org" }))).toBe(true);
|
|
95
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, SAMPLE_LAYER2)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
it("takes into account extras prop", () => {
|
|
98
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: { otherProp: "abc" } }))).toBe(false);
|
|
99
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: undefined }))).toBe(false);
|
|
100
|
+
const { extras } = SAMPLE_LAYER1, layer = __rest(SAMPLE_LAYER1, ["extras"]);
|
|
101
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, layer)).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe("layers with and without id", () => {
|
|
105
|
+
it("compares by properties", () => {
|
|
106
|
+
expect(isLayerSameAndUnchanged(SAMPLE_LAYER1, Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "123" }))).toBe(false);
|
|
107
|
+
expect(isLayerSameAndUnchanged(Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: "123" }), Object.assign(Object.assign({ id: "123" }, SAMPLE_LAYER1), { name: SAMPLE_LAYER1.name }))).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("computeMapContextDiff", () => {
|
|
112
|
+
let contextOld;
|
|
113
|
+
let contextNew;
|
|
114
|
+
let diff;
|
|
115
|
+
describe("no change", () => {
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, SAMPLE_LAYER1] });
|
|
118
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, SAMPLE_LAYER1] });
|
|
119
|
+
});
|
|
120
|
+
it("outputs the correct diff", () => {
|
|
121
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
122
|
+
expect(diff).toEqual({
|
|
123
|
+
layersAdded: [],
|
|
124
|
+
layersChanged: [],
|
|
125
|
+
layersRemoved: [],
|
|
126
|
+
layersReordered: [],
|
|
127
|
+
viewChanges: {},
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe("layers added", () => {
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER1] });
|
|
134
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, SAMPLE_LAYER1, SAMPLE_LAYER4] });
|
|
135
|
+
});
|
|
136
|
+
it("outputs the correct diff", () => {
|
|
137
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
138
|
+
expect(diff).toEqual({
|
|
139
|
+
layersAdded: [
|
|
140
|
+
{
|
|
141
|
+
layer: SAMPLE_LAYER2,
|
|
142
|
+
position: 0,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
layer: SAMPLE_LAYER4,
|
|
146
|
+
position: 2,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
layersChanged: [],
|
|
150
|
+
layersRemoved: [],
|
|
151
|
+
layersReordered: [],
|
|
152
|
+
viewChanges: {},
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe("layers removed", () => {
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, SAMPLE_LAYER1, SAMPLE_LAYER4] });
|
|
159
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER4] });
|
|
160
|
+
});
|
|
161
|
+
it("outputs the correct diff", () => {
|
|
162
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
163
|
+
expect(diff).toEqual({
|
|
164
|
+
layersAdded: [],
|
|
165
|
+
layersChanged: [],
|
|
166
|
+
layersRemoved: [
|
|
167
|
+
{
|
|
168
|
+
layer: SAMPLE_LAYER2,
|
|
169
|
+
position: 0,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
layer: SAMPLE_LAYER1,
|
|
173
|
+
position: 1,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
layersReordered: [],
|
|
177
|
+
viewChanges: {},
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("layers changed", () => {
|
|
182
|
+
beforeEach(() => {
|
|
183
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 123, version: 3 })] });
|
|
184
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [
|
|
185
|
+
Object.assign(Object.assign({}, SAMPLE_LAYER2), { extras: { prop: true } }),
|
|
186
|
+
Object.assign(Object.assign({}, SAMPLE_LAYER1), { id: 123, version: 10 }),
|
|
187
|
+
] });
|
|
188
|
+
});
|
|
189
|
+
it("outputs the correct diff", () => {
|
|
190
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
191
|
+
expect(diff).toEqual({
|
|
192
|
+
layersAdded: [],
|
|
193
|
+
layersChanged: [
|
|
194
|
+
{
|
|
195
|
+
layer: contextNew.layers[0],
|
|
196
|
+
position: 0,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
layer: contextNew.layers[1],
|
|
200
|
+
position: 1,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
layersRemoved: [],
|
|
204
|
+
layersReordered: [],
|
|
205
|
+
viewChanges: {},
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe("reordering", () => {
|
|
210
|
+
describe("three layers reordered", () => {
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER1, SAMPLE_LAYER2, SAMPLE_LAYER3] });
|
|
213
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, SAMPLE_LAYER1, SAMPLE_LAYER3] });
|
|
214
|
+
});
|
|
215
|
+
it("outputs the correct diff", () => {
|
|
216
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
217
|
+
expect(diff).toEqual({
|
|
218
|
+
layersAdded: [],
|
|
219
|
+
layersChanged: [],
|
|
220
|
+
layersRemoved: [],
|
|
221
|
+
layersReordered: [
|
|
222
|
+
{
|
|
223
|
+
layer: SAMPLE_LAYER2,
|
|
224
|
+
newPosition: 0,
|
|
225
|
+
previousPosition: 1,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
layer: SAMPLE_LAYER1,
|
|
229
|
+
newPosition: 1,
|
|
230
|
+
previousPosition: 0,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
viewChanges: {},
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe("four layers reordered", () => {
|
|
238
|
+
beforeEach(() => {
|
|
239
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [
|
|
240
|
+
SAMPLE_LAYER1,
|
|
241
|
+
SAMPLE_LAYER3,
|
|
242
|
+
SAMPLE_LAYER4,
|
|
243
|
+
SAMPLE_LAYER2,
|
|
244
|
+
] });
|
|
245
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [
|
|
246
|
+
SAMPLE_LAYER4,
|
|
247
|
+
SAMPLE_LAYER3,
|
|
248
|
+
SAMPLE_LAYER1,
|
|
249
|
+
SAMPLE_LAYER2,
|
|
250
|
+
] });
|
|
251
|
+
});
|
|
252
|
+
it("outputs the correct diff", () => {
|
|
253
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
254
|
+
expect(diff).toEqual({
|
|
255
|
+
layersAdded: [],
|
|
256
|
+
layersChanged: [],
|
|
257
|
+
layersRemoved: [],
|
|
258
|
+
layersReordered: [
|
|
259
|
+
{
|
|
260
|
+
layer: SAMPLE_LAYER4,
|
|
261
|
+
newPosition: 0,
|
|
262
|
+
previousPosition: 2,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
layer: SAMPLE_LAYER1,
|
|
266
|
+
newPosition: 2,
|
|
267
|
+
previousPosition: 0,
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
viewChanges: {},
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
describe("combined changes", () => {
|
|
276
|
+
let changedLayer;
|
|
277
|
+
beforeEach(() => {
|
|
278
|
+
changedLayer = Object.assign(Object.assign({}, SAMPLE_LAYER3), { extras: { prop: true } });
|
|
279
|
+
contextOld = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER1, SAMPLE_LAYER5, SAMPLE_LAYER3, SAMPLE_LAYER4] });
|
|
280
|
+
contextNew = Object.assign(Object.assign({}, SAMPLE_CONTEXT), { layers: [SAMPLE_LAYER2, changedLayer, SAMPLE_LAYER5] });
|
|
281
|
+
});
|
|
282
|
+
it("outputs the correct diff", () => {
|
|
283
|
+
diff = computeMapContextDiff(contextNew, contextOld);
|
|
284
|
+
expect(diff).toEqual({
|
|
285
|
+
layersAdded: [
|
|
286
|
+
{
|
|
287
|
+
layer: SAMPLE_LAYER2,
|
|
288
|
+
position: 0,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
layersChanged: [
|
|
292
|
+
{
|
|
293
|
+
layer: changedLayer,
|
|
294
|
+
position: 1,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
layersRemoved: [
|
|
298
|
+
{
|
|
299
|
+
layer: SAMPLE_LAYER1,
|
|
300
|
+
position: 0,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
layer: SAMPLE_LAYER4,
|
|
304
|
+
position: 3,
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
layersReordered: [
|
|
308
|
+
{
|
|
309
|
+
layer: changedLayer,
|
|
310
|
+
newPosition: 1,
|
|
311
|
+
previousPosition: 2,
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
layer: SAMPLE_LAYER5,
|
|
315
|
+
newPosition: 2,
|
|
316
|
+
previousPosition: 1,
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
viewChanges: {},
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
describe("getLayerHash", () => {
|
|
325
|
+
it("works with serializable entities", () => {
|
|
326
|
+
expect(getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: {
|
|
327
|
+
array: [11, 22, null, undefined],
|
|
328
|
+
object: {},
|
|
329
|
+
undef: undefined,
|
|
330
|
+
} }))).toBeTypeOf("string");
|
|
331
|
+
});
|
|
332
|
+
it("ignores extra property by default", () => {
|
|
333
|
+
expect(getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: {
|
|
334
|
+
array: [11, 22, null, undefined],
|
|
335
|
+
object: {},
|
|
336
|
+
} }))).toEqual(getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: { hello: "world" } })));
|
|
337
|
+
});
|
|
338
|
+
it("works with non-serializable entities (but they are ignored)", () => {
|
|
339
|
+
const hash = getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: {
|
|
340
|
+
func: () => 123,
|
|
341
|
+
canvas: document.createElement("canvas"),
|
|
342
|
+
} }), true);
|
|
343
|
+
expect(hash).toBeTypeOf("string");
|
|
344
|
+
expect(hash).toEqual(getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: {
|
|
345
|
+
func: () => 456,
|
|
346
|
+
canvas: document.createElement("div"),
|
|
347
|
+
} }), true));
|
|
348
|
+
expect(hash).not.toEqual(getLayerHash(Object.assign(Object.assign({}, SAMPLE_LAYER1), { extras: {} }), true));
|
|
349
|
+
});
|
|
350
|
+
it("does not take into account properties order", () => {
|
|
351
|
+
expect(getLayerHash({
|
|
352
|
+
type: "wms",
|
|
353
|
+
url: "http://abc.org/wms",
|
|
354
|
+
name: "myLayer",
|
|
355
|
+
extras: {
|
|
356
|
+
array: [1, 2, 3],
|
|
357
|
+
object: {},
|
|
358
|
+
},
|
|
359
|
+
}, true)).toEqual(getLayerHash({
|
|
360
|
+
extras: {
|
|
361
|
+
array: [1, 2, 3],
|
|
362
|
+
object: {},
|
|
363
|
+
},
|
|
364
|
+
url: "http://abc.org/wms",
|
|
365
|
+
type: "wms",
|
|
366
|
+
name: "myLayer",
|
|
367
|
+
}, true));
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../lib/utils/url.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EAAE,GACrB,MAAM,CAWR"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes the given search params from the URL completely; this is case-insensitive
|
|
3
|
+
* @param url
|
|
4
|
+
* @param searchParams
|
|
5
|
+
*/
|
|
6
|
+
export function removeSearchParams(url, searchParams) {
|
|
7
|
+
const toDelete = [];
|
|
8
|
+
const urlObj = new URL(url, window.location.toString());
|
|
9
|
+
const keysLower = searchParams.map((p) => p.toLowerCase());
|
|
10
|
+
for (const param of urlObj.searchParams.keys()) {
|
|
11
|
+
if (keysLower.indexOf(param.toLowerCase()) > -1) {
|
|
12
|
+
toDelete.push(param);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
toDelete.map((param) => urlObj.searchParams.delete(param));
|
|
16
|
+
return urlObj.toString();
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.test.d.ts","sourceRoot":"","sources":["../../lib/utils/url.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { removeSearchParams } from "./url";
|
|
2
|
+
describe("URL utils", () => {
|
|
3
|
+
describe("removeSearchParams", () => {
|
|
4
|
+
it("removes given search params in a case insensitive way", () => {
|
|
5
|
+
expect(removeSearchParams("http://my.org/abc/?arg0=1234&arg1=aaa&Arg1=111&ARG2=&aRG3=fff&arg4=5678", ["ARG1", "arg2", "arg3"])).toEqual("http://my.org/abc/?arg0=1234&arg4=5678");
|
|
6
|
+
});
|
|
7
|
+
});
|
|
8
|
+
});
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MapContextLayer, MapContextView } from "./map-context";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Associates a position to a layer; the position is the index of
|
|
5
|
+
* the layer in the layers array
|
|
6
|
+
*/
|
|
7
|
+
export interface MapContextLayerPositioned {
|
|
8
|
+
layer: MapContextLayer;
|
|
9
|
+
position: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Describes a layer being moved to a different position
|
|
14
|
+
*/
|
|
15
|
+
export interface MapContextLayerReordered {
|
|
16
|
+
layer: MapContextLayer;
|
|
17
|
+
newPosition: number;
|
|
18
|
+
previousPosition: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Describes a delta between two contexts, in order to be
|
|
23
|
+
* applied to an existing map.
|
|
24
|
+
*
|
|
25
|
+
* For positions to be correct the order of operations should be:
|
|
26
|
+
* 1. change layers
|
|
27
|
+
* 2. remove layers
|
|
28
|
+
* 3. add layers
|
|
29
|
+
* 4. move layers
|
|
30
|
+
*/
|
|
31
|
+
export interface MapContextDiff {
|
|
32
|
+
layersChanged: MapContextLayerPositioned[];
|
|
33
|
+
layersReordered: MapContextLayerReordered[];
|
|
34
|
+
layersRemoved: MapContextLayerPositioned[];
|
|
35
|
+
layersAdded: MapContextLayerPositioned[];
|
|
36
|
+
viewChanges: MapContextView;
|
|
37
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { FeatureCollection, Geometry } from "geojson";
|
|
2
|
+
|
|
3
|
+
export type LayerDimensions = Record<string, string>;
|
|
4
|
+
|
|
5
|
+
export type LayerExtras = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
export interface MapContextBaseLayer {
|
|
8
|
+
id?: string | number;
|
|
9
|
+
version?: number;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This property can be used to store anything application-specific on layers; as its content may occasionally
|
|
13
|
+
* be serialized to JSON for change detection purposes, it is not recommended to store Functions or other
|
|
14
|
+
* non-serializable entities
|
|
15
|
+
*/
|
|
16
|
+
extras?: LayerExtras;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MapContextLayerWms extends MapContextBaseLayer {
|
|
20
|
+
type: "wms";
|
|
21
|
+
url: string;
|
|
22
|
+
name: string;
|
|
23
|
+
dimensions?: LayerDimensions;
|
|
24
|
+
style?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MapContextLayerWmts extends MapContextBaseLayer {
|
|
28
|
+
type: "wmts";
|
|
29
|
+
url: string;
|
|
30
|
+
name: string;
|
|
31
|
+
dimensions?: LayerDimensions;
|
|
32
|
+
style?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MapContextLayerWfs extends MapContextBaseLayer {
|
|
36
|
+
type: "wfs";
|
|
37
|
+
url: string;
|
|
38
|
+
featureType: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface MapContextLayerXyz extends MapContextBaseLayer {
|
|
42
|
+
type: "xyz";
|
|
43
|
+
url: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface LayerGeojson extends MapContextBaseLayer {
|
|
47
|
+
type: "geojson";
|
|
48
|
+
}
|
|
49
|
+
interface LayerGeojsonWithUrl extends LayerGeojson {
|
|
50
|
+
url: string;
|
|
51
|
+
data?: never;
|
|
52
|
+
}
|
|
53
|
+
interface LayerGeojsonWithData extends LayerGeojson {
|
|
54
|
+
data: FeatureCollection<Geometry | null> | string;
|
|
55
|
+
url?: never;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @interface
|
|
60
|
+
*/
|
|
61
|
+
export type MapContextLayerGeojson = LayerGeojsonWithUrl | LayerGeojsonWithData;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @interface
|
|
65
|
+
*/
|
|
66
|
+
export type MapContextLayer =
|
|
67
|
+
| MapContextLayerWms
|
|
68
|
+
| MapContextLayerWmts
|
|
69
|
+
| MapContextLayerWfs
|
|
70
|
+
| MapContextLayerXyz
|
|
71
|
+
| MapContextLayerGeojson;
|
|
72
|
+
|
|
73
|
+
export type Coordinate = [number, number];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Min X, min Y, max X, max Y
|
|
77
|
+
*/
|
|
78
|
+
export type Extent = [number, number, number, number];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @property center Expressed in longitude/latitude
|
|
82
|
+
* @property extent Expressed in longitude/latitude
|
|
83
|
+
* @property maxExtent Expressed in longitude/latitude
|
|
84
|
+
*/
|
|
85
|
+
export interface MapContextView {
|
|
86
|
+
center?: Coordinate;
|
|
87
|
+
zoom?: number;
|
|
88
|
+
extent?: Extent;
|
|
89
|
+
maxZoom?: number;
|
|
90
|
+
maxExtent?: Extent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface MapContext {
|
|
94
|
+
layers: MapContextLayer[];
|
|
95
|
+
view: MapContextView;
|
|
96
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { deepFreeze } from "./freeze";
|
|
2
|
+
|
|
3
|
+
describe("freeze util", () => {
|
|
4
|
+
describe("deepFreeze", () => {
|
|
5
|
+
let obj: any;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
obj = deepFreeze({
|
|
8
|
+
ab: "cde",
|
|
9
|
+
fg: {
|
|
10
|
+
h: true,
|
|
11
|
+
ij: 12,
|
|
12
|
+
},
|
|
13
|
+
k: [
|
|
14
|
+
{
|
|
15
|
+
l: 456,
|
|
16
|
+
mn: ["opqr"],
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
it("prevents mutation of root properties", () => {
|
|
22
|
+
expect(() => {
|
|
23
|
+
obj.ab = "AA";
|
|
24
|
+
}).toThrow();
|
|
25
|
+
});
|
|
26
|
+
it("prevents mutation of nested properties", () => {
|
|
27
|
+
expect(() => {
|
|
28
|
+
obj.fg.h = "AA";
|
|
29
|
+
}).toThrow();
|
|
30
|
+
});
|
|
31
|
+
it("prevents mutation of objects inside nested arrays", () => {
|
|
32
|
+
expect(() => {
|
|
33
|
+
obj.k[0].l = "AA";
|
|
34
|
+
}).toThrow();
|
|
35
|
+
});
|
|
36
|
+
describe("starting with array", () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
obj = deepFreeze([
|
|
39
|
+
{
|
|
40
|
+
l: 456,
|
|
41
|
+
mn: ["opqr"],
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
it("prevents mutation of nested objects", () => {
|
|
46
|
+
expect(() => {
|
|
47
|
+
obj[0].l = "AA";
|
|
48
|
+
}).toThrow();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function deepFreeze<U>(obj: U): U {
|
|
2
|
+
if (Array.isArray(obj)) {
|
|
3
|
+
for (const elt of obj) {
|
|
4
|
+
deepFreeze(elt);
|
|
5
|
+
}
|
|
6
|
+
return obj;
|
|
7
|
+
} else if (obj instanceof Object) {
|
|
8
|
+
for (const prop in obj) {
|
|
9
|
+
deepFreeze((obj as Record<string, unknown>)[prop]);
|
|
10
|
+
}
|
|
11
|
+
return Object.freeze(obj);
|
|
12
|
+
}
|
|
13
|
+
return obj;
|
|
14
|
+
}
|