@dra2020/baseclient 1.0.12 → 1.0.13
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/package.json +16 -15
- package/LICENSE +0 -21
- package/README.md +0 -26
- package/dist/all/all.d.ts +0 -20
- package/dist/baseclient.js +0 -10113
- package/dist/baseclient.js.map +0 -1
- package/dist/context/all.d.ts +0 -1
- package/dist/context/context.d.ts +0 -13
- package/dist/filterexpr/all.d.ts +0 -1
- package/dist/filterexpr/filterexpr.d.ts +0 -67
- package/dist/fsm/all.d.ts +0 -1
- package/dist/fsm/fsm.d.ts +0 -119
- package/dist/geo/all.d.ts +0 -2
- package/dist/geo/geo.d.ts +0 -67
- package/dist/geo/vfeature.d.ts +0 -4
- package/dist/logabstract/all.d.ts +0 -1
- package/dist/logabstract/log.d.ts +0 -26
- package/dist/logclient/all.d.ts +0 -1
- package/dist/logclient/log.d.ts +0 -6
- package/dist/ot-editutil/all.d.ts +0 -2
- package/dist/ot-editutil/oteditutil.d.ts +0 -14
- package/dist/ot-editutil/otmaputil.d.ts +0 -21
- package/dist/ot-js/all.d.ts +0 -9
- package/dist/ot-js/otarray.d.ts +0 -111
- package/dist/ot-js/otclientengine.d.ts +0 -38
- package/dist/ot-js/otcomposite.d.ts +0 -37
- package/dist/ot-js/otcounter.d.ts +0 -17
- package/dist/ot-js/otengine.d.ts +0 -22
- package/dist/ot-js/otmap.d.ts +0 -19
- package/dist/ot-js/otserverengine.d.ts +0 -38
- package/dist/ot-js/otsession.d.ts +0 -114
- package/dist/ot-js/ottypes.d.ts +0 -29
- package/dist/poly/all.d.ts +0 -15
- package/dist/poly/blend.d.ts +0 -1
- package/dist/poly/boundbox.d.ts +0 -16
- package/dist/poly/cartesian.d.ts +0 -5
- package/dist/poly/graham-scan.d.ts +0 -8
- package/dist/poly/hash.d.ts +0 -1
- package/dist/poly/matrix.d.ts +0 -24
- package/dist/poly/minbound.d.ts +0 -1
- package/dist/poly/poly.d.ts +0 -52
- package/dist/poly/polybin.d.ts +0 -5
- package/dist/poly/polylabel.d.ts +0 -7
- package/dist/poly/polypack.d.ts +0 -30
- package/dist/poly/polyround.d.ts +0 -1
- package/dist/poly/polysimplify.d.ts +0 -1
- package/dist/poly/quad.d.ts +0 -48
- package/dist/poly/selfintersect.d.ts +0 -1
- package/dist/poly/shamos.d.ts +0 -1
- package/dist/poly/simplify.d.ts +0 -2
- package/dist/poly/topo.d.ts +0 -46
- package/dist/poly/union.d.ts +0 -49
- package/dist/util/all.d.ts +0 -5
- package/dist/util/bintrie.d.ts +0 -93
- package/dist/util/countedhash.d.ts +0 -19
- package/dist/util/gradient.d.ts +0 -15
- package/dist/util/indexedarray.d.ts +0 -15
- package/dist/util/util.d.ts +0 -68
- package/docs/context.md +0 -2
- package/docs/filterexpr.md +0 -22
- package/docs/fsm.md +0 -243
- package/docs/logabstract.md +0 -2
- package/docs/logclient.md +0 -2
- package/docs/ot-editutil.md +0 -2
- package/docs/ot-js.md +0 -95
- package/docs/poly.md +0 -103
- package/docs/util.md +0 -2
- package/lib/all/all.ts +0 -21
- package/lib/context/all.ts +0 -1
- package/lib/context/context.ts +0 -82
- package/lib/filterexpr/all.ts +0 -1
- package/lib/filterexpr/filterexpr.ts +0 -699
- package/lib/fsm/all.ts +0 -1
- package/lib/fsm/fsm.ts +0 -559
- package/lib/geo/all.ts +0 -2
- package/lib/geo/geo.ts +0 -452
- package/lib/geo/vfeature.ts +0 -34
- package/lib/logabstract/all.ts +0 -1
- package/lib/logabstract/log.ts +0 -55
- package/lib/logclient/all.ts +0 -1
- package/lib/logclient/log.ts +0 -105
- package/lib/ot-editutil/all.ts +0 -2
- package/lib/ot-editutil/oteditutil.ts +0 -180
- package/lib/ot-editutil/otmaputil.ts +0 -209
- package/lib/ot-js/all.ts +0 -9
- package/lib/ot-js/otarray.ts +0 -1168
- package/lib/ot-js/otclientengine.ts +0 -327
- package/lib/ot-js/otcomposite.ts +0 -247
- package/lib/ot-js/otcounter.ts +0 -145
- package/lib/ot-js/otengine.ts +0 -71
- package/lib/ot-js/otmap.ts +0 -144
- package/lib/ot-js/otserverengine.ts +0 -329
- package/lib/ot-js/otsession.ts +0 -202
- package/lib/ot-js/ottypes.ts +0 -98
- package/lib/poly/all.ts +0 -15
- package/lib/poly/blend.ts +0 -27
- package/lib/poly/boundbox.ts +0 -102
- package/lib/poly/cartesian.ts +0 -130
- package/lib/poly/graham-scan.ts +0 -401
- package/lib/poly/hash.ts +0 -15
- package/lib/poly/matrix.ts +0 -309
- package/lib/poly/minbound.ts +0 -211
- package/lib/poly/poly.ts +0 -767
- package/lib/poly/polybin.ts +0 -218
- package/lib/poly/polylabel.ts +0 -204
- package/lib/poly/polypack.ts +0 -468
- package/lib/poly/polyround.ts +0 -30
- package/lib/poly/polysimplify.ts +0 -24
- package/lib/poly/quad.ts +0 -272
- package/lib/poly/selfintersect.ts +0 -87
- package/lib/poly/shamos.ts +0 -297
- package/lib/poly/simplify.ts +0 -119
- package/lib/poly/topo.ts +0 -499
- package/lib/poly/union.ts +0 -388
- package/lib/util/all.ts +0 -5
- package/lib/util/bintrie.ts +0 -603
- package/lib/util/countedhash.ts +0 -83
- package/lib/util/gradient.ts +0 -108
- package/lib/util/indexedarray.ts +0 -80
- package/lib/util/util.ts +0 -695
package/lib/poly/simplify.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Adapted from:
|
|
3
|
-
(c) 2017, Vladimir Agafonkin
|
|
4
|
-
Simplify.js, a high-performance JS polyline simplification library
|
|
5
|
-
mourner.github.io/simplify-js
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type Point = [ number, number];
|
|
9
|
-
|
|
10
|
-
// square distance between 2 points
|
|
11
|
-
function getSqDist(p1: Point, p2: Point): number
|
|
12
|
-
{
|
|
13
|
-
var dx = p1[0] - p2[0],
|
|
14
|
-
dy = p1[1] - p2[1];
|
|
15
|
-
|
|
16
|
-
return dx * dx + dy * dy;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// square distance from a point to a segment
|
|
20
|
-
function getSqSegDist(p: Point, p1: Point, p2: Point): number
|
|
21
|
-
{
|
|
22
|
-
var x = p1[0],
|
|
23
|
-
y = p1[1],
|
|
24
|
-
dx = p2[0] - x,
|
|
25
|
-
dy = p2[1] - y;
|
|
26
|
-
|
|
27
|
-
if (dx !== 0 || dy !== 0) {
|
|
28
|
-
|
|
29
|
-
var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
|
|
30
|
-
|
|
31
|
-
if (t > 1) {
|
|
32
|
-
x = p2[0];
|
|
33
|
-
y = p2[1];
|
|
34
|
-
|
|
35
|
-
} else if (t > 0) {
|
|
36
|
-
x += dx * t;
|
|
37
|
-
y += dy * t;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
dx = p[0] - x;
|
|
42
|
-
dy = p[1] - y;
|
|
43
|
-
|
|
44
|
-
return dx * dx + dy * dy;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// rest of the code doesn't care about point format
|
|
49
|
-
|
|
50
|
-
// basic distance-based simplification
|
|
51
|
-
function simplifyRadialDist(points: Point[], sqTolerance: number): Point[]
|
|
52
|
-
{
|
|
53
|
-
let prevPoint: Point = points[0],
|
|
54
|
-
newPoints: Point[] = [prevPoint],
|
|
55
|
-
point: Point;
|
|
56
|
-
|
|
57
|
-
for (let i = 1, len = points.length; i < len; i++) {
|
|
58
|
-
point = points[i];
|
|
59
|
-
|
|
60
|
-
if (getSqDist(point, prevPoint) > sqTolerance) {
|
|
61
|
-
newPoints.push(point);
|
|
62
|
-
prevPoint = point;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (prevPoint !== point) newPoints.push(point);
|
|
67
|
-
|
|
68
|
-
return newPoints;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function simplifyDPStep(points: Point[], first: number, last: number, sqTolerance: number, simplified: Point[]): void
|
|
72
|
-
{
|
|
73
|
-
let maxSqDist: number = sqTolerance,
|
|
74
|
-
index: number;
|
|
75
|
-
|
|
76
|
-
for (let i = first + 1; i < last; i++)
|
|
77
|
-
{
|
|
78
|
-
let sqDist = getSqSegDist(points[i], points[first], points[last]);
|
|
79
|
-
|
|
80
|
-
if (sqDist > maxSqDist)
|
|
81
|
-
{
|
|
82
|
-
index = i;
|
|
83
|
-
maxSqDist = sqDist;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (maxSqDist > sqTolerance)
|
|
88
|
-
{
|
|
89
|
-
if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
|
|
90
|
-
simplified.push(points[index]);
|
|
91
|
-
if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// simplification using Ramer-Douglas-Peucker algorithm
|
|
96
|
-
function simplifyDouglasPeucker(points: Point[], sqTolerance: number): Point[]
|
|
97
|
-
{
|
|
98
|
-
let last: number = points.length - 1;
|
|
99
|
-
|
|
100
|
-
let simplified: Point[] = [points[0]];
|
|
101
|
-
simplifyDPStep(points, 0, last, sqTolerance, simplified);
|
|
102
|
-
simplified.push(points[last]);
|
|
103
|
-
|
|
104
|
-
return simplified;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// both algorithms combined for awesome performance
|
|
108
|
-
export function simplify(points: Point[], tolerance: number = 1, highestQuality: boolean = false): Point[]
|
|
109
|
-
{
|
|
110
|
-
if (tolerance == 0 || points.length <= 2)
|
|
111
|
-
return points;
|
|
112
|
-
|
|
113
|
-
let sqTolerance: number = tolerance * tolerance;
|
|
114
|
-
|
|
115
|
-
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
|
|
116
|
-
points = simplifyDouglasPeucker(points, sqTolerance);
|
|
117
|
-
|
|
118
|
-
return points;
|
|
119
|
-
}
|
package/lib/poly/topo.ts
DELETED
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Forked version that supports packing
|
|
3
|
-
import * as TopoClient from '@dra2020/topojson-client';
|
|
4
|
-
// Forked version that fixes self-looping hole problem
|
|
5
|
-
import * as TopoServer from '@dra2020/topojson-server';
|
|
6
|
-
// Forked version that fixes performance problem
|
|
7
|
-
import * as TopoSimplify from '@dra2020/topojson-simplify';
|
|
8
|
-
|
|
9
|
-
import * as Util from '../util/all';
|
|
10
|
-
import * as FSM from '../fsm/all';
|
|
11
|
-
|
|
12
|
-
import * as P from './poly';
|
|
13
|
-
import * as Q from './quad';
|
|
14
|
-
import * as PP from './polypack';
|
|
15
|
-
import * as PL from './polylabel';
|
|
16
|
-
import { selfIntersectFast } from './shamos';
|
|
17
|
-
|
|
18
|
-
export type Topo = any;
|
|
19
|
-
|
|
20
|
-
function getGEOID(f: any): string
|
|
21
|
-
{
|
|
22
|
-
if (f.features && f.features.length) f = f.features[0];
|
|
23
|
-
else if (Array.isArray(f)) f = f[0];
|
|
24
|
-
if (f.properties.id !== undefined) return 'id';
|
|
25
|
-
if (f.properties.GEOID !== undefined) return 'GEOID';
|
|
26
|
-
if (f.properties.GEOID10 !== undefined) return 'GEOID10';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function topoFromCollection(col: any): Topo
|
|
30
|
-
{
|
|
31
|
-
if (col == null) return null;
|
|
32
|
-
let save = PP.featureUnpackTemporarily(col);
|
|
33
|
-
let prop = getGEOID(col);
|
|
34
|
-
let objects: any = {};
|
|
35
|
-
col.features.forEach((f: any) => objects[f.properties[prop]] = f);
|
|
36
|
-
let topo = TopoServer.topology(objects);
|
|
37
|
-
PP.featureRepack(col, save);
|
|
38
|
-
if (col.datasets)
|
|
39
|
-
(topo as any).datasets = col.datasets;
|
|
40
|
-
return topo;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function ringsCancel(outerPoly: any, innerRing: any): boolean
|
|
44
|
-
{
|
|
45
|
-
if (outerPoly.length != 1) return false;
|
|
46
|
-
let outerRing = outerPoly[0];
|
|
47
|
-
if (outerRing.length !== innerRing.length) return false;
|
|
48
|
-
let n = outerRing.length-1;
|
|
49
|
-
let i = 0;
|
|
50
|
-
for (; i <= n; i++, n--)
|
|
51
|
-
if (! Util.shallowEqual(outerRing[i], innerRing[n]))
|
|
52
|
-
return false;
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function correctGeometry(f: any): any
|
|
57
|
-
{
|
|
58
|
-
if (f && f.geometry && f.geometry.type === 'MultiPolygon' && f.geometry.coordinates)
|
|
59
|
-
{
|
|
60
|
-
let multiPoly = f.geometry.coordinates;
|
|
61
|
-
|
|
62
|
-
// Convert degenerate MultiPolygon to Polygon
|
|
63
|
-
if (multiPoly.length == 1)
|
|
64
|
-
{
|
|
65
|
-
f.geometry.type = 'Polygon';
|
|
66
|
-
f.geometry.coordinates = multiPoly[0];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// TopoJSON does not guarantee proper winding order which messes up later processing. Fix it.
|
|
71
|
-
P.featureRewind(f);
|
|
72
|
-
|
|
73
|
-
return f;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function topoContiguity(topo: Topo): any
|
|
77
|
-
{
|
|
78
|
-
let objects: any[] = Object.values(topo.objects);
|
|
79
|
-
let geoid = getGEOID(objects);
|
|
80
|
-
objects.forEach((o: any) => { o.properties.id = o.properties[geoid] });
|
|
81
|
-
let neighbors = TopoClient.neighbors(objects, true);
|
|
82
|
-
let result: any = {};
|
|
83
|
-
result['OUT_OF_STATE'] = [];
|
|
84
|
-
objects.forEach((o: any, i: number) => {
|
|
85
|
-
result[o.properties.id] = neighbors[i].map((j: any) => {
|
|
86
|
-
if (j >= 0)
|
|
87
|
-
return objects[j].properties.id;
|
|
88
|
-
else
|
|
89
|
-
{
|
|
90
|
-
result['OUT_OF_STATE'].push(o.properties.id);
|
|
91
|
-
return 'OUT_OF_STATE';
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function topoToFeature(topo: Topo, geoid: string): any
|
|
99
|
-
{
|
|
100
|
-
return correctGeometry(TopoClient.feature(topo, topo.objects[geoid]));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function topoToCollection(topo: Topo): any
|
|
104
|
-
{
|
|
105
|
-
let col: any = { type: 'FeatureCollection', features: [] };
|
|
106
|
-
Object.keys(topo.objects).forEach((geoid: string) => {
|
|
107
|
-
col.features.push(topoToFeature(topo, geoid));
|
|
108
|
-
});
|
|
109
|
-
if (topo.datasets) col.datasets = topo.datasets;
|
|
110
|
-
return col;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function keepArcs(topo: any, arcs: any, keepweight: number): void
|
|
114
|
-
{
|
|
115
|
-
arcs.forEach((a: any) => {
|
|
116
|
-
if (Array.isArray(a))
|
|
117
|
-
keepArcs(topo, a, keepweight);
|
|
118
|
-
else
|
|
119
|
-
{
|
|
120
|
-
let arc = topo.arcs[a < 0 ? ~a : a];
|
|
121
|
-
arc.forEach((pt: any) => {
|
|
122
|
-
if (pt[2] >= keepweight)
|
|
123
|
-
pt[2] = Infinity;
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function fullFidelity(topo: any, arcs: any): boolean
|
|
130
|
-
{
|
|
131
|
-
let bFull = true;
|
|
132
|
-
|
|
133
|
-
arcs.forEach((a: any) => {
|
|
134
|
-
if (Array.isArray(a))
|
|
135
|
-
bFull = bFull && fullFidelity(topo, a);
|
|
136
|
-
else
|
|
137
|
-
{
|
|
138
|
-
let arc = topo.arcs[a < 0 ? ~a : a];
|
|
139
|
-
arc.forEach((pt: any) => {
|
|
140
|
-
if (bFull && pt[2] !== Infinity)
|
|
141
|
-
bFull = false;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
return bFull;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function misMatchPoly(p1: any, p2: any): boolean
|
|
149
|
-
{
|
|
150
|
-
if (p1 == null || p2 == null || p1.length != p2.length) return true;
|
|
151
|
-
for (let i = 0; i < p1.length; i++)
|
|
152
|
-
if (p1[i] == null || p2[i] == null) return true;
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function misMatchMulti(m1: any, m2: any): boolean
|
|
157
|
-
{
|
|
158
|
-
if (m1 == null || m2 == null || m1.length != m2.length) return true;
|
|
159
|
-
for (let i = 0; i < m1.length; i++)
|
|
160
|
-
if (misMatchPoly(m1[i], m2[i])) return true;
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function misMatchObject(o1: any, o2: any): boolean
|
|
165
|
-
{
|
|
166
|
-
if (o1 == null || o2 == null || o1.type !== o2.type) return true;
|
|
167
|
-
return (o1.type === 'MultiPolygon') ? misMatchMulti(o1.arcs, o2.arcs) : misMatchPoly(o1.arcs, o2.arcs);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const MAX_TRIES = 25;
|
|
171
|
-
|
|
172
|
-
function bigTimeString(ms: number): string
|
|
173
|
-
{
|
|
174
|
-
let seconds = Math.trunc(ms / 1000);
|
|
175
|
-
let minutes = Math.trunc(seconds / 60);
|
|
176
|
-
seconds -= minutes * 60;
|
|
177
|
-
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export interface SimplifyOptions
|
|
181
|
-
{
|
|
182
|
-
minArea?: number,
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const DefaultSimplifyOptions: SimplifyOptions = { minArea: 500 };
|
|
186
|
-
|
|
187
|
-
function log(s: string): void
|
|
188
|
-
{
|
|
189
|
-
//console.log(s);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
// topoSimplifyCollection:
|
|
194
|
-
// This implements our simplification strategy for block/precinct level shapes. The basic idea is to
|
|
195
|
-
// ensure that all shapes generated at the block level have non-zero area and do not introduce self-crossing
|
|
196
|
-
// edges. Since topological simplification happens at the edge level, self-crossing is a risk when opposite
|
|
197
|
-
// edges make simplification decisions that cause the edges to cross. Crossing edges result in visual anomalies.
|
|
198
|
-
// In addition, our basic strategy is that if we have well-formed block shapes, all other shape processing
|
|
199
|
-
// (virtual features, precincts, counties, districts) can be done by fast merges that generate well-formed
|
|
200
|
-
// shapes without holes, overlaps or crossing edges.
|
|
201
|
-
// The strategy here is to first just do topological simplification at the default simplification level. We then
|
|
202
|
-
// scan for problematic shapes (zero area, too thin and narrow or self-crossing) and keep additional points on
|
|
203
|
-
// the edges (arcs) for those shapes, adding additional points on each iteration step until we have a good set.
|
|
204
|
-
// For tiny shapes (zero area or short and thin) we just immediately say keep all points since these are mostly
|
|
205
|
-
// shapes with a small number of points anyway.
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
export function topoSimplifyCollection(col: any, options?: SimplifyOptions): any
|
|
209
|
-
{
|
|
210
|
-
options = Util.shallowAssignImmutable(DefaultSimplifyOptions, options);
|
|
211
|
-
|
|
212
|
-
if (col == null) return null;
|
|
213
|
-
let elapsedTotal = new Util.Elapsed();
|
|
214
|
-
let elapsed = new Util.Elapsed();
|
|
215
|
-
let topo = topoFromCollection(col);
|
|
216
|
-
log(`topoSimplifyCollection: fromCollection: ${Math.round(elapsed.ms())}ms`);
|
|
217
|
-
elapsed.start();
|
|
218
|
-
topo = TopoSimplify.presimplify(topo, TopoSimplify['sphericalTriangleArea']);
|
|
219
|
-
log(`topoSimplifyCollection: presimplify: ${Math.round(elapsed.ms())}ms`);
|
|
220
|
-
elapsed.start();
|
|
221
|
-
|
|
222
|
-
// Keep iterating on removing simplification from degenerate shapes
|
|
223
|
-
let nTries = 1;
|
|
224
|
-
let nBadLast = Number.MAX_VALUE;
|
|
225
|
-
let keepweight = 1E-11;
|
|
226
|
-
let nLabelTolerance = 1E-4;
|
|
227
|
-
let minArea = options.minArea;
|
|
228
|
-
let keepTiny = new WeakMap<any, any>();
|
|
229
|
-
while (true)
|
|
230
|
-
{
|
|
231
|
-
let testtopo = TopoSimplify.simplify(topo, 1E-10);
|
|
232
|
-
elapsed.start();
|
|
233
|
-
let nBad = 0;
|
|
234
|
-
let nTiny = 0;
|
|
235
|
-
col.features.forEach((f: any) => {
|
|
236
|
-
let oOld: any = topo.objects[f.properties.id];
|
|
237
|
-
let oNew: any = testtopo.objects[f.properties.id];
|
|
238
|
-
if (! keepTiny.has(f))
|
|
239
|
-
{
|
|
240
|
-
// Walk through each polygon of a multipolygon separately since I may have a large non-degenerate
|
|
241
|
-
// shape combined with degenerate smaller shapes that I want to identify. I do not examine holes
|
|
242
|
-
// separately under the assumption that holes will have matching polygons that would be identified
|
|
243
|
-
// and would cause those shared edges (between the hole and the filling polygon) to be preserved if necessary.
|
|
244
|
-
//
|
|
245
|
-
// I do make a final pass of testing for self-intersection that looks at both polygon and hole edges.
|
|
246
|
-
//
|
|
247
|
-
let arcs = PP.normalizeArcs(oNew.arcs);
|
|
248
|
-
let npoly = PP.countArcPolygons(arcs);
|
|
249
|
-
let bDecided = false;
|
|
250
|
-
let iPoly = 0;
|
|
251
|
-
for (; !bDecided && iPoly < npoly; iPoly++)
|
|
252
|
-
{
|
|
253
|
-
let pp = PP.polyPackTopoArcs(testtopo, arcs, iPoly);
|
|
254
|
-
P.polyRewindRings(pp);
|
|
255
|
-
let a = P.polyArea(pp);
|
|
256
|
-
if (a <= 0)
|
|
257
|
-
{
|
|
258
|
-
keepTiny.set(f, f);
|
|
259
|
-
keepArcs(topo, oOld.arcs, 0); // keeps all points to avoid reprocessing these tiny shapes
|
|
260
|
-
nBad++, nTiny++, bDecided = true;
|
|
261
|
-
}
|
|
262
|
-
else if (a < minArea)
|
|
263
|
-
{
|
|
264
|
-
let d = PL.polyLabel(pp);
|
|
265
|
-
if (d.d < nLabelTolerance)
|
|
266
|
-
{
|
|
267
|
-
keepTiny.set(f, f);
|
|
268
|
-
keepArcs(topo, oOld.arcs, 0); // keeps all points to avoid reprocessing these tiny shapes
|
|
269
|
-
nBad++, nTiny++, bDecided = true;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (! bDecided)
|
|
274
|
-
{
|
|
275
|
-
let pp = PP.polyPackTopoArcs(testtopo, arcs);
|
|
276
|
-
if (selfIntersectFast(pp))
|
|
277
|
-
{
|
|
278
|
-
keepArcs(topo, oOld.arcs, keepweight);
|
|
279
|
-
nBad++;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
log(`topoSimplifyCollection: pass ${nTries}: ${nBad} (${nTiny} tiny) of ${col.features.length} features are degenerate`);
|
|
285
|
-
|
|
286
|
-
// If not making progress, keep more points
|
|
287
|
-
if (nBad >= nBadLast)
|
|
288
|
-
{
|
|
289
|
-
keepweight /= 10;
|
|
290
|
-
log(`topoSimplifyCollection: pass ${nTries}: reducing weight limit to ${keepweight}`);
|
|
291
|
-
}
|
|
292
|
-
nBadLast = nBad;
|
|
293
|
-
|
|
294
|
-
if (nBad && nTries > MAX_TRIES)
|
|
295
|
-
console.error(`topoSimplifyCollection: failed to finalize simplify down to zero degenerate features`);
|
|
296
|
-
// If no bad block shapes, or finished trying, just return result
|
|
297
|
-
if (nBad == 0 || nTries > MAX_TRIES)
|
|
298
|
-
{
|
|
299
|
-
col = topoToCollection(testtopo);
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
nTries++;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
log(`topoSimplifyCollection: total elapsed time: ${bigTimeString(elapsedTotal.ms())}`);
|
|
307
|
-
|
|
308
|
-
return col;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
export function topoMerge(topo: Topo, geoids: string[]): any
|
|
312
|
-
{
|
|
313
|
-
if (geoids == null || geoids.length == 0) return null;
|
|
314
|
-
let objects: any[] = [];
|
|
315
|
-
geoids.forEach((geoid) => objects.push(topo.objects[geoid]));
|
|
316
|
-
return correctGeometry({ type: 'Feature', properties: {}, geometry: TopoClient.merge(topo, objects) });
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export function topoMergeFeatures(topo: Topo, features: any[]): any
|
|
320
|
-
{
|
|
321
|
-
if (features == null || features.length == 0) return null;
|
|
322
|
-
let prop = getGEOID(features);
|
|
323
|
-
return topoMerge(topo, features.map(f => f.properties[prop]));
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
let UniqueState = FSM.FSM_CUSTOM1;
|
|
327
|
-
let FSM_COMPUTING = UniqueState++;
|
|
328
|
-
|
|
329
|
-
class FsmIncrementalUnion extends FSM.Fsm
|
|
330
|
-
{
|
|
331
|
-
options: P.TickOptions;
|
|
332
|
-
key: any;
|
|
333
|
-
map: any; // { [geoid: string]: Feature }
|
|
334
|
-
result: any;
|
|
335
|
-
work: Q.WorkDone;
|
|
336
|
-
|
|
337
|
-
constructor(env: FSM.FsmEnvironment, options: P.TickOptions, key: any, map?: any)
|
|
338
|
-
{
|
|
339
|
-
super(env);
|
|
340
|
-
this.options = options;
|
|
341
|
-
this.key = key;
|
|
342
|
-
this.result = null;
|
|
343
|
-
this.map = null;
|
|
344
|
-
if (map) this.recompute(map);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
matches(key: any): boolean
|
|
348
|
-
{
|
|
349
|
-
return Util.shallowEqual(this.key, key);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
recompute(map: any): void
|
|
353
|
-
{
|
|
354
|
-
if (this.map != null && map != null && Util.shallowEqual(map, this.map))
|
|
355
|
-
{
|
|
356
|
-
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
357
|
-
}
|
|
358
|
-
else if (map == null || Util.isEmpty(map))
|
|
359
|
-
{
|
|
360
|
-
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
361
|
-
this.result = null;
|
|
362
|
-
this.map = map;
|
|
363
|
-
}
|
|
364
|
-
else
|
|
365
|
-
{
|
|
366
|
-
let values = Object.values(map);
|
|
367
|
-
this.work = { nUnion: values.length, nDifference: 0, ms: 0 };
|
|
368
|
-
let elapsed = new Util.Elapsed();
|
|
369
|
-
this.result = topoMergeFeatures(this.key.multi.allTopo(), values);
|
|
370
|
-
this.work.ms = elapsed.ms();
|
|
371
|
-
this.map = map;
|
|
372
|
-
}
|
|
373
|
-
this.setState(FSM.FSM_DONE);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
cancel(): void
|
|
377
|
-
{
|
|
378
|
-
this.result = null;
|
|
379
|
-
this.map = null;
|
|
380
|
-
this.setState(FSM.FSM_DONE);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
tick(): void
|
|
384
|
-
{
|
|
385
|
-
if (this.ready)
|
|
386
|
-
{
|
|
387
|
-
switch (this.state)
|
|
388
|
-
{
|
|
389
|
-
case FSM.FSM_STARTING:
|
|
390
|
-
// never initialized to do work (see recompute())
|
|
391
|
-
this.setState(FSM.FSM_DONE);
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
export interface TopoUnionResult
|
|
399
|
-
{
|
|
400
|
-
key: any;
|
|
401
|
-
poly: any;
|
|
402
|
-
work: Q.WorkDone;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
export class FsmTopoUnion extends FSM.Fsm
|
|
406
|
-
{
|
|
407
|
-
options: P.TickOptions;
|
|
408
|
-
unions: FsmIncrementalUnion[];
|
|
409
|
-
work: Q.WorkDone;
|
|
410
|
-
|
|
411
|
-
constructor(env: FSM.FsmEnvironment, options?: P.TickOptions)
|
|
412
|
-
{
|
|
413
|
-
super(env);
|
|
414
|
-
this.options = Util.shallowAssignImmutable(P.DefaultTickOptions, options);
|
|
415
|
-
this.unions = [];
|
|
416
|
-
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
get result(): TopoUnionResult[]
|
|
420
|
-
{
|
|
421
|
-
if (this.unions.length > 0 && this.state === FSM.FSM_DONE)
|
|
422
|
-
return this.unions.map((i: FsmIncrementalUnion) => ({ key: i.key, poly: i.result, work: i.work }) );
|
|
423
|
-
else
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
cancel(): void
|
|
428
|
-
{
|
|
429
|
-
this.unions.forEach((i: FsmIncrementalUnion) => {
|
|
430
|
-
i.cancel();
|
|
431
|
-
});
|
|
432
|
-
this.unions = [];
|
|
433
|
-
this.setState(FSM.FSM_DONE);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
cancelOne(key: any): void
|
|
437
|
-
{
|
|
438
|
-
for (let i = 0; i < this.unions.length; i++)
|
|
439
|
-
{
|
|
440
|
-
let u = this.unions[i];
|
|
441
|
-
if (u.matches(key))
|
|
442
|
-
{
|
|
443
|
-
u.cancel();
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
recompute(key: any, map: any): void
|
|
450
|
-
{
|
|
451
|
-
let fsm: FsmIncrementalUnion = this.unions.find((i: FsmIncrementalUnion) => i.matches(key));
|
|
452
|
-
if (fsm == null)
|
|
453
|
-
{
|
|
454
|
-
fsm = new FsmIncrementalUnion(this.env, this.options, key, map);
|
|
455
|
-
this.unions.push(fsm);
|
|
456
|
-
}
|
|
457
|
-
else
|
|
458
|
-
fsm.recompute(map);
|
|
459
|
-
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
460
|
-
this.unions.forEach((u) => { this.work.nUnion += u.work.nUnion; this.work.nDifference += u.work.nDifference });
|
|
461
|
-
this.waitOn(fsm);
|
|
462
|
-
this.setState(FSM_COMPUTING);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
tick(): void
|
|
466
|
-
{
|
|
467
|
-
if (this.ready)
|
|
468
|
-
{
|
|
469
|
-
switch (this.state)
|
|
470
|
-
{
|
|
471
|
-
case FSM.FSM_STARTING:
|
|
472
|
-
case FSM_COMPUTING:
|
|
473
|
-
if (this.unions) this.unions.forEach((u) => { this.work.ms += u.work.ms });
|
|
474
|
-
this.setState(FSM.FSM_DONE);
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export function topoPacked(topo: any): boolean
|
|
482
|
-
{
|
|
483
|
-
return topo.packed !== undefined;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export function topoPack(topo: any): any
|
|
487
|
-
{
|
|
488
|
-
let tc = TopoClient;
|
|
489
|
-
TopoClient.packArcs(topo);
|
|
490
|
-
TopoClient.packArcIndices(topo);
|
|
491
|
-
return topo;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
export function topoUnpack(topo: any): any
|
|
495
|
-
{
|
|
496
|
-
TopoClient.unpackArcs(topo);
|
|
497
|
-
TopoClient.unpackArcIndices(topo);
|
|
498
|
-
return topo;
|
|
499
|
-
}
|