@dra2020/baseclient 1.0.15 → 1.0.18

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 (97) hide show
  1. package/README.md +5 -16
  2. package/dist/all/all.d.ts +2 -18
  3. package/dist/baseclient.js +1323 -173
  4. package/dist/baseclient.js.map +1 -1
  5. package/dist/filterexpr/filterexpr.d.ts +3 -0
  6. package/dist/fsm/fsm.d.ts +1 -0
  7. package/dist/geo/all.d.ts +2 -0
  8. package/dist/geo/geo.d.ts +68 -0
  9. package/dist/geo/vfeature.d.ts +4 -0
  10. package/dist/ot-js/otsession.d.ts +3 -0
  11. package/dist/poly/union.d.ts +1 -0
  12. package/docs/filterexpr.md +22 -0
  13. package/lib/all/all.ts +2 -22
  14. package/lib/filterexpr/filterexpr.ts +79 -5
  15. package/lib/fsm/fsm.ts +12 -2
  16. package/lib/geo/all.ts +2 -0
  17. package/lib/geo/geo.ts +467 -0
  18. package/lib/geo/vfeature.ts +34 -0
  19. package/lib/ot-js/otsession.ts +4 -1
  20. package/lib/poly/hash.ts +1 -1
  21. package/lib/poly/polybin.ts +5 -4
  22. package/lib/poly/polypack.ts +16 -4
  23. package/lib/poly/topo.ts +26 -41
  24. package/lib/poly/union.ts +17 -0
  25. package/package.json +17 -5
  26. package/dist/all/allclient.d.ts +0 -18
  27. package/dist/base.js +0 -33010
  28. package/dist/base.js.map +0 -1
  29. package/dist/dbabstract/all.d.ts +0 -1
  30. package/dist/dbabstract/db.d.ts +0 -83
  31. package/dist/dbdynamo/all.d.ts +0 -1
  32. package/dist/dbdynamo/dbdynamo.d.ts +0 -190
  33. package/dist/fsmfile/all.d.ts +0 -1
  34. package/dist/fsmfile/fsmfile.d.ts +0 -47
  35. package/dist/jsonstream/all.d.ts +0 -1
  36. package/dist/jsonstream/jsonstream.d.ts +0 -130
  37. package/dist/lambda/all.d.ts +0 -1
  38. package/dist/lambda/env.d.ts +0 -10
  39. package/dist/lambda/lambda.d.ts +0 -18
  40. package/dist/logserver/all.d.ts +0 -5
  41. package/dist/logserver/log.d.ts +0 -11
  42. package/dist/logserver/logaccum.d.ts +0 -154
  43. package/dist/logserver/logblob.d.ts +0 -24
  44. package/dist/logserver/logconcat.d.ts +0 -55
  45. package/dist/logserver/logkey.d.ts +0 -28
  46. package/dist/memsqs/all.d.ts +0 -4
  47. package/dist/memsqs/client.d.ts +0 -13
  48. package/dist/memsqs/loopback.d.ts +0 -11
  49. package/dist/memsqs/orderedlist.d.ts +0 -19
  50. package/dist/memsqs/queue.d.ts +0 -84
  51. package/dist/memsqs/server.d.ts +0 -37
  52. package/dist/storage/all.d.ts +0 -4
  53. package/dist/storage/datablob.d.ts +0 -9
  54. package/dist/storage/env.d.ts +0 -10
  55. package/dist/storage/splitsblob.d.ts +0 -13
  56. package/dist/storage/storage.d.ts +0 -166
  57. package/dist/storages3/all.d.ts +0 -1
  58. package/dist/storages3/s3.d.ts +0 -62
  59. package/docs/dbabstract.md +0 -2
  60. package/docs/dbdynamo.md +0 -2
  61. package/docs/fsmfile.md +0 -2
  62. package/docs/jsonstream.md +0 -44
  63. package/docs/lambda.md +0 -2
  64. package/docs/logserver.md +0 -2
  65. package/docs/storage.md +0 -2
  66. package/docs/storages3.md +0 -2
  67. package/lib/all/allclient.ts +0 -19
  68. package/lib/dbabstract/all.ts +0 -1
  69. package/lib/dbabstract/db.ts +0 -246
  70. package/lib/dbdynamo/all.ts +0 -1
  71. package/lib/dbdynamo/dbdynamo.ts +0 -1551
  72. package/lib/fsmfile/all.ts +0 -1
  73. package/lib/fsmfile/fsmfile.ts +0 -236
  74. package/lib/jsonstream/all.ts +0 -1
  75. package/lib/jsonstream/jsonstream.ts +0 -940
  76. package/lib/lambda/all.ts +0 -1
  77. package/lib/lambda/env.ts +0 -13
  78. package/lib/lambda/lambda.ts +0 -120
  79. package/lib/logserver/all.ts +0 -5
  80. package/lib/logserver/log.ts +0 -565
  81. package/lib/logserver/logaccum.ts +0 -1445
  82. package/lib/logserver/logblob.ts +0 -84
  83. package/lib/logserver/logconcat.ts +0 -313
  84. package/lib/logserver/logkey.ts +0 -125
  85. package/lib/memsqs/all.ts +0 -4
  86. package/lib/memsqs/client.ts +0 -268
  87. package/lib/memsqs/loopback.ts +0 -64
  88. package/lib/memsqs/orderedlist.ts +0 -74
  89. package/lib/memsqs/queue.ts +0 -395
  90. package/lib/memsqs/server.ts +0 -262
  91. package/lib/storage/all.ts +0 -4
  92. package/lib/storage/datablob.ts +0 -36
  93. package/lib/storage/env.ts +0 -14
  94. package/lib/storage/splitsblob.ts +0 -63
  95. package/lib/storage/storage.ts +0 -604
  96. package/lib/storages3/all.ts +0 -1
  97. package/lib/storages3/s3.ts +0 -576
package/lib/geo/geo.ts ADDED
@@ -0,0 +1,467 @@
1
+ import * as geojson from 'geojson';
2
+ import * as Util from '../util/all'
3
+ import * as Poly from '../poly/all'
4
+
5
+ export type GeoProperties = geojson.GeoJsonProperties;
6
+ export type GeoFeature = geojson.Feature;
7
+ export type GeoFeatureArray = GeoFeature[];
8
+ export type GeoFeatureCollection = geojson.FeatureCollection;
9
+
10
+ export function geoEnsureID(col: GeoFeatureCollection): void
11
+ {
12
+ let prop: string;
13
+ const props = ['id', 'GEOID', 'GEOID10', 'GEOID20', 'GEOID30' ];
14
+
15
+ if (col && col.features && col.features.length > 0)
16
+ {
17
+ let f = col.features[0];
18
+ if (f.properties.id !== undefined) return;
19
+ props.forEach(p => { if (prop === undefined && f.properties[p] !== undefined) prop = p; });
20
+ if (prop)
21
+ col.features.forEach(f => { f.properties.id = f.properties[prop] });
22
+ }
23
+ }
24
+
25
+ export function geoCollectionToMap(col: GeoFeatureCollection): GeoFeatureMap
26
+ {
27
+ if (col == null) return null;
28
+ let map: GeoFeatureMap = {};
29
+ col.features.forEach((f: GeoFeature) => { map[String(f.properties.id)] = f; });
30
+ return map;
31
+ }
32
+
33
+ export function geoMapToCollection(map: GeoFeatureMap): GeoFeatureCollection
34
+ {
35
+ if (map == null || Util.countKeys(map) == 0) return null;
36
+ let col: GeoFeatureCollection = { type: 'FeatureCollection', features: [] };
37
+ Object.keys(map).forEach((geoid: string) => { col.features.push(map[geoid]) });
38
+ return col;
39
+ }
40
+
41
+ export function geoCollectionToTopo(col: GeoFeatureCollection): Poly.Topo
42
+ {
43
+ let topo = Poly.topoFromCollection(col);
44
+ Poly.topoPack(topo);
45
+ return topo;
46
+ }
47
+
48
+ export function geoTopoToCollection(topo: Poly.Topo): GeoFeatureCollection
49
+ {
50
+ let col = Poly.topoToCollection(topo);
51
+ Poly.featurePack(col);
52
+ return col;
53
+ }
54
+
55
+ export interface GeoFeatureMap
56
+ {
57
+ [id: string]: GeoFeature; // Maps id to GeoFeature
58
+ }
59
+
60
+ export type FeatureFunc = (f: GeoFeature) => void;
61
+
62
+ interface GeoEntry
63
+ {
64
+ tag: string;
65
+ col?: GeoFeatureCollection;
66
+ map?: GeoFeatureMap;
67
+ topo?: Poly.Topo;
68
+ }
69
+ type GeoEntryMap = { [tag: string]: GeoEntry };
70
+
71
+ export function geoEqual(m1: GeoMultiCollection, m2: GeoMultiCollection): boolean
72
+ {
73
+ let n1 = m1 ? m1.length : 0;
74
+ let n2 = m2 ? m2.length : 0;
75
+
76
+ if (n1 != n2) return false;
77
+ if (n1 == 0) return true;
78
+
79
+ let n = 0;
80
+ let eq = true;
81
+ m1.forEach(f => { if (eq && !m2.find(f.properties.id)) eq = false });
82
+ return eq;
83
+ }
84
+
85
+ export function geoMapEqual(m1: GeoFeatureMap, m2: GeoFeatureMap): boolean
86
+ {
87
+ if (m1 == null) return Util.isEmpty(m2);
88
+ if (m2 == null) return Util.isEmpty(m1);
89
+ let p: string;
90
+ for (p in m1) if (m1.hasOwnProperty(p))
91
+ if (m1[p] !== m2[p])
92
+ return false;
93
+ for (p in m2) if (m2.hasOwnProperty(p))
94
+ if (m1[p] === undefined)
95
+ return false;
96
+ return true;
97
+ }
98
+
99
+ export class GeoMultiCollection
100
+ {
101
+ entries: GeoEntryMap;
102
+ all: GeoEntry;
103
+ hidden: any;
104
+ stamp: number;
105
+
106
+ constructor(tag?: string, topo?: Poly.Topo, col?: GeoFeatureCollection, map?: GeoFeatureMap)
107
+ {
108
+ this.stamp = Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER / 2);
109
+ this.empty();
110
+ if (tag)
111
+ this.add(tag, topo, col, map);
112
+ }
113
+
114
+ empty()
115
+ {
116
+ this.entries = {};
117
+ this.hidden = {};
118
+ this._onChange();
119
+ }
120
+
121
+ get nEntries(): number { return Util.countKeys(this.entries) }
122
+
123
+ nthEntry(n: number): GeoEntry
124
+ {
125
+ return Util.nthProperty(this.entries, n) as GeoEntry;
126
+ }
127
+
128
+ add(tag: string, topo: Poly.Topo, col: GeoFeatureCollection, map: GeoFeatureMap): void
129
+ {
130
+ let entry = this.entries[tag];
131
+ if (entry === undefined) entry = { tag: tag }, this.entries[tag] = entry;
132
+ if ((topo && entry.topo !== topo) || (col && entry.col !== col) || (map && entry.map !== map))
133
+ {
134
+ entry.topo = topo;
135
+ entry.col = col;
136
+ entry.map = map;
137
+ this._onChange();
138
+ }
139
+ else if (topo == null && col == null && map == null)
140
+ this.remove(tag);
141
+ }
142
+
143
+ addMulti(multi: GeoMultiCollection): void
144
+ {
145
+ multi.forEachEntry(e => {
146
+ this.add(e.tag, e.topo, e.col, e.map);
147
+ });
148
+ for (let p in multi.hidden) if (multi.hidden.hasOwnProperty(p))
149
+ {
150
+ this.hidden[p] = true;
151
+ this._onChange();
152
+ }
153
+ }
154
+
155
+ remove(tag: string): void
156
+ {
157
+ let entry = this.entries[tag];
158
+ if (entry)
159
+ {
160
+ if (entry.topo || entry.col || entry.map)
161
+ this._onChange();
162
+ delete this.entries[tag];
163
+ }
164
+ }
165
+
166
+ _onChange(): void
167
+ {
168
+ this.all = { tag: 'all' };
169
+ this.stamp++;
170
+ }
171
+
172
+ _col(e: GeoEntry): GeoFeatureCollection
173
+ {
174
+ if (e == null) return null;
175
+ if (! e.col)
176
+ {
177
+ if (e.map)
178
+ e.col = geoMapToCollection(e.map);
179
+ else if (e.topo)
180
+ e.col = geoTopoToCollection(e.topo);
181
+ }
182
+ return e.col;
183
+ }
184
+
185
+ _map(e: GeoEntry): GeoFeatureMap
186
+ {
187
+ if (e == null) return null;
188
+ if (! e.map)
189
+ {
190
+ if (e.col)
191
+ e.map = geoCollectionToMap(e.col);
192
+ else if (e.topo)
193
+ {
194
+ e.col = geoTopoToCollection(e.topo);
195
+ e.map = geoCollectionToMap(e.col);
196
+ }
197
+ }
198
+ return e.map;
199
+ }
200
+
201
+ _topo(e: GeoEntry): Poly.Topo
202
+ {
203
+ if (e == null) return null;
204
+ if (! e.topo)
205
+ {
206
+ if (e.col)
207
+ e.topo = geoCollectionToTopo(e.col);
208
+ else if (e.map)
209
+ {
210
+ e.col = geoMapToCollection(e.map);
211
+ e.topo = geoCollectionToTopo(e.col);
212
+ }
213
+ }
214
+ return e.topo;
215
+ }
216
+
217
+ colOf(tag: string): GeoFeatureCollection { return this._col(this.entries[tag]); }
218
+ mapOf(tag: string): GeoFeatureMap { return this._map(this.entries[tag]); }
219
+ topoOf(tag: string): Poly.Topo { return this._topo(this.entries[tag]); }
220
+
221
+ forEachEntry(cb: (e: GeoEntry) => void): void
222
+ {
223
+ Object.values(this.entries).forEach(cb);
224
+ }
225
+
226
+ allCol(): GeoFeatureCollection
227
+ {
228
+ if (this.nEntries == 0) return null;
229
+ if (! this.all.col)
230
+ {
231
+ // optimise case where one entry
232
+ let n = this.nEntries;
233
+ if (n == 1)
234
+ this.all.col = this._col(this.nthEntry(0));
235
+ else
236
+ // Going from map to collection guarantees that any duplicates are removed
237
+ this.all.col = geoMapToCollection(this.allMap());
238
+ }
239
+ return this.all.col;
240
+ }
241
+
242
+ allMap(): GeoFeatureMap
243
+ {
244
+ if (this.nEntries == 0) return null;
245
+ if (! this.all.map)
246
+ {
247
+ // optimise case where one entry
248
+ let n = this.nEntries;
249
+ if (n == 1)
250
+ this.all.map = this._map(this.nthEntry(0));
251
+ else
252
+ {
253
+ let map: GeoFeatureMap = {};
254
+ this.all.map = map;
255
+ this.forEach(f => { map[String(f.properties.id)] = f });
256
+ }
257
+ }
258
+ return this.all.map;
259
+ }
260
+
261
+ allTopo(): Poly.Topo
262
+ {
263
+ if (this.nEntries == 0) return null;
264
+ if (! this.all.topo)
265
+ {
266
+ // optimise case where one entry
267
+ let n = this.nEntries;
268
+ if (n == 1)
269
+ this.all.topo = this._topo(this.nthEntry(0));
270
+ else
271
+ this.all.topo = geoCollectionToTopo(this.allCol());
272
+ }
273
+ return this.all.topo;
274
+ }
275
+
276
+ hide(id: any): void
277
+ {
278
+ if (id)
279
+ {
280
+ if (typeof id === 'string')
281
+ this.hidden[id] = true;
282
+ else if (Array.isArray(id))
283
+ id.forEach((i: string) => { this.hidden[i] = true })
284
+ else if (typeof id === 'object')
285
+ for (let p in id) if (id.hasOwnProperty(p)) this.hidden[p] = true;
286
+ this._onChange();
287
+ }
288
+ }
289
+
290
+ show(id: any): void
291
+ {
292
+ if (id)
293
+ {
294
+ if (typeof id === 'string')
295
+ delete this.hidden[id];
296
+ else if (Array.isArray(id))
297
+ id.forEach((i: string) => { delete this.hidden[i] })
298
+ else if (typeof id === 'object')
299
+ for (let p in id) if (id.hasOwnProperty(p)) delete this.hidden[p];
300
+ this._onChange();
301
+ }
302
+ }
303
+
304
+ showAll(): void
305
+ {
306
+ if (! Util.isEmpty(this.hidden))
307
+ {
308
+ this.hidden = {};
309
+ this._onChange();
310
+ }
311
+ }
312
+
313
+ get length(): number
314
+ {
315
+ let n = 0;
316
+ this.forEachEntry(e => {
317
+ if (e.col)
318
+ n += e.col.features.length;
319
+ else if (e.map)
320
+ n += Util.countKeys(e.map);
321
+ else if (e.topo)
322
+ n += Util.countKeys(e.topo.objects);
323
+ });
324
+ return n;
325
+ }
326
+
327
+ // Use forEach in preference to iteration using this function
328
+ nthFeature(n: number): GeoFeature
329
+ {
330
+ let found: GeoFeature;
331
+
332
+ if (n >= 0)
333
+ this.forEachEntry(e => {
334
+ if (found) return;
335
+ let col = this._col(e);
336
+ if (col)
337
+ if (n > col.features.length)
338
+ n -= col.features.length;
339
+ else
340
+ found = col.features[n];
341
+ });
342
+ return found;
343
+ }
344
+
345
+ nthFilteredFeature(n: number, cb: (f: GeoFeature) => boolean)
346
+ {
347
+ let found: GeoFeature;
348
+
349
+ this.forEachEntry(e => {
350
+ if (found) return;
351
+ let col = this._col(e);
352
+ if (col)
353
+ for (let i = 0; !found && i < col.features.length; i++)
354
+ {
355
+ let f = col.features[i];
356
+ if (this.hidden[f.properties.id] === undefined && cb(f))
357
+ {
358
+ if (n === 0)
359
+ {
360
+ found = f;
361
+ break;
362
+ }
363
+ n--;
364
+ }
365
+ }
366
+ });
367
+ return found;
368
+ }
369
+
370
+ forEach(cb: FeatureFunc): void
371
+ {
372
+ this.forEachEntry(e => {
373
+ let col = this._col(e);
374
+ if (e.col)
375
+ e.col.features.forEach(f => { if (this.hidden[f.properties.id] === undefined) cb(f) })
376
+ });
377
+ }
378
+
379
+ map(cb: (f: GeoFeature) => GeoFeature): GeoFeature[]
380
+ {
381
+ let features: GeoFeature[] = [];
382
+ this.forEach((f: GeoFeature) => { features.push(cb(f)) });
383
+ return features;
384
+ }
385
+
386
+ isHidden(id: string): boolean
387
+ {
388
+ return this.hidden[id] !== undefined;
389
+ }
390
+
391
+ find(id: string): GeoFeature
392
+ {
393
+ if (this.hidden[id] !== undefined)
394
+ return undefined;
395
+
396
+ let entries = Object.values(this.entries);
397
+ for (let i = 0; i < entries.length; i++)
398
+ {
399
+ let map = this._map(entries[i]);
400
+ if (map[id])
401
+ return map[id];
402
+ }
403
+
404
+ return undefined;
405
+ }
406
+
407
+ filter(test: (f: GeoFeature) => boolean): GeoMultiCollection
408
+ {
409
+ let m = new GeoMultiCollection();
410
+ this.forEachEntry(e => {
411
+ let col = this._col(e);
412
+ let features = col ? col.features.filter(test) : null;
413
+ if (features && features.length)
414
+ m.add(e.tag, null, { type: 'FeatureCollection', features: features }, null);
415
+ });
416
+ return m;
417
+ }
418
+ }
419
+
420
+ export enum geoIntersectOptions { Intersects, Bounds, BoundsCenter };
421
+
422
+ function geoBoxIntersect(x1: Poly.BoundBox, x2: Poly.BoundBox, opt: geoIntersectOptions): boolean
423
+ {
424
+ if (x1.left === undefined || x2.left === undefined) return false;
425
+
426
+ let l1 = x1.left;
427
+ let l2 = x2.left;
428
+ let r1 = x1.right;
429
+ let r2 = x2.right;
430
+ let b1 = x1.top; // flip
431
+ let b2 = x2.top; // flip
432
+ let t1 = x1.bottom; // flip
433
+ let t2 = x2.bottom; // flip
434
+ let cx2 = l2 + (r2 - l2) / 2;
435
+ let cy2 = t2 + (b2 - t2) / 2;
436
+
437
+ // Note I flipped top and bottom above when extracting,
438
+ // in order to make below logic work for normal y axis alignment (0 at top).
439
+ switch (opt)
440
+ {
441
+ case geoIntersectOptions.Intersects:
442
+ return !(l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1);
443
+ case geoIntersectOptions.Bounds:
444
+ return l1 <= l2 && t1 <= t2 && r1 >= r2 && b1 >= b2;
445
+ case geoIntersectOptions.BoundsCenter:
446
+ return l1 <= cx2 && t1 <= cy2 && r1 >= cx2 && b1 >= cy2;
447
+ }
448
+ }
449
+
450
+ export function geoIntersect(multi: GeoMultiCollection, bbox: Poly.BoundBox, opt: geoIntersectOptions): GeoMultiCollection
451
+ {
452
+ let m: GeoFeatureMap = {};
453
+ let bboxPoly = Poly.boundboxPoly(bbox);
454
+
455
+ multi.forEach((f: GeoFeature) => {
456
+ let box = Poly.boundbox(f);
457
+ if (geoBoxIntersect(bbox, box, opt))
458
+ {
459
+ if (opt !== geoIntersectOptions.Intersects || Poly.polyIntersects(bboxPoly, f))
460
+ m[f.properties.id] = f;
461
+ }
462
+ });
463
+
464
+ let result = new GeoMultiCollection();
465
+ result.add('result', null, null, m);
466
+ return result;
467
+ }
@@ -0,0 +1,34 @@
1
+ import * as geojson from 'geojson';
2
+ import * as Util from '../util/all'
3
+ import * as Poly from '../poly/all'
4
+ import * as G from './geo';
5
+
6
+ // Given the topology for a precinct, the bintrie mapping and the list of blocks, construct the
7
+ // feature data for the virtual feature.
8
+ //
9
+
10
+ export function computeVFeature(topoPrecinct: Poly.Topo, bintrie: Util.BinTrie, blocks: string[]): G.GeoFeature
11
+ {
12
+ let contiguity = new Util.IndexedArray();
13
+ let block_contiguity = new Util.IndexedArray();
14
+ let f = Poly.topoMerge(topoPrecinct, blocks);
15
+ f.properties.datasets = {};
16
+ blocks.forEach(blockid => {
17
+ let b = topoPrecinct.objects[blockid];
18
+ if (b.properties.datasets)
19
+ Util.deepAccum(f.properties.datasets, b.properties.datasets);
20
+ if (b.properties.contiguity)
21
+ {
22
+ b.properties.contiguity.forEach((id: string) => {
23
+ contiguity.set(id === 'OUT_OF_STATE' ? id : bintrie.get(id));
24
+ });
25
+ b.properties.contiguity.forEach((id: string) => {
26
+ block_contiguity.set(id);
27
+ });
28
+ }
29
+ });
30
+ f.properties.contiguity = contiguity.asArray();
31
+ f.properties.block_contiguity = block_contiguity.asArray();
32
+ f.properties.blocks = blocks;
33
+ return f;
34
+ }
@@ -23,7 +23,8 @@ export const FilterRecent: number = 3;
23
23
  export const FilterTrash: number = 4;
24
24
  export const FilterPublic: number = 5;
25
25
  export const FilterOfficial: number = 6;
26
- export const FilterCount: number = 7;
26
+ export const FilterCOI: number = 7;
27
+ export const FilterCount: number = 8;
27
28
  export type Filter = number;
28
29
 
29
30
  // Permissions
@@ -111,6 +112,8 @@ export interface SessionProps
111
112
  loadFailed: boolean;
112
113
  accessMap: AccessMap;
113
114
  revisions: RevisionList;
115
+ expunged?: boolean;
116
+ expungeDate?: string;
114
117
  xprops?: { [prop: string]: string };
115
118
  }
116
119
 
package/lib/poly/hash.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as objectHash from 'object-hash';
1
+ import objectHash from 'object-hash';
2
2
 
3
3
  export function qhash(o: any, keys?: any): any
4
4
  {
@@ -54,9 +54,10 @@ export function packCollection(coder: Util.Coder, col: any): ArrayBuffer
54
54
  {
55
55
  // Compute size
56
56
  let pp = PP.featurePack(col) as PP.PolyPack;
57
- let buffer: any = col.features.length ? col.features[0].geometry.packed.buffer : null; // to restore, below
57
+ let f: any = col.features.find((f: any) => { return f.geometry.packed ? f.geometry.packed.buffer : null });
58
+ let buffer: any = f ? f.geometry.packed.buffer : null;
58
59
  let size = 16; // int endiness, offset to coordinates, float endiness
59
- col.features.forEach((f: any) => { delete f.geometry.packed.buffer; }); // reconstructed when unpacking
60
+ col.features.forEach((f: any) => { if (f.geometry.packed) delete f.geometry.packed.buffer; }); // reconstructed when unpacking
60
61
  let j = JSON.stringify(col);
61
62
  size += sizeOfString(coder, j);
62
63
  size += pad(size, 8);
@@ -84,7 +85,7 @@ export function packCollection(coder: Util.Coder, col: any): ArrayBuffer
84
85
  buf64[foff++] = buf[i];
85
86
 
86
87
  // Now restore
87
- col.features.forEach((f: any) => { f.geometry.packed.buffer = buffer; });
88
+ col.features.forEach((f: any) => { if (f.geometry.packed) f.geometry.packed.buffer = buffer; });
88
89
  PP.featureUnpack(col);
89
90
 
90
91
  return ab;
@@ -144,7 +145,7 @@ export function unpackCollection(coder: Util.Coder, ab: ArrayBuffer): any
144
145
  let offset = 16;
145
146
  let j = unpackString(coder, buf8, buf32, offset);
146
147
  col = JSON.parse(j);
147
- col.features.forEach((f: any) => { f.geometry.packed.buffer = buf64 });
148
+ col.features.forEach((f: any) => { if (f.geometry.packed) f.geometry.packed.buffer = buf64 });
148
149
  return col;
149
150
  }
150
151
 
@@ -221,7 +221,8 @@ export function polyPack(coords: any, prepack?: PolyPack): PolyPack
221
221
 
222
222
  // Transparently handle polygon or multi-polygon
223
223
  let depth = Util.depthof(coords);
224
- if (depth == 3) coords = [ [ coords ] ];
224
+ if (depth == 2) coords = [ [ [ coords ] ] ];
225
+ else if (depth == 3) coords = [ [ coords ] ];
225
226
  else if (depth == 4) coords = [ coords ];
226
227
 
227
228
  let nFloats = polyPackSize(coords);
@@ -325,7 +326,7 @@ export function polyUnpack(prepack: any): any
325
326
 
326
327
  export function featurePackSize(f: any): number
327
328
  {
328
- if (f && f.geometry && f.geometry.coordinates)
329
+ if (f && f.geometry && f.geometry.coordinates && f.geometry.type !== 'Point')
329
330
  return polyPackSize(f.geometry.coordinates);
330
331
  return 0;
331
332
  }
@@ -334,7 +335,9 @@ export function featurePack(f: any, prepack?: PolyPack): any
334
335
  {
335
336
  if (f && f.type === 'Feature')
336
337
  {
337
- if (f.geometry && f.geometry.coordinates)
338
+ if (f.geometry && f.geometry.type === 'Point')
339
+ return prepack ? { offset: prepack.offset, length: 0, buffer: prepack.buffer } : { offset: 0, length: 0, buffer: null };
340
+ else if (f.geometry && f.geometry.coordinates)
338
341
  {
339
342
  f.geometry.packed = polyPack(f.geometry.coordinates, prepack);
340
343
  delete f.geometry.coordinates;
@@ -374,9 +377,18 @@ export function featureUnpack(f: any): any
374
377
  if (f && f.geometry && f.geometry.packed !== undefined)
375
378
  {
376
379
  f.geometry.coordinates = polyUnpack(f.geometry.packed);
380
+ let depth = Util.depthof(f.geometry.coordinates);
377
381
  // Check for oops, optimized away the multipolygon in polyUnpack
378
- if (f.geometry.type === 'MultiPolygon' && Util.depthof(f.geometry.coordinates) === 4)
382
+ if (f.geometry.type === 'MultiPolygon' && depth === 4)
379
383
  f.geometry.coordinates = [ f.geometry.coordinates ];
384
+ else if (f.geometry.type === 'Point' && depth != 2)
385
+ {
386
+ while (depth > 2)
387
+ {
388
+ f.geometry.coordinates = f.geometry.coordinates[0];
389
+ depth = Util.depthof(f.geometry.coordinates);
390
+ }
391
+ }
380
392
  delete f.geometry.packed;
381
393
  }
382
394
  else if (f.type && f.type === 'FeatureCollection' && f.features)