@dra2020/baseclient 1.0.169 → 1.0.171
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/baseclient.js +184 -42
- package/dist/baseclient.js.map +1 -1
- package/dist/ot-js/otsession.d.ts +2 -0
- package/dist/poly/polybin.d.ts +21 -1
- package/dist/poly/polypack.d.ts +35 -1
- package/lib/ot-js/otsession.ts +2 -0
- package/lib/poly/polybin.ts +113 -17
- package/lib/poly/polypack.ts +80 -24
- package/lib/poly/topo.ts +22 -0
- package/package.json +1 -1
|
@@ -93,6 +93,7 @@ export interface SessionProps {
|
|
|
93
93
|
published?: string;
|
|
94
94
|
locked?: boolean;
|
|
95
95
|
official: boolean;
|
|
96
|
+
template?: boolean;
|
|
96
97
|
loadFailed: boolean;
|
|
97
98
|
accessMap: AccessMap;
|
|
98
99
|
revisions: RevisionList;
|
|
@@ -115,6 +116,7 @@ export interface SessionUpdateProps {
|
|
|
115
116
|
deleted?: boolean;
|
|
116
117
|
published?: boolean;
|
|
117
118
|
official?: boolean;
|
|
119
|
+
template?: boolean;
|
|
118
120
|
locked?: boolean;
|
|
119
121
|
name?: string;
|
|
120
122
|
description?: string;
|
package/dist/poly/polybin.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import * as Util from '../util/all';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Serialize a collection to the geopack format.
|
|
4
|
+
*
|
|
5
|
+
* The file wants one contiguous coordinate section with every feature's offset an index into it, and
|
|
6
|
+
* that used to be arranged by requiring the whole collection to already sit in ONE buffer at exactly
|
|
7
|
+
* those offsets - packCollection simply blitted that buffer across whole. Which held only as long as
|
|
8
|
+
* nothing had touched the collection since: pack a collection, add a feature, pack again, and the
|
|
9
|
+
* second pack either redid all the work or produced offsets into a buffer that no longer described it.
|
|
10
|
+
*
|
|
11
|
+
* So the coordinates are gathered feature by feature instead. Each one is given its place in the
|
|
12
|
+
* output, and its floats are copied from wherever they happen to live now - one buffer or several. The
|
|
13
|
+
* work is the same (every float is copied either way), the assumption is gone, and featurePack is free
|
|
14
|
+
* to leave already-packed features alone.
|
|
15
|
+
*
|
|
16
|
+
* THE FORMAT IS UNCHANGED. Offsets are assigned in feature order from zero, which is exactly what a
|
|
17
|
+
* single shared buffer produced, so files written before and after this are byte-identical and each
|
|
18
|
+
* reads under the other.
|
|
19
|
+
*
|
|
20
|
+
* `omit` names properties to leave out of the serialized JSON - see the note on collectOmitted.
|
|
21
|
+
*/
|
|
22
|
+
export declare function packCollection(coder: Util.Coder, col: any, omit?: string[]): ArrayBuffer;
|
|
3
23
|
export declare function unpackCollection(coder: Util.Coder, ab: ArrayBuffer): any;
|
|
4
24
|
export declare function topoToBuffer(coder: Util.Coder, topo: any): ArrayBuffer;
|
|
5
25
|
export declare function topoFromBuffer(coder: Util.Coder, ab: ArrayBuffer): any;
|
package/dist/poly/polypack.d.ts
CHANGED
|
@@ -19,8 +19,42 @@ export declare function polyPackTopoArcs(topo: any, arcs: any, iPoly?: number):
|
|
|
19
19
|
export declare function polyPack(coords: any, prepack?: PolyPack): PolyPack;
|
|
20
20
|
export declare function polyPackCopy(coords: any): PolyPack;
|
|
21
21
|
export declare function polyUnpack(prepack: any): any;
|
|
22
|
+
/** Whether this feature's coordinates are already in packed form. */
|
|
23
|
+
export declare function featureIsPacked(f: any): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Floats this feature would ADD to a buffer if packed now: zero if it is already packed, and zero for
|
|
26
|
+
* the shapes that carry no packed coordinates at all (points, and features with no geometry).
|
|
27
|
+
*/
|
|
22
28
|
export declare function featurePackSize(f: any): number;
|
|
23
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Repair packed buffers that have been through JSON and come back as plain objects.
|
|
31
|
+
*
|
|
32
|
+
* A Float64Array does not survive JSON.stringify/parse - it comes back as {"0":n,"1":n,...} - so a
|
|
33
|
+
* collection that has been round-tripped through JSON while packed arrives with something that looks
|
|
34
|
+
* packed but cannot be read. This puts the numbers back into a real Float64Array.
|
|
35
|
+
*
|
|
36
|
+
* Split out of featurePack, where it used to sit inline in the middle of the packing path, because it
|
|
37
|
+
* is not packing: it is a repair for a specific way collections arrive damaged, it applies to
|
|
38
|
+
* collections that are ALREADY packed, and burying it inside meant featurePack had to return early
|
|
39
|
+
* with a different kind of value to signal it had happened. Returns whether anything was repaired.
|
|
40
|
+
*/
|
|
41
|
+
export declare function repairPackedBuffers(col: any): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Ensure a feature or collection is fully packed, and answer with the object handed in.
|
|
44
|
+
*
|
|
45
|
+
* ALWAYS the same object, whatever was passed and whatever it had to do. The previous signature
|
|
46
|
+
* returned three different things - a PolyPack for a feature, a PolyPack for a collection it packed,
|
|
47
|
+
* and the collection itself for one that was already packed - so a caller could not use the result
|
|
48
|
+
* without knowing which case it had fallen into, and every caller but one ignored it. The one that
|
|
49
|
+
* did not assigned a PolyPack to geometry.coordinates.
|
|
50
|
+
*
|
|
51
|
+
* A collection packs its UNPACKED features into one new buffer and leaves packed ones where they are.
|
|
52
|
+
* That is what stops a second call re-packing work already done, which for a large collection means
|
|
53
|
+
* allocating and copying the whole coordinate set again for nothing. Features already packed keep
|
|
54
|
+
* whatever buffer they were in - the collection may end up spanning several - and packCollection no
|
|
55
|
+
* longer minds, because it copies feature by feature rather than assuming one buffer holds them all.
|
|
56
|
+
*/
|
|
57
|
+
export declare function featurePack(f: any): any;
|
|
24
58
|
export declare function featureUnpack(f: any): any;
|
|
25
59
|
export type SavePack = WeakMap<any, any>;
|
|
26
60
|
export declare function featureUnpackTemporarily(f: any, save?: SavePack): SavePack;
|
package/lib/ot-js/otsession.ts
CHANGED
|
@@ -121,6 +121,7 @@ export interface SessionProps
|
|
|
121
121
|
published?: string;
|
|
122
122
|
locked?: boolean;
|
|
123
123
|
official: boolean;
|
|
124
|
+
template?: boolean;
|
|
124
125
|
loadFailed: boolean;
|
|
125
126
|
accessMap: AccessMap;
|
|
126
127
|
revisions: RevisionList;
|
|
@@ -143,6 +144,7 @@ export interface SessionUpdateProps
|
|
|
143
144
|
deleted?: boolean;
|
|
144
145
|
published?: boolean;
|
|
145
146
|
official?: boolean;
|
|
147
|
+
template?: boolean;
|
|
146
148
|
locked?: boolean;
|
|
147
149
|
name?: string;
|
|
148
150
|
description?: string;
|
package/lib/poly/polybin.ts
CHANGED
|
@@ -50,20 +50,109 @@ function unpackString(coder: Util.Coder, buf8: Uint8Array, buf32: Int32Array, of
|
|
|
50
50
|
return s;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Properties temporarily removed for the duration of a pack, and where they came from.
|
|
55
|
+
*
|
|
56
|
+
* WHY THIS IS WORTH HAVING. The JSON in a geopack is JSON.stringify of the collection, so anything
|
|
57
|
+
* hanging off a feature goes into the file. A Float64Array does not survive that: it serializes as
|
|
58
|
+
* {"0":n,"1":n,...}, which is both enormous and unreadable on the way back - the same damage
|
|
59
|
+
* repairPackedBuffers exists to undo for coordinates. PackedFields are exactly such arrays, and they
|
|
60
|
+
* are DERIVED, so writing them is waste even when it works. Naming them here means a caller cannot
|
|
61
|
+
* forget, and does not have to strip and rebuild them around every call.
|
|
62
|
+
*/
|
|
63
|
+
interface OmittedProps
|
|
54
64
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
o: any;
|
|
66
|
+
name: string;
|
|
67
|
+
value: any;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function collectOmitted(col: any, omit?: string[]): OmittedProps[]
|
|
71
|
+
{
|
|
72
|
+
let saved: OmittedProps[] = [];
|
|
73
|
+
if (! omit || omit.length === 0) return saved;
|
|
74
|
+
|
|
75
|
+
const take = (o: any) => {
|
|
76
|
+
if (! o) return;
|
|
77
|
+
omit.forEach(name => {
|
|
78
|
+
if (o[name] !== undefined) { saved.push({ o, name, value: o[name] }); delete o[name] }
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
take(col);
|
|
83
|
+
if (col.features) col.features.forEach((f: any) => { take(f); take(f.properties) });
|
|
84
|
+
return saved;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function restoreOmitted(saved: OmittedProps[]): void
|
|
88
|
+
{
|
|
89
|
+
saved.forEach(s => { s.o[s.name] = s.value });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** One feature's coordinates during a pack: where they are now, and where they are going. */
|
|
93
|
+
interface PackSlot
|
|
94
|
+
{
|
|
95
|
+
packed: any; // the feature's geometry.packed
|
|
96
|
+
buffer: any; // its buffer, to be put back afterwards
|
|
97
|
+
fromOffset: number; // its index within that buffer
|
|
98
|
+
toOffset: number; // its index within the file's coordinate section
|
|
99
|
+
length: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Serialize a collection to the geopack format.
|
|
104
|
+
*
|
|
105
|
+
* The file wants one contiguous coordinate section with every feature's offset an index into it, and
|
|
106
|
+
* that used to be arranged by requiring the whole collection to already sit in ONE buffer at exactly
|
|
107
|
+
* those offsets - packCollection simply blitted that buffer across whole. Which held only as long as
|
|
108
|
+
* nothing had touched the collection since: pack a collection, add a feature, pack again, and the
|
|
109
|
+
* second pack either redid all the work or produced offsets into a buffer that no longer described it.
|
|
110
|
+
*
|
|
111
|
+
* So the coordinates are gathered feature by feature instead. Each one is given its place in the
|
|
112
|
+
* output, and its floats are copied from wherever they happen to live now - one buffer or several. The
|
|
113
|
+
* work is the same (every float is copied either way), the assumption is gone, and featurePack is free
|
|
114
|
+
* to leave already-packed features alone.
|
|
115
|
+
*
|
|
116
|
+
* THE FORMAT IS UNCHANGED. Offsets are assigned in feature order from zero, which is exactly what a
|
|
117
|
+
* single shared buffer produced, so files written before and after this are byte-identical and each
|
|
118
|
+
* reads under the other.
|
|
119
|
+
*
|
|
120
|
+
* `omit` names properties to leave out of the serialized JSON - see the note on collectOmitted.
|
|
121
|
+
*/
|
|
122
|
+
export function packCollection(coder: Util.Coder, col: any, omit?: string[]): ArrayBuffer
|
|
123
|
+
{
|
|
124
|
+
// Which features arrive unpacked, so they can be left that way. Packing is how the coordinates get
|
|
125
|
+
// written; it is not something the caller asked for, and a caller that handed over an unpacked
|
|
126
|
+
// collection and got a packed one back would find its own coordinates gone. Equally, a caller whose
|
|
127
|
+
// collection was ALREADY packed must not have it unpacked - that rebuilds every coordinate array
|
|
128
|
+
// for nothing, which on a large collection is the memory this change exists to stop spending.
|
|
129
|
+
let wasUnpacked: any[] = [];
|
|
130
|
+
if (col.features) col.features.forEach((f: any) => { if (! PP.featureIsPacked(f)) wasUnpacked.push(f) });
|
|
131
|
+
|
|
132
|
+
PP.featurePack(col);
|
|
133
|
+
|
|
134
|
+
// Where every feature's coordinates are now, and where they will be in the file.
|
|
135
|
+
let slots: PackSlot[] = [];
|
|
136
|
+
let nFloats = 0;
|
|
137
|
+
col.features.forEach((f: any) => {
|
|
138
|
+
const packed = f.geometry ? f.geometry.packed : null;
|
|
139
|
+
if (! packed) return;
|
|
140
|
+
slots.push({ packed, buffer: packed.buffer, fromOffset: packed.offset, toOffset: nFloats,
|
|
141
|
+
length: packed.length });
|
|
142
|
+
nFloats += packed.length;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// The JSON carries each feature's offset, so the offsets have to be the file's before it is built.
|
|
146
|
+
// Buffers come out because they are reconstructed on the way back in.
|
|
147
|
+
slots.forEach(s => { s.packed.offset = s.toOffset; delete s.packed.buffer });
|
|
148
|
+
const omitted = collectOmitted(col, omit);
|
|
149
|
+
|
|
150
|
+
let size = 16; // int endianness, offset to coordinates, float endianness
|
|
61
151
|
let j = JSON.stringify(col);
|
|
62
152
|
size += sizeOfString(coder, j);
|
|
63
153
|
size += pad(size, 8);
|
|
64
|
-
let fullsize = size +
|
|
154
|
+
let fullsize = size + nFloats * 8;
|
|
65
155
|
|
|
66
|
-
// Now pack it
|
|
67
156
|
let ab = new ArrayBuffer(fullsize);
|
|
68
157
|
let buf8 = new Uint8Array(ab);
|
|
69
158
|
let buf32 = new Int32Array(ab);
|
|
@@ -79,14 +168,21 @@ export function packCollection(coder: Util.Coder, col: any): ArrayBuffer
|
|
|
79
168
|
offset += pad(offset, 8);
|
|
80
169
|
if (offset != size)
|
|
81
170
|
throw 'Oops, packing error.';
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
171
|
+
|
|
172
|
+
const foff = offset >> 3;
|
|
173
|
+
slots.forEach(s => {
|
|
174
|
+
const src = s.buffer as Float64Array;
|
|
175
|
+
if (! src) return; // no coordinates: points, and features with no geometry
|
|
176
|
+
let to = foff + s.toOffset;
|
|
177
|
+
let from = s.fromOffset;
|
|
178
|
+
for (let i = 0; i < s.length; i++)
|
|
179
|
+
buf64[to++] = src[from++];
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Put the collection back exactly as it was found.
|
|
183
|
+
slots.forEach(s => { s.packed.offset = s.fromOffset; s.packed.buffer = s.buffer });
|
|
184
|
+
restoreOmitted(omitted);
|
|
185
|
+
wasUnpacked.forEach(f => PP.featureUnpack(f));
|
|
90
186
|
|
|
91
187
|
return ab;
|
|
92
188
|
}
|
package/lib/poly/polypack.ts
CHANGED
|
@@ -330,14 +330,58 @@ export function polyUnpack(prepack: any): any
|
|
|
330
330
|
return coords.length > 1 ? coords : coords[0];
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
/** Whether this feature's coordinates are already in packed form. */
|
|
334
|
+
export function featureIsPacked(f: any): boolean
|
|
335
|
+
{
|
|
336
|
+
return !! (f && f.geometry && f.geometry.packed !== undefined);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Floats this feature would ADD to a buffer if packed now: zero if it is already packed, and zero for
|
|
341
|
+
* the shapes that carry no packed coordinates at all (points, and features with no geometry).
|
|
342
|
+
*/
|
|
333
343
|
export function featurePackSize(f: any): number
|
|
334
344
|
{
|
|
345
|
+
if (featureIsPacked(f)) return 0;
|
|
335
346
|
if (f && f.geometry && f.geometry.coordinates && f.geometry.type !== 'Point')
|
|
336
347
|
return polyPackSize(f.geometry.coordinates);
|
|
337
348
|
return 0;
|
|
338
349
|
}
|
|
339
350
|
|
|
340
|
-
|
|
351
|
+
/**
|
|
352
|
+
* Repair packed buffers that have been through JSON and come back as plain objects.
|
|
353
|
+
*
|
|
354
|
+
* A Float64Array does not survive JSON.stringify/parse - it comes back as {"0":n,"1":n,...} - so a
|
|
355
|
+
* collection that has been round-tripped through JSON while packed arrives with something that looks
|
|
356
|
+
* packed but cannot be read. This puts the numbers back into a real Float64Array.
|
|
357
|
+
*
|
|
358
|
+
* Split out of featurePack, where it used to sit inline in the middle of the packing path, because it
|
|
359
|
+
* is not packing: it is a repair for a specific way collections arrive damaged, it applies to
|
|
360
|
+
* collections that are ALREADY packed, and burying it inside meant featurePack had to return early
|
|
361
|
+
* with a different kind of value to signal it had happened. Returns whether anything was repaired.
|
|
362
|
+
*/
|
|
363
|
+
export function repairPackedBuffers(col: any): boolean
|
|
364
|
+
{
|
|
365
|
+
if (! col || ! col.features || col.features.length === 0) return false;
|
|
366
|
+
|
|
367
|
+
let ff: any = col.features[0];
|
|
368
|
+
if (! featureIsPacked(ff) || ArrayBuffer.isView(ff.geometry.packed.buffer)) return false;
|
|
369
|
+
|
|
370
|
+
// One buffer is shared by every feature of a collection, so recovering the first recovers all.
|
|
371
|
+
let o: any = ff.geometry.packed.buffer;
|
|
372
|
+
let b = new Float64Array(Util.countKeys(o));
|
|
373
|
+
for (const i in o) b[Number(i)] = o[i];
|
|
374
|
+
col.features.forEach((f: any) => { if (featureIsPacked(f)) f.geometry.packed.buffer = b });
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Pack ONE feature's coordinates, into `prepack`'s buffer when given one. Returns the PolyPack that
|
|
380
|
+
* describes where they went, which is how the collection path below advances its offset.
|
|
381
|
+
*
|
|
382
|
+
* Internal: featurePack is the public entry and always answers with the object it was handed.
|
|
383
|
+
*/
|
|
384
|
+
function packOne(f: any, prepack?: PolyPack): PolyPack
|
|
341
385
|
{
|
|
342
386
|
if (f && f.type === 'Feature')
|
|
343
387
|
{
|
|
@@ -355,45 +399,57 @@ export function featurePack(f: any, prepack?: PolyPack): any
|
|
|
355
399
|
}
|
|
356
400
|
return f.geometry.packed;
|
|
357
401
|
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Ensure a feature or collection is fully packed, and answer with the object handed in.
|
|
407
|
+
*
|
|
408
|
+
* ALWAYS the same object, whatever was passed and whatever it had to do. The previous signature
|
|
409
|
+
* returned three different things - a PolyPack for a feature, a PolyPack for a collection it packed,
|
|
410
|
+
* and the collection itself for one that was already packed - so a caller could not use the result
|
|
411
|
+
* without knowing which case it had fallen into, and every caller but one ignored it. The one that
|
|
412
|
+
* did not assigned a PolyPack to geometry.coordinates.
|
|
413
|
+
*
|
|
414
|
+
* A collection packs its UNPACKED features into one new buffer and leaves packed ones where they are.
|
|
415
|
+
* That is what stops a second call re-packing work already done, which for a large collection means
|
|
416
|
+
* allocating and copying the whole coordinate set again for nothing. Features already packed keep
|
|
417
|
+
* whatever buffer they were in - the collection may end up spanning several - and packCollection no
|
|
418
|
+
* longer minds, because it copies feature by feature rather than assuming one buffer holds them all.
|
|
419
|
+
*/
|
|
420
|
+
export function featurePack(f: any): any
|
|
421
|
+
{
|
|
422
|
+
if (f && f.type === 'Feature')
|
|
423
|
+
{
|
|
424
|
+
if (! featureIsPacked(f)) packOne(f);
|
|
425
|
+
return f;
|
|
426
|
+
}
|
|
358
427
|
else if (f && f.type === 'FeatureCollection' && f.features)
|
|
359
428
|
{
|
|
360
|
-
|
|
361
|
-
if (f.features.length == 0) return f;
|
|
429
|
+
if (f.features.length === 0) return f;
|
|
362
430
|
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
if (ff && ff.geometry && ff.geometry.packed)
|
|
366
|
-
{
|
|
367
|
-
// Already packed
|
|
368
|
-
if (ArrayBuffer.isView(ff.geometry.packed.buffer))
|
|
369
|
-
return f;
|
|
370
|
-
// Improperly packed (buffer converted to object - convert back to buffer
|
|
371
|
-
let o: any = ff.geometry.packed.buffer;
|
|
372
|
-
let b = new Float64Array(Util.countKeys(o));
|
|
373
|
-
for (const i in o) b[Number(i)] = o[i];
|
|
374
|
-
f.features.forEach((ff: any) => ff.geometry.packed.buffer = b);
|
|
375
|
-
return f;
|
|
376
|
-
}
|
|
431
|
+
// Before measuring anything: a collection in this state looks packed but is not readable.
|
|
432
|
+
repairPackedBuffers(f);
|
|
377
433
|
|
|
378
|
-
//
|
|
434
|
+
// One buffer for everything still unpacked. Sized first so it is allocated exactly once.
|
|
379
435
|
let nFloats: number = 0;
|
|
380
436
|
let i: number;
|
|
381
437
|
for (i = 0; i < f.features.length; i++)
|
|
382
438
|
nFloats += featurePackSize(f.features[i]);
|
|
383
|
-
let ab = new ArrayBuffer(nFloats * 8);
|
|
384
|
-
prepack = { offset: 0, length: nFloats, buffer: new Float64Array(ab) };
|
|
385
439
|
|
|
386
|
-
|
|
440
|
+
let prepack: PolyPack = { offset: 0, length: nFloats,
|
|
441
|
+
buffer: new Float64Array(new ArrayBuffer(nFloats * 8)) };
|
|
387
442
|
for (i = 0; i < f.features.length; i++)
|
|
388
443
|
{
|
|
389
|
-
|
|
444
|
+
if (featureIsPacked(f.features[i])) continue;
|
|
445
|
+
let postpack = packOne(f.features[i], prepack);
|
|
390
446
|
prepack.offset += postpack ? postpack.length : 0;
|
|
391
447
|
}
|
|
392
448
|
if (prepack.offset != nFloats)
|
|
393
449
|
throw 'oops, packing bug';
|
|
394
|
-
return
|
|
450
|
+
return f;
|
|
395
451
|
}
|
|
396
|
-
return
|
|
452
|
+
return f;
|
|
397
453
|
}
|
|
398
454
|
|
|
399
455
|
export function featureUnpack(f: any): any
|
package/lib/poly/topo.ts
CHANGED
|
@@ -36,6 +36,20 @@ function getGEOID(f: any): string
|
|
|
36
36
|
export function topoFromCollection(col: any): Topo
|
|
37
37
|
{
|
|
38
38
|
if (col == null) return null;
|
|
39
|
+
|
|
40
|
+
// The objects map below is keyed per feature, so every feature needs an id that is present, unique
|
|
41
|
+
// and a string - and the topology is meaningless without one. A collection that has no usable id
|
|
42
|
+
// would otherwise key EVERY feature to undefined and collapse the whole thing onto a single object,
|
|
43
|
+
// silently: the topology comes back with one object in it and nothing anywhere reports a problem.
|
|
44
|
+
// An imported GeoJSON file is the ordinary way to arrive here in that state, because GeoJSON puts a
|
|
45
|
+
// feature's id at the TOP level and most exporters write it there rather than in properties.
|
|
46
|
+
//
|
|
47
|
+
// geoEnsureID also NORMALIZES, which matters as much: it copies GEOID/GEOID10 into properties.id,
|
|
48
|
+
// so the key used here is properties.id whatever the collection called it. Callers that read the
|
|
49
|
+
// objects map back - topoSimplifyCollection does, by properties.id - would otherwise be looking up
|
|
50
|
+
// a key this function never used.
|
|
51
|
+
G.geoEnsureID(col);
|
|
52
|
+
|
|
39
53
|
let save = PP.featureUnpackTemporarily(col);
|
|
40
54
|
let prop = getGEOID(col);
|
|
41
55
|
let objects: any = {};
|
|
@@ -260,6 +274,14 @@ export function topoSimplifyCollection(col: any, options?: SimplifyOptions): any
|
|
|
260
274
|
let oOld: any = topo.objects[f.properties.id];
|
|
261
275
|
let oNew: any = testtopo.objects[f.properties.id];
|
|
262
276
|
|
|
277
|
+
// Every feature must have found its own object. topoFromCollection guarantees it, so reaching
|
|
278
|
+
// here means the keying has diverged - and the two ways that goes wrong are both silent or
|
|
279
|
+
// worse: a collection with no ids used to put every feature on ONE object and come back
|
|
280
|
+
// simplified to a single feature, and a mismatched key dereferences undefined a few lines
|
|
281
|
+
// below. Neither is worth guessing through, so stop and leave the collection alone.
|
|
282
|
+
if (oOld === undefined || oNew === undefined)
|
|
283
|
+
throw `topoSimplifyCollection: feature id ${f.properties.id} has no topology object`;
|
|
284
|
+
|
|
263
285
|
// Ignore points
|
|
264
286
|
if (f.geometry && f.geometry.type === 'Point')
|
|
265
287
|
keepTiny.set(f, f);
|