@dra2020/baseclient 1.0.49 → 1.0.52

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/geo/geo.d.ts CHANGED
@@ -10,7 +10,14 @@ export declare type GeoCentroidMap = {
10
10
  y: number;
11
11
  };
12
12
  };
13
- export declare function geoEnsureID(col: GeoFeatureCollection): void;
13
+ export interface NormalizeOptions {
14
+ joinPolygons?: boolean;
15
+ checkRewind?: boolean;
16
+ ensureID?: boolean;
17
+ }
18
+ export declare function geoEnsureID(col: GeoFeatureCollection, options?: NormalizeOptions): void;
19
+ export declare function geoNormalizeFeature(f: any, options?: NormalizeOptions): GeoFeature;
20
+ export declare function geoNormalizeCollection(col: GeoFeatureCollection, options?: NormalizeOptions): GeoFeatureCollection;
14
21
  export declare function geoCollectionToMap(col: GeoFeatureCollection): GeoFeatureMap;
15
22
  export declare function geoMapToCollection(map: GeoFeatureMap): GeoFeatureCollection;
16
23
  export declare function geoMapToCollectionNonNull(map: GeoFeatureMap): GeoFeatureCollection;
@@ -8,6 +8,7 @@ export declare function topoToFeature(topo: Topo, geoid: string): any;
8
8
  export declare function topoToCollection(topo: Topo): any;
9
9
  export interface SimplifyOptions {
10
10
  minArea?: number;
11
+ log?: boolean;
11
12
  }
12
13
  export declare function topoSimplifyCollection(col: any, options?: SimplifyOptions): any;
13
14
  export declare function topoMerge(topo: Topo, geoids: string[]): any;
@@ -3,3 +3,4 @@ export * from './countedhash';
3
3
  export * from './indexedarray';
4
4
  export * from './gradient';
5
5
  export * from './bintrie';
6
+ export * from './bitset';
@@ -0,0 +1,10 @@
1
+ export declare class ListToBitset {
2
+ list: string[];
3
+ index: {
4
+ [s: string]: number;
5
+ };
6
+ size: number;
7
+ constructor(list: string[]);
8
+ toBits(l: string[]): Uint8Array;
9
+ toList(u8: Uint8Array): string[];
10
+ }
package/lib/geo/geo.ts CHANGED
@@ -8,16 +8,40 @@ export type GeoFeatureArray = GeoFeature[];
8
8
  export type GeoFeatureCollection = geojson.FeatureCollection;
9
9
  export type GeoCentroidMap = { [geoid: string]: { x: number, y: number } };
10
10
 
11
- export function geoEnsureID(col: GeoFeatureCollection): void
11
+ export interface NormalizeOptions
12
12
  {
13
+ joinPolygons?: boolean,
14
+ checkRewind?: boolean,
15
+ ensureID?: boolean,
16
+ }
17
+ const NormalizeAll: NormalizeOptions = { joinPolygons: true, checkRewind: true, ensureID: true };
18
+
19
+ // set the canonical 'id' property from the best property value.
20
+ // if joinPolygons is true, we do not enforce uniqueness.
21
+ //
22
+
23
+ export function geoEnsureID(col: GeoFeatureCollection, options?: NormalizeOptions): void
24
+ {
25
+ options = Util.shallowAssignImmutable({}, options);
26
+
13
27
  let prop: string;
14
- const props = ['id', 'GEOID', 'GEOID10', 'GEOID20', 'GEOID30' ];
28
+ const props = [ 'id', 'GEOID', 'GEOID10', 'GEOID20', 'GEOID30', 'DISTRICT', 'DISTRICTNO', 'DISTRICTNAME' ];
15
29
 
16
30
  if (col && col.features && col.features.length > 0)
17
31
  {
18
32
  let f = col.features[0];
19
- if (f.properties.id !== undefined) return;
20
- props.forEach(p => { if (prop === undefined && f.properties[p] !== undefined) prop = p; });
33
+ if (f.properties.id !== undefined) return; // short-cut - assume if 'id' is set, we're all good.
34
+ props.forEach(p => {
35
+ if (prop === undefined)
36
+ if (f.properties[p] !== undefined)
37
+ prop = p;
38
+ else
39
+ {
40
+ p = p.toLowerCase();
41
+ if (f.properties[p] !== undefined)
42
+ prop = p
43
+ }
44
+ });
21
45
  if (prop)
22
46
  col.features.forEach(f => { f.properties.id = f.properties[prop] });
23
47
  else
@@ -28,6 +52,94 @@ export function geoEnsureID(col: GeoFeatureCollection): void
28
52
  }
29
53
  }
30
54
 
55
+ export function geoNormalizeFeature(f: any, options?: NormalizeOptions): GeoFeature
56
+ {
57
+ options = Util.shallowAssignImmutable({}, options);
58
+
59
+ if (f && f.geometry && f.geometry.type === 'MultiPolygon' && f.geometry.coordinates)
60
+ {
61
+ let multiPoly = f.geometry.coordinates;
62
+
63
+ // Convert degenerate MultiPolygon to Polygon
64
+ if (multiPoly.length == 1)
65
+ {
66
+ f.geometry.type = 'Polygon';
67
+ f.geometry.coordinates = multiPoly[0];
68
+ }
69
+ }
70
+ else if (f && f.geometry && f.geometry.type === 'Point' && f.geometry.coordinates)
71
+ {
72
+ while (Array.isArray(f.geometry.coordinates[0]))
73
+ f.geometry.coordinates = f.geometry.coordinates[0];
74
+ }
75
+ else if (options.checkRewind)
76
+ // Various tools do not guarantee valid GeoJSON winding rules. Verify it since internal processing
77
+ // assumes it is correct.
78
+ Poly.featureRewind(f);
79
+
80
+ return f;
81
+ }
82
+
83
+ function onlyPolygons(col: GeoFeatureCollection): boolean
84
+ {
85
+ if (col && Array.isArray(col.features))
86
+ for (let i = 0; i < col.features.length; i++)
87
+ {
88
+ let f = col.features[i];
89
+ if (f.geometry && f.geometry.type === 'MultiPolygon')
90
+ return false;
91
+ }
92
+
93
+ return true;
94
+ }
95
+
96
+ function mergePolygon(f1: any, f2: any): any
97
+ {
98
+ if (!f1) return f2;
99
+ if (!f2) return f1;
100
+ if (f1.geometry.type !== 'Polygon' && f1.geometry.type !== 'MultiPolygon')
101
+ return f1;
102
+ if (f2.geometry.type !== 'Polygon' && f2.geometry.type !== 'MultiPolygon')
103
+ return f2;
104
+ if (f1.geometry.type === 'Polygon')
105
+ {
106
+ f1.geometry.type = 'MultiPolygon';
107
+ if (f2.geometry.type === 'Polygon')
108
+ f1.geometry.coordinates = [ f1.geometry.coordinates, f2.geometry.coordinates ];
109
+ else
110
+ f1.geometry.coordinates = [ f1.geometry.coordinates, ...f2.geometry.coordinates ];
111
+ }
112
+ else
113
+ {
114
+ if (f2.geometry.type === 'Polygon')
115
+ f1.geometry.coordinates.push(f2.geometry.coordinates);
116
+ else
117
+ f1.geometry.coordinates = [...f1.geometry.coordinates, ...f2.geometry.coordinates];
118
+ }
119
+ return f1;
120
+ }
121
+
122
+ export function geoNormalizeCollection(col: GeoFeatureCollection, options?: NormalizeOptions): GeoFeatureCollection
123
+ {
124
+ options = Util.shallowAssignImmutable(NormalizeAll, options);
125
+
126
+ // Normalize individual features
127
+ if (col && Array.isArray(col.features)) col.features.forEach((f: GeoFeature) => geoNormalizeFeature(f, options));
128
+
129
+ // Ensure ID
130
+ if (options.ensureID)
131
+ geoEnsureID(col, options);
132
+
133
+ // Merge polygons into multi-polygons based on id?
134
+ if (options.ensureID && options.joinPolygons && onlyPolygons(col))
135
+ {
136
+ let map: GeoFeatureMap = {};
137
+ col.features.forEach(f => { let id = f.properties.id; map[id] = mergePolygon(map[id], f) });
138
+ col.features = Object.values(map);
139
+ }
140
+ return col;
141
+ }
142
+
31
143
  export function geoCollectionToMap(col: GeoFeatureCollection): GeoFeatureMap
32
144
  {
33
145
  if (col == null) return null;
package/lib/poly/topo.ts CHANGED
@@ -215,13 +215,14 @@ function intpt(f: any): { x: number, y: number }
215
215
  export interface SimplifyOptions
216
216
  {
217
217
  minArea?: number,
218
+ log?: boolean,
218
219
  }
219
220
 
220
221
  const DefaultSimplifyOptions: SimplifyOptions = { minArea: 500 };
221
222
 
222
- function log(s: string): void
223
+ function log(emitlog: boolean, s: string): void
223
224
  {
224
- console.log(s);
225
+ if (emitlog) console.log(s);
225
226
  }
226
227
 
227
228
  //
@@ -248,10 +249,10 @@ export function topoSimplifyCollection(col: any, options?: SimplifyOptions): any
248
249
  let elapsedTotal = new Util.Elapsed();
249
250
  let elapsed = new Util.Elapsed();
250
251
  let topo = topoFromCollection(col);
251
- log(`topoSimplifyCollection: fromCollection: ${Math.round(elapsed.ms())}ms`);
252
+ log(options.log, `topoSimplifyCollection: fromCollection: ${Math.round(elapsed.ms())}ms`);
252
253
  elapsed.start();
253
254
  topo = TopoSimplify.presimplify(topo, TopoSimplify['sphericalTriangleArea']);
254
- log(`topoSimplifyCollection: presimplify: ${Math.round(elapsed.ms())}ms`);
255
+ log(options.log, `topoSimplifyCollection: presimplify: ${Math.round(elapsed.ms())}ms`);
255
256
  elapsed.start();
256
257
 
257
258
  // Keep iterating on removing simplification from degenerate shapes
@@ -328,19 +329,19 @@ export function topoSimplifyCollection(col: any, options?: SimplifyOptions): any
328
329
  keepTiny.set(f, f);
329
330
  keepArcs(topo, oOld.arcs, 0); // keeps all points to avoid reprocessing these tiny shapes
330
331
  nBad++, nTiny++;
331
- log(`topoSimplifyCollection: ${f.properties.id}: increasing feature fidelity because intpt dist is ${d}`);
332
+ log(options.log, `topoSimplifyCollection: ${f.properties.id}: increasing feature fidelity because intpt dist is ${d}`);
332
333
  }
333
334
  }
334
335
  }
335
336
  }
336
337
  });
337
- log(`topoSimplifyCollection: pass ${nTries}: ${nBad} (${nTiny} tiny) of ${col.features.length} features are degenerate`);
338
+ log(options.log, `topoSimplifyCollection: pass ${nTries}: ${nBad} (${nTiny} tiny) of ${col.features.length} features are degenerate`);
338
339
 
339
340
  // If not making progress, keep more points
340
341
  if (nBad >= nBadLast)
341
342
  {
342
343
  keepweight /= 10;
343
- log(`topoSimplifyCollection: pass ${nTries}: reducing weight limit to ${keepweight}`);
344
+ log(options.log, `topoSimplifyCollection: pass ${nTries}: reducing weight limit to ${keepweight}`);
344
345
  }
345
346
  nBadLast = nBad;
346
347
 
@@ -356,7 +357,7 @@ export function topoSimplifyCollection(col: any, options?: SimplifyOptions): any
356
357
  nTries++;
357
358
  }
358
359
 
359
- log(`topoSimplifyCollection: total elapsed time: ${bigTimeString(elapsedTotal.ms())}`);
360
+ log(options.log, `topoSimplifyCollection: total elapsed time: ${bigTimeString(elapsedTotal.ms())}`);
360
361
 
361
362
  return col;
362
363
  }
package/lib/util/all.ts CHANGED
@@ -3,3 +3,4 @@ export * from './countedhash';
3
3
  export * from './indexedarray';
4
4
  export * from './gradient';
5
5
  export * from './bintrie';
6
+ export * from './bitset';
@@ -0,0 +1,49 @@
1
+ const BitLookup = [ 1, 2, 4, 8, 16, 32, 64, 128 ];
2
+
3
+ export class ListToBitset
4
+ {
5
+ list: string[];
6
+ index: { [s: string]: number };
7
+ size: number;
8
+
9
+ constructor(list: string[])
10
+ {
11
+ this.list = list;
12
+ this.index = {};
13
+ this.size = Math.floor((this.list.length+7)/8);
14
+ this.list.forEach((s: string, i: number) => { this.index[s] = i });
15
+ }
16
+
17
+ toBits(l: string[]): Uint8Array
18
+ {
19
+ let ab = new ArrayBuffer(this.size);
20
+ let u8 = new Uint8Array(ab);
21
+ if (l) l.forEach(s => {
22
+ let n = this.index[s];
23
+ let i = Math.floor(n/8);
24
+ u8[i] |= BitLookup[n % 8];
25
+ });
26
+ return u8;
27
+ }
28
+
29
+ toList(u8: Uint8Array): string[]
30
+ {
31
+ let list: string[] = [];
32
+ for (let i = 0; i < u8.length; i++)
33
+ {
34
+ let u = u8[i];
35
+ if (u)
36
+ {
37
+ if (u & 1) list.push(this.list[i*8+0]);
38
+ if (u & 2) list.push(this.list[i*8+1]);
39
+ if (u & 4) list.push(this.list[i*8+2]);
40
+ if (u & 8) list.push(this.list[i*8+3]);
41
+ if (u & 16) list.push(this.list[i*8+4]);
42
+ if (u & 32) list.push(this.list[i*8+5]);
43
+ if (u & 64) list.push(this.list[i*8+6]);
44
+ if (u & 128) list.push(this.list[i*8+7]);
45
+ }
46
+ }
47
+ return list;
48
+ }
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dra2020/baseclient",
3
- "version": "1.0.49",
3
+ "version": "1.0.52",
4
4
  "description": "Utility functions for Javascript projects.",
5
5
  "main": "dist/baseclient.js",
6
6
  "types": "./dist/all/all.d.ts",