@dra2020/baseclient 1.0.13 → 1.0.14

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 (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/all/all.d.ts +36 -0
  4. package/dist/all/allclient.d.ts +18 -0
  5. package/dist/base.js +33010 -0
  6. package/dist/base.js.map +1 -0
  7. package/dist/baseclient.js +8991 -0
  8. package/dist/baseclient.js.map +1 -0
  9. package/dist/context/all.d.ts +1 -0
  10. package/dist/context/context.d.ts +13 -0
  11. package/dist/dbabstract/all.d.ts +1 -0
  12. package/dist/dbabstract/db.d.ts +83 -0
  13. package/dist/dbdynamo/all.d.ts +1 -0
  14. package/dist/dbdynamo/dbdynamo.d.ts +190 -0
  15. package/dist/filterexpr/all.d.ts +1 -0
  16. package/dist/filterexpr/filterexpr.d.ts +64 -0
  17. package/dist/fsm/all.d.ts +1 -0
  18. package/dist/fsm/fsm.d.ts +118 -0
  19. package/dist/fsmfile/all.d.ts +1 -0
  20. package/dist/fsmfile/fsmfile.d.ts +47 -0
  21. package/dist/jsonstream/all.d.ts +1 -0
  22. package/dist/jsonstream/jsonstream.d.ts +130 -0
  23. package/dist/lambda/all.d.ts +1 -0
  24. package/dist/lambda/env.d.ts +10 -0
  25. package/dist/lambda/lambda.d.ts +18 -0
  26. package/dist/logabstract/all.d.ts +1 -0
  27. package/dist/logabstract/log.d.ts +26 -0
  28. package/dist/logclient/all.d.ts +1 -0
  29. package/dist/logclient/log.d.ts +6 -0
  30. package/dist/logserver/all.d.ts +5 -0
  31. package/dist/logserver/log.d.ts +11 -0
  32. package/dist/logserver/logaccum.d.ts +154 -0
  33. package/dist/logserver/logblob.d.ts +24 -0
  34. package/dist/logserver/logconcat.d.ts +55 -0
  35. package/dist/logserver/logkey.d.ts +28 -0
  36. package/dist/memsqs/all.d.ts +4 -0
  37. package/dist/memsqs/client.d.ts +13 -0
  38. package/dist/memsqs/loopback.d.ts +11 -0
  39. package/dist/memsqs/orderedlist.d.ts +19 -0
  40. package/dist/memsqs/queue.d.ts +84 -0
  41. package/dist/memsqs/server.d.ts +37 -0
  42. package/dist/ot-editutil/all.d.ts +2 -0
  43. package/dist/ot-editutil/oteditutil.d.ts +14 -0
  44. package/dist/ot-editutil/otmaputil.d.ts +21 -0
  45. package/dist/ot-js/all.d.ts +9 -0
  46. package/dist/ot-js/otarray.d.ts +111 -0
  47. package/dist/ot-js/otclientengine.d.ts +38 -0
  48. package/dist/ot-js/otcomposite.d.ts +37 -0
  49. package/dist/ot-js/otcounter.d.ts +17 -0
  50. package/dist/ot-js/otengine.d.ts +22 -0
  51. package/dist/ot-js/otmap.d.ts +19 -0
  52. package/dist/ot-js/otserverengine.d.ts +38 -0
  53. package/dist/ot-js/otsession.d.ts +111 -0
  54. package/dist/ot-js/ottypes.d.ts +29 -0
  55. package/dist/poly/all.d.ts +15 -0
  56. package/dist/poly/blend.d.ts +1 -0
  57. package/dist/poly/boundbox.d.ts +16 -0
  58. package/dist/poly/cartesian.d.ts +5 -0
  59. package/dist/poly/graham-scan.d.ts +8 -0
  60. package/dist/poly/hash.d.ts +1 -0
  61. package/dist/poly/matrix.d.ts +24 -0
  62. package/dist/poly/minbound.d.ts +1 -0
  63. package/dist/poly/poly.d.ts +52 -0
  64. package/dist/poly/polybin.d.ts +5 -0
  65. package/dist/poly/polylabel.d.ts +7 -0
  66. package/dist/poly/polypack.d.ts +30 -0
  67. package/dist/poly/polyround.d.ts +1 -0
  68. package/dist/poly/polysimplify.d.ts +1 -0
  69. package/dist/poly/quad.d.ts +48 -0
  70. package/dist/poly/selfintersect.d.ts +1 -0
  71. package/dist/poly/shamos.d.ts +1 -0
  72. package/dist/poly/simplify.d.ts +2 -0
  73. package/dist/poly/topo.d.ts +46 -0
  74. package/dist/poly/union.d.ts +48 -0
  75. package/dist/storage/all.d.ts +4 -0
  76. package/dist/storage/datablob.d.ts +9 -0
  77. package/dist/storage/env.d.ts +10 -0
  78. package/dist/storage/splitsblob.d.ts +13 -0
  79. package/dist/storage/storage.d.ts +166 -0
  80. package/dist/storages3/all.d.ts +1 -0
  81. package/dist/storages3/s3.d.ts +62 -0
  82. package/dist/util/all.d.ts +5 -0
  83. package/dist/util/bintrie.d.ts +93 -0
  84. package/dist/util/countedhash.d.ts +19 -0
  85. package/dist/util/gradient.d.ts +15 -0
  86. package/dist/util/indexedarray.d.ts +15 -0
  87. package/dist/util/util.d.ts +68 -0
  88. package/docs/context.md +2 -0
  89. package/docs/dbabstract.md +2 -0
  90. package/docs/dbdynamo.md +2 -0
  91. package/docs/fsm.md +243 -0
  92. package/docs/fsmfile.md +2 -0
  93. package/docs/jsonstream.md +44 -0
  94. package/docs/lambda.md +2 -0
  95. package/docs/logabstract.md +2 -0
  96. package/docs/logclient.md +2 -0
  97. package/docs/logserver.md +2 -0
  98. package/docs/ot-editutil.md +2 -0
  99. package/docs/ot-js.md +95 -0
  100. package/docs/poly.md +103 -0
  101. package/docs/storage.md +2 -0
  102. package/docs/storages3.md +2 -0
  103. package/docs/util.md +2 -0
  104. package/lib/all/all.ts +41 -0
  105. package/lib/all/allclient.ts +19 -0
  106. package/lib/context/all.ts +1 -0
  107. package/lib/context/context.ts +82 -0
  108. package/lib/dbabstract/all.ts +1 -0
  109. package/lib/dbabstract/db.ts +246 -0
  110. package/lib/dbdynamo/all.ts +1 -0
  111. package/lib/dbdynamo/dbdynamo.ts +1551 -0
  112. package/lib/filterexpr/all.ts +1 -0
  113. package/lib/filterexpr/filterexpr.ts +625 -0
  114. package/lib/fsm/all.ts +1 -0
  115. package/lib/fsm/fsm.ts +549 -0
  116. package/lib/fsmfile/all.ts +1 -0
  117. package/lib/fsmfile/fsmfile.ts +236 -0
  118. package/lib/jsonstream/all.ts +1 -0
  119. package/lib/jsonstream/jsonstream.ts +940 -0
  120. package/lib/lambda/all.ts +1 -0
  121. package/lib/lambda/env.ts +13 -0
  122. package/lib/lambda/lambda.ts +120 -0
  123. package/lib/logabstract/all.ts +1 -0
  124. package/lib/logabstract/log.ts +55 -0
  125. package/lib/logclient/all.ts +1 -0
  126. package/lib/logclient/log.ts +105 -0
  127. package/lib/logserver/all.ts +5 -0
  128. package/lib/logserver/log.ts +565 -0
  129. package/lib/logserver/logaccum.ts +1445 -0
  130. package/lib/logserver/logblob.ts +84 -0
  131. package/lib/logserver/logconcat.ts +313 -0
  132. package/lib/logserver/logkey.ts +125 -0
  133. package/lib/memsqs/all.ts +4 -0
  134. package/lib/memsqs/client.ts +268 -0
  135. package/lib/memsqs/loopback.ts +64 -0
  136. package/lib/memsqs/orderedlist.ts +74 -0
  137. package/lib/memsqs/queue.ts +395 -0
  138. package/lib/memsqs/server.ts +262 -0
  139. package/lib/ot-editutil/all.ts +2 -0
  140. package/lib/ot-editutil/oteditutil.ts +180 -0
  141. package/lib/ot-editutil/otmaputil.ts +209 -0
  142. package/lib/ot-js/all.ts +9 -0
  143. package/lib/ot-js/otarray.ts +1168 -0
  144. package/lib/ot-js/otclientengine.ts +327 -0
  145. package/lib/ot-js/otcomposite.ts +247 -0
  146. package/lib/ot-js/otcounter.ts +145 -0
  147. package/lib/ot-js/otengine.ts +71 -0
  148. package/lib/ot-js/otmap.ts +144 -0
  149. package/lib/ot-js/otserverengine.ts +329 -0
  150. package/lib/ot-js/otsession.ts +199 -0
  151. package/lib/ot-js/ottypes.ts +98 -0
  152. package/lib/poly/all.ts +15 -0
  153. package/lib/poly/blend.ts +27 -0
  154. package/lib/poly/boundbox.ts +102 -0
  155. package/lib/poly/cartesian.ts +130 -0
  156. package/lib/poly/graham-scan.ts +401 -0
  157. package/lib/poly/hash.ts +15 -0
  158. package/lib/poly/matrix.ts +309 -0
  159. package/lib/poly/minbound.ts +211 -0
  160. package/lib/poly/poly.ts +767 -0
  161. package/lib/poly/polybin.ts +218 -0
  162. package/lib/poly/polylabel.ts +204 -0
  163. package/lib/poly/polypack.ts +458 -0
  164. package/lib/poly/polyround.ts +30 -0
  165. package/lib/poly/polysimplify.ts +24 -0
  166. package/lib/poly/quad.ts +272 -0
  167. package/lib/poly/selfintersect.ts +87 -0
  168. package/lib/poly/shamos.ts +297 -0
  169. package/lib/poly/simplify.ts +119 -0
  170. package/lib/poly/topo.ts +525 -0
  171. package/lib/poly/union.ts +371 -0
  172. package/lib/storage/all.ts +4 -0
  173. package/lib/storage/datablob.ts +36 -0
  174. package/lib/storage/env.ts +14 -0
  175. package/lib/storage/splitsblob.ts +63 -0
  176. package/lib/storage/storage.ts +604 -0
  177. package/lib/storages3/all.ts +1 -0
  178. package/lib/storages3/s3.ts +576 -0
  179. package/lib/util/all.ts +5 -0
  180. package/lib/util/bintrie.ts +603 -0
  181. package/lib/util/countedhash.ts +83 -0
  182. package/lib/util/gradient.ts +108 -0
  183. package/lib/util/indexedarray.ts +80 -0
  184. package/lib/util/util.ts +695 -0
  185. package/package.json +8 -8
@@ -0,0 +1,767 @@
1
+ import * as Util from '../util/all';
2
+
3
+ import * as PP from './polypack';
4
+ import { makeConvexHullGrahamScan } from './graham-scan';
5
+
6
+ // For incremental poly interface
7
+ export interface TickOptions
8
+ {
9
+ maxLeafCount?: number;
10
+ maxDepth?: number;
11
+ tickStep?: number;
12
+ }
13
+
14
+ export const DefaultTickOptions = { maxLeafCount: 256, maxDepth: 20, tickStep: 1 };
15
+
16
+
17
+ // Internal utilities
18
+
19
+ export const EARTH_RADIUS = 6371000; // Radius of earth in meters
20
+
21
+ export interface PolyOptions
22
+ {
23
+ noLatitudeCorrection?: boolean;
24
+ }
25
+ export const DefaultOptions: PolyOptions = { };
26
+
27
+ // Return geographic polygon area in meters^2
28
+ export function polySimpleArea(b: Float64Array, iOffset: number, n: number): number
29
+ {
30
+ let p1x: number;
31
+ let p1y: number;
32
+ let p2x: number;
33
+ let p2y: number;
34
+ let p3x: number;
35
+ let p3y: number;
36
+ let dx: number;
37
+ let l: number;
38
+ let m: number;
39
+ let u: number;
40
+ let i: number;
41
+ let total: number = 0;
42
+ if (n > 2)
43
+ {
44
+ for (i = 0; i < n; i++)
45
+ {
46
+ if (i === n - 2)
47
+ {
48
+ l = n - 2;
49
+ m = n - 1;
50
+ u = 0;
51
+ }
52
+ else if (i === n - 1)
53
+ {
54
+ l = n - 1;
55
+ m = 0;
56
+ u = 1;
57
+ }
58
+ else {
59
+ l = i;
60
+ m = i + 1;
61
+ u = i + 2;
62
+ }
63
+ p1x = b[iOffset+l*2];
64
+ p1y = b[iOffset+l*2+1];
65
+ p2x = b[iOffset+m*2];
66
+ p2y = b[iOffset+m*2+1];
67
+ p3x = b[iOffset+u*2];
68
+ p3y = b[iOffset+u*2+1];
69
+ dx = (Util.deg2rad(p3x) - Util.deg2rad(p1x));
70
+ dx *= Math.sin(Util.deg2rad(p2y));
71
+ total += dx;
72
+ }
73
+ total *= EARTH_RADIUS * EARTH_RADIUS / 2;
74
+ }
75
+ return Math.abs(total);
76
+ }
77
+
78
+ // Allow bare polygon coordinates array or GeoJSON feature.
79
+ // Normalize to multipolygon points array.
80
+
81
+ export function polyNormalize(poly: any): PP.PolyPack
82
+ {
83
+ return PP.polyPack(poly);
84
+ }
85
+
86
+ export function polyNull(poly: any): boolean
87
+ {
88
+ let pp = PP.polyPack(poly);
89
+ return pp == null || pp.length == 2;
90
+ }
91
+
92
+ // Area of geodesic polygon
93
+ export function polyArea(poly: any): number
94
+ {
95
+ let pp = polyNormalize(poly);
96
+ let a: number = 0;
97
+
98
+ // MultiPolygon is a set of polygons
99
+ PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
100
+ a += polySimpleArea(b, iOffset, nPoints) * (iRing == 0 ? 1 : -1);
101
+ });
102
+
103
+ return a;
104
+ }
105
+
106
+ // NOTE - COMPACTNESS: Added the diameter of the geodesic polygon in meters
107
+ export function polyDiameter(poly: any, options?: PolyOptions): number
108
+ {
109
+ if (options === undefined) options = DefaultOptions;
110
+
111
+ const ch: any = polyConvexHull(poly);
112
+ const chCircle: Circle = polyToCircle(ch);
113
+
114
+ const lon: number = chCircle.x;
115
+ const lat: number = chCircle.y;
116
+ const r: number = chCircle.r;
117
+
118
+ const lonDistance: number = haversine(lon - r, lat, lon + r, lat, options);
119
+ const latDistance: number = haversine(lon, lat - r, lon, lat + r, options);
120
+
121
+ const diameter: number = Math.max(lonDistance, latDistance);
122
+
123
+ return diameter;
124
+ }
125
+
126
+ // Return distance in meters given two lat/lon points
127
+ function haversine(x1: number, y1: number, x2: number, y2: number, options: PolyOptions): number
128
+ {
129
+ let dLat = Util.deg2rad(y2 - y1);
130
+ let dLon = Util.deg2rad(x2 - x1);
131
+ let c: number;
132
+
133
+ // Short circuit for using simple cartesian algorithm instead of haversine
134
+ if (options.noLatitudeCorrection)
135
+ c = Math.sqrt((dLat*dLat)+(dLon*dLon));
136
+ else
137
+ {
138
+ let lat1 = Util.deg2rad(y1);
139
+ let lat2 = Util.deg2rad(y2);
140
+
141
+ let a = Math.sin(dLat/2) * Math.sin(dLat/2) +
142
+ Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2)
143
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
144
+ }
145
+
146
+ return EARTH_RADIUS * c;
147
+ }
148
+
149
+ // The perimeter of geodesic polygon in meters
150
+ export function polyPerimeter(poly: any, options?: PolyOptions): number
151
+ {
152
+ if (options === undefined) options = DefaultOptions;
153
+
154
+ let pp = polyNormalize(poly);
155
+ let perimeter: number = 0;
156
+
157
+ PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
158
+ if (iRing > 0) return; // skip holes
159
+ let iStart = iOffset;
160
+ let iEnd = (iOffset + nPoints * 2) - 2; // index to last point, not past last point
161
+ for (; iOffset < iEnd; iOffset += 2)
162
+ perimeter += haversine(b[iOffset], b[iOffset+1], b[iOffset+2], b[iOffset+3], options);
163
+
164
+ // Close ring if necessary
165
+ if (nPoints > 2 && (b[iStart] != b[iEnd] || b[iStart+1] != b[iEnd+1]))
166
+ perimeter += haversine(b[iStart], b[iStart+1], b[iEnd], b[iEnd+1], options);
167
+ });
168
+
169
+ return perimeter;
170
+ }
171
+
172
+ class Point {
173
+ public constructor(
174
+ public x: number,
175
+ public y: number) {}
176
+ }
177
+
178
+ export class Circle {
179
+ public constructor(
180
+ public x: number,
181
+ public y: number,
182
+ public r: number) {}
183
+ }
184
+
185
+ //
186
+ // Returns the smallest circle that encloses the given polygon.
187
+ // Runs in expected O(n) time, randomized.
188
+ // Note: If 0 points are given, null is returned.
189
+ // If 1 point is given, a circle of radius 0 is returned.
190
+ //
191
+
192
+ export function polyToCircle(poly: any): Circle|null
193
+ {
194
+ return makeCircle(polyToExteriorPoints(poly));
195
+ }
196
+
197
+ //
198
+ // Returns the circle whose perimeter is equal to the perimeter of the bounding perimeter
199
+ // of the polygon. Use binary search to find an approximation.
200
+ //
201
+
202
+ export function polyToPolsbyPopperCircle(poly: any, options?: PolyOptions): Circle|null
203
+ {
204
+ let pp = polyNormalize(poly);
205
+
206
+ let c = polyToCircle(poly);
207
+ if (c == null) return c;
208
+ let p: number = polyPerimeter(poly, options);
209
+ c.r = (p / (2 * Math.PI)) / 111139;
210
+ return c;
211
+ }
212
+
213
+ function makeCircle(points: Array<Point>): Circle|null
214
+ {
215
+ if (points == null) return null;
216
+
217
+ // Clone list to preserve the caller's data, do Durstenfeld shuffle
218
+ let shuffled: Array<Point> = points.slice();
219
+ for (let i = points.length - 1; i >= 0; i--) {
220
+ let j = Math.floor(Math.random() * (i + 1));
221
+ j = Math.max(Math.min(j, i), 0);
222
+ const temp: Point = shuffled[i];
223
+ shuffled[i] = shuffled[j];
224
+ shuffled[j] = temp;
225
+ }
226
+
227
+ // Progressively add points to circle or recompute circle
228
+ let c: Circle|null = null;
229
+ shuffled.forEach((p: Point, i: number) => {
230
+ if (c === null || !isInCircle(c, p))
231
+ c = makeCircleOnePoint(shuffled.slice(0, i + 1), p);
232
+ });
233
+ return c;
234
+ }
235
+
236
+ // One boundary point known
237
+ function makeCircleOnePoint(points: Array<Point>, p: Point): Circle
238
+ {
239
+ let c: Circle = new Circle(p.x, p.y, 0);
240
+ points.forEach((q: Point, i: number) => {
241
+ if (!isInCircle(c, q)) {
242
+ if (c.r == 0)
243
+ c = makeDiameter(p, q);
244
+ else
245
+ c = makeCircleTwoPoints(points.slice(0, i + 1), p, q);
246
+ }
247
+ });
248
+ return c;
249
+ }
250
+
251
+ // Two boundary points known
252
+ function makeCircleTwoPoints(points: Array<Point>, p: Point, q: Point): Circle
253
+ {
254
+ const circ: Circle = makeDiameter(p, q);
255
+ let left : Circle|null = null;
256
+ let right: Circle|null = null;
257
+
258
+ // For each point not in the two-point circle
259
+ for (const r of points) {
260
+ if (isInCircle(circ, r))
261
+ continue;
262
+
263
+ // Form a circumcircle and classify it on left or right side
264
+ const cross: number = crossProduct(p.x, p.y, q.x, q.y, r.x, r.y);
265
+ const c: Circle|null = makeCircumcircle(p, q, r);
266
+ if (c === null)
267
+ continue;
268
+ else if (cross > 0 && (left === null || crossProduct(p.x, p.y, q.x, q.y, c.x, c.y) > crossProduct(p.x, p.y, q.x, q.y, left.x, left.y)))
269
+ left = c;
270
+ else if (cross < 0 && (right === null || crossProduct(p.x, p.y, q.x, q.y, c.x, c.y) < crossProduct(p.x, p.y, q.x, q.y, right.x, right.y)))
271
+ right = c;
272
+ }
273
+
274
+ // Select which circle to return
275
+ if (left === null && right === null)
276
+ return circ;
277
+ else if (left === null && right !== null)
278
+ return right;
279
+ else if (left !== null && right === null)
280
+ return left;
281
+ else if (left !== null && right !== null)
282
+ return left.r <= right.r ? left : right;
283
+ else
284
+ throw "Assertion error";
285
+ }
286
+
287
+ function makeDiameter(a: Point, b: Point): Circle
288
+ {
289
+ const cx: number = (a.x + b.x) / 2;
290
+ const cy: number = (a.y + b.y) / 2;
291
+ const r0: number = Util.distance(cx, cy, a.x, a.y);
292
+ const r1: number = Util.distance(cx, cy, b.x, b.y);
293
+ return new Circle(cx, cy, Math.max(r0, r1));
294
+ }
295
+
296
+ function makeCircumcircle(a: Point, b: Point, c: Point): Circle|null
297
+ {
298
+ // Mathematical algorithm from Wikipedia: Circumscribed circle
299
+ const ox: number = (Math.min(a.x, b.x, c.x) + Math.max(a.x, b.x, c.x)) / 2;
300
+ const oy: number = (Math.min(a.y, b.y, c.y) + Math.max(a.y, b.y, c.y)) / 2;
301
+ const ax: number = a.x - ox; const ay: number = a.y - oy;
302
+ const bx: number = b.x - ox; const by: number = b.y - oy;
303
+ const cx: number = c.x - ox; const cy: number = c.y - oy;
304
+ const d: number = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2;
305
+ if (d == 0)
306
+ return null;
307
+ const x: number = ox + ((ax*ax + ay*ay) * (by - cy) + (bx*bx + by*by) * (cy - ay) + (cx*cx + cy*cy) * (ay - by)) / d;
308
+ const y: number = oy + ((ax*ax + ay*ay) * (cx - bx) + (bx*bx + by*by) * (ax - cx) + (cx*cx + cy*cy) * (bx - ax)) / d;
309
+ const ra: number = Util.distance(x, y, a.x, a.y);
310
+ const rb: number = Util.distance(x, y, b.x, b.y);
311
+ const rc: number = Util.distance(x, y, c.x, c.y);
312
+ return new Circle(x, y, Math.max(ra, rb, rc));
313
+ }
314
+
315
+ /* Simple mathematical functions */
316
+
317
+ const MULTIPLICATIVE_EPSILON: number = 1 + 1e-14;
318
+
319
+ function isInCircle(c: Circle|null, p: Point): boolean
320
+ {
321
+ return c !== null && Util.distance(p.x, p.y, c.x, c.y) <= c.r * MULTIPLICATIVE_EPSILON;
322
+ }
323
+
324
+ // Returns twice the signed area of the triangle defined by (x0, y0), (x1, y1), (x2, y2).
325
+ function crossProduct(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number): number
326
+ {
327
+ return (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
328
+ }
329
+
330
+ // cache x,y circle offsets for one quadrant indexed by number of segments
331
+ let circleOffsets: any = {};
332
+
333
+ function getCircleOffsets(n: number): any
334
+ {
335
+ if (circleOffsets[n] === undefined)
336
+ {
337
+ let a: number[] = [];
338
+ let incr = (Math.PI / 2) / n;
339
+ let theta: number = 0;
340
+ let i: number, j: number;
341
+
342
+ // Compute NE quadrant
343
+ for (i = 0; i < n; i++, theta += incr)
344
+ {
345
+ a.push(Math.sin(theta));
346
+ a.push(Math.cos(theta));
347
+ }
348
+ // Add top of circle
349
+ a.push(0);
350
+ a.push(1);
351
+ // Now flip X and replicate to NW quadrant
352
+ for (i = 0; i < n; i++)
353
+ {
354
+ j = (n-i)*2;
355
+ a.push(- a[j-1]);
356
+ a.push(a[j-2]);
357
+ }
358
+ // Now flip X and Y and replicate to SW quadrant
359
+ for (i = 0; i < n; i++)
360
+ {
361
+ j = (n-i)*2;
362
+ a.push(- a[j-1]);
363
+ a.push(- a[j-2]);
364
+ }
365
+ // Add bottom of circle
366
+ a.push(0);
367
+ a.push(-1);
368
+ // Now flip Y and replicate to SE quadrant
369
+ for (i = 0; i < n; i++)
370
+ {
371
+ j = (n-i)*2;
372
+ a.push(a[j-1]);
373
+ a.push(- a[j-2]);
374
+ }
375
+ // Duplicate final point per GeoJSON spec
376
+ a.push(a[0]);
377
+ a.push(a[1]);
378
+
379
+ circleOffsets[n] = a;
380
+ }
381
+
382
+ return circleOffsets[n];
383
+ }
384
+
385
+ // Note that this is essentially an inversion of polyToCircle which computes a mathematical
386
+ // circle given the point values, ignoring latitude correction. This inversion also
387
+ // ignores that correction which means when plotted on a 2D map looks circular but the edge of
388
+ // the circle is not a fixed distance (in physical units, e.g. meters) from the center.
389
+ //
390
+
391
+ export function polyFromCircle(c: Circle, nSegments?: number): any
392
+ {
393
+ if (c === null || c.r == 0) return null;
394
+ if (!nSegments || nSegments < 8) nSegments = 8;
395
+ let poly: any = [];
396
+
397
+ let offsets: any = getCircleOffsets(nSegments);
398
+ const n: number = offsets.length;
399
+
400
+ for (let i: number = 0; i < n; i += 2)
401
+ poly.push([ c.x + (c.r * offsets[i]), c.y + (c.r * offsets[i+1]) ]);
402
+
403
+ // return multi-polygon
404
+ return [ [ poly ] ];
405
+ }
406
+
407
+ // isLeft(): tests if a point is Left|On|Right of an infinite line.
408
+ // Input: three points P0, P1, and P2
409
+ // Return: >0 for P2 left of the line through P0 and P1
410
+ // =0 for P2 on the line
411
+ // <0 for P2 right of the line
412
+
413
+ function sortPointX(a: Point, b: Point): number { return a.x - b.x; }
414
+ function sortPointY(a: Point, b: Point): number { return a.y - b.y; }
415
+ function sortPoint(a: Point, b: Point): number { return a.x === b.x ? a.y - b.y : a.x - b.x; }
416
+ function isLeft(p0: Point, p1: Point, p2: Point): number { return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); }
417
+
418
+ function pointsToPoly(points: Point[]): any
419
+ {
420
+ let p: any = [];
421
+
422
+ for (let i: number = 0; i < points.length; i++)
423
+ p.push( [ points[i].x, points[i].y ] );
424
+
425
+ return [ p ];
426
+ }
427
+
428
+ export function polyToExteriorPoints(poly: any): any
429
+ {
430
+ let pp = polyNormalize(poly);
431
+ if (pp == null) return null;
432
+
433
+ let points: Point[] = [];
434
+
435
+ PP.polyPackEachPoint(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number) => {
436
+ if (iRing > 0) return; // skip holes
437
+ points.push(new Point(b[iOffset], b[iOffset+1]));
438
+ });
439
+
440
+ return points.length > 0 ? points : null;
441
+ }
442
+
443
+ //
444
+ // polyConvexHull() - Two algorithms:
445
+ // * Gram Scan (default) - in graham-scan.ts
446
+ // * A.M.Andrew's monotone chain 2D convex hull algorithm - below
447
+ //
448
+
449
+ export function polyConvexHull(poly: any, altAlgorithm?: any): any
450
+ {
451
+ if (altAlgorithm !== undefined)
452
+ return makeConvexHullMonotoneChain2D(poly);
453
+
454
+ return makeConvexHullGrahamScan(poly);
455
+ }
456
+
457
+ export function makeConvexHullMonotoneChain2D(poly: any): any
458
+ {
459
+ // Normalize input
460
+ let points = polyToExteriorPoints(poly);
461
+ if (points == null) return null;
462
+
463
+ points.sort(sortPoint);
464
+
465
+ let H: Point[] = [];
466
+
467
+ // the output array H[] will be used as the stack
468
+ let n: number = points.length;
469
+ let i: number;
470
+ let bot: number = 0;
471
+ let top: number = -1; // indices for bottom and top of the stack
472
+
473
+ // Get the indices of points with min x-coord and min|max y-coord
474
+ let minmin: number = 0, minmax: number;
475
+
476
+ let xmin = points[0].x;
477
+ for (i = 1; i < n; i++)
478
+ if (points[i].x != xmin)
479
+ break;
480
+
481
+ minmax = i - 1;
482
+ if (minmax == n - 1) // degenerate case: all x-coords == xmin
483
+ {
484
+ H[++top] = points[minmin];
485
+ if (points[minmax].y != points[minmin].y) // a nontrivial segment
486
+ H[++top] = points[minmax];
487
+ H[++top] = points[minmin]; // add polygon endpoint
488
+ return pointsToPoly(H);
489
+ }
490
+
491
+ // Get the indices of points with max x-coord and min|max y-coord
492
+ let maxmin: number, maxmax: number = n - 1;
493
+ let xmax: number = points[n - 1].x;
494
+ for (i = n - 2; i >= 0; i--)
495
+ if (points[i].x != xmax)
496
+ break;
497
+ maxmin = i + 1;
498
+
499
+ // Compute the lower hull on the stack H
500
+ H[++top] = points[minmin]; // push minmin point onto stack
501
+ i = minmax;
502
+ while (++i <= maxmin)
503
+ {
504
+ // the lower line joins points[minmin] with points[maxmin]
505
+ if (isLeft(points[minmin], points[maxmin], points[i]) >= 0 && i < maxmin)
506
+ continue; // ignore points[i] above or on the lower line
507
+
508
+ while (top > 0) // there are at least 2 points on the stack
509
+ {
510
+ // test if points[i] is left of the line at the stack top
511
+ if (isLeft(H[top - 1], H[top], points[i]) > 0)
512
+ break; // points[i] is a new hull vertex
513
+ else
514
+ top--; // pop top point off stack
515
+ }
516
+
517
+ H[++top] = points[i]; // push points[i] onto stack
518
+ }
519
+
520
+ // Next, compute the upper hull on the stack H above the bottom hull
521
+ if (maxmax != maxmin) // if distinct xmax points
522
+ H[++top] = points[maxmax]; // push maxmax point onto stack
523
+
524
+ bot = top; // the bottom point of the upper hull stack
525
+ i = maxmin;
526
+ while (--i >= minmax)
527
+ {
528
+ // the upper line joins points[maxmax] with points[minmax]
529
+ if (isLeft(points[maxmax], points[minmax], points[i]) >= 0 && i > minmax)
530
+ continue; // ignore points[i] below or on the upper line
531
+
532
+ while (top > bot) // at least 2 points on the upper stack
533
+ {
534
+ // test if points[i] is left of the line at the stack top
535
+ if (isLeft(H[top - 1], H[top], points[i]) > 0)
536
+ break; // points[i] is a new hull vertex
537
+ else
538
+ top--; // pop top point off stack
539
+ }
540
+
541
+ if (points[i].x == H[0].x && points[i].y == H[0].y)
542
+ return pointsToPoly(H); // special case (mgomes)
543
+
544
+ H[++top] = points[i]; // push points[i] onto stack
545
+ }
546
+
547
+ if (minmax != minmin)
548
+ H[++top] = points[minmin]; // push joining endpoint onto stack
549
+
550
+ return pointsToPoly(H);
551
+ }
552
+
553
+ // NOTE - COMPACTNESS: Removed all the compactness notes.
554
+
555
+ // TODO - COMPACTNESS: Ultimately remove this interface & polyCompactness(),
556
+ // when dra-client no longer uses them.
557
+ export interface CompactnessDescription
558
+ {
559
+ value: number,
560
+ reock: number,
561
+ polsby_popper: number,
562
+ convex_hull: number,
563
+ schwartzberg: number,
564
+ }
565
+
566
+ export function polyCompactness(poly: any, options?: PolyOptions): CompactnessDescription
567
+ {
568
+ if (options === undefined) options = DefaultOptions;
569
+
570
+ let pp = polyNormalize(poly);
571
+
572
+ let area: number = polyArea(pp);
573
+ let perimeter: number = polyPerimeter(pp, options);
574
+
575
+ let circle: Circle = polyToCircle(pp);
576
+ let circleArea: number = polyArea(polyFromCircle(circle));
577
+ let ppcircleArea: number = polyArea(polyFromCircle(polyToPolsbyPopperCircle(poly)));
578
+ let hullArea: number = polyArea(polyConvexHull(pp));
579
+
580
+ let result: CompactnessDescription = { value: 0, reock: 0, polsby_popper: 0, convex_hull: 0, schwartzberg: 0 };
581
+ if (area == 0 || circle == null || circle.r == 0) return result;
582
+
583
+ result.reock = area / circleArea;
584
+ result.polsby_popper = area / ppcircleArea;
585
+ result.convex_hull = area / hullArea;
586
+ result.schwartzberg = ((2 * Math.PI) * Math.sqrt(area / Math.PI)) / perimeter;
587
+
588
+ // Weight Reock and Polsby-Popper equally, normalize in 0-100 range
589
+ result.value = Math.trunc(((result.reock + result.polsby_popper) / 2) * 100);
590
+ return result;
591
+ }
592
+
593
+
594
+ export interface PolyDescription
595
+ {
596
+ npoly: number,
597
+ nhole: number,
598
+ npoint: number
599
+ }
600
+
601
+ const EmptyPolyDescription = { npoly: 0, nhole: 0, npoint: 0 };
602
+
603
+ export function polyDescribe(poly: any): PolyDescription
604
+ {
605
+ let pp = polyNormalize(poly);
606
+ let d = Util.shallowCopy(EmptyPolyDescription);
607
+
608
+ PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
609
+ d.npoint += nPoints;
610
+ if (iRing == 0)
611
+ d.npoly++;
612
+ else
613
+ d.nhole++;
614
+ });
615
+
616
+ return d;
617
+ }
618
+
619
+ export function npoints(poly: any): number
620
+ {
621
+ let d = polyDescribe(poly);
622
+ return d.npoint;
623
+ }
624
+
625
+ //
626
+ // polyTransform: transform each point. Called for all polygons and all rings.
627
+ // point, by point. Returns a packed structure.
628
+ //
629
+ export function polyTransform(poly: any, transformFn: any): any
630
+ {
631
+ // Get all the points -- DON'T skip holes -- and transform them.
632
+ // Don't alter input structure so copy if polyNormalize did not.
633
+ let pp = polyNormalize(poly);
634
+ if (! PP.polyPacked(poly))
635
+ pp = PP.polyPackCopy(pp);
636
+
637
+ PP.polyPackEachPoint(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number) =>
638
+ {
639
+ const [newX, newY] = transformFn([b[iOffset], b[iOffset+1]]);
640
+ b[iOffset] = newX;
641
+ b[iOffset+1] = newY;
642
+ });
643
+
644
+ return pp;
645
+ }
646
+
647
+ function identityTransform(pt: number[]): number[]
648
+ {
649
+ return [pt[0], pt[1]];
650
+ }
651
+
652
+ function closeRing(ring: any): any
653
+ {
654
+ if (ring.length > 1)
655
+ {
656
+ let p1 = ring[0];
657
+ let p2 = ring[ring.length-1];
658
+ if (p1[0] !== p2[0] || p1[1] !== p2[1])
659
+ ring.push(p1);
660
+ }
661
+ return ring;
662
+ }
663
+
664
+ function closePoly(poly: any): any
665
+ {
666
+ poly.forEach(closeRing);
667
+ return poly;
668
+ }
669
+
670
+ // This mutates the passed in feature to ensure it is correctly wound
671
+ // For convenience, passed back the value provided.
672
+ export function featureRewind(poly: any): any
673
+ {
674
+ let pp = polyNormalize(poly);
675
+ if (pp == null) return null;
676
+ polyRewindRings(pp);
677
+ if (poly.type === 'Feature')
678
+ {
679
+ if (poly.geometry.packed !== pp)
680
+ {
681
+ poly.geometry.coordinates = PP.polyUnpack(pp);
682
+ // Also make sure first === last coordinate
683
+ let d = Util.depthof(poly.geometry.coordinates);
684
+ if (d === 4) closePoly(poly.geometry.coordinates);
685
+ else if (d === 5) poly.geometry.coordinates.forEach(closePoly);
686
+ }
687
+ return poly;
688
+ }
689
+ else if (poly === pp)
690
+ return pp;
691
+ else
692
+ return PP.polyUnpack(pp);
693
+ }
694
+
695
+ //
696
+ // polyRewindRings: Check the winding order of the polygon's ring. Rewind them,
697
+ // if necessary. Return a packed polygon.
698
+ //
699
+ export function polyRewindRings(pp: any, bLog: boolean = false): any
700
+ {
701
+ if (pp == null || pp.buffer == null) return;
702
+
703
+ if (pp.offset === undefined) throw 'oops: not a PolyPack';
704
+
705
+ PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) =>
706
+ {
707
+ const iStart = iOffset;
708
+ const iEnd = iStart + (nPoints * 2) - 2;
709
+
710
+ // Determine the winding order of the ring
711
+ let direction = 0;
712
+
713
+ // Start at the second point
714
+ iOffset += 2;
715
+ for (; iOffset <= iEnd; iOffset += 2)
716
+ {
717
+ // The previous point
718
+ let jOffset = iOffset - 2;
719
+
720
+ // Sum over the edges
721
+ direction += twoTimesArea(b[iOffset], b[jOffset], b[iOffset+1], b[jOffset+1]);
722
+ }
723
+
724
+ // Implicitly close the ring, if necessary
725
+ if (nPoints > 2 && (b[iStart] != b[iEnd] || b[iStart + 1] != b[iEnd + 1]))
726
+ {
727
+ if (bLog) console.log(`Implicitly closing polygon ${iPoly}, ring ${iRing}.`);
728
+
729
+ direction += twoTimesArea(b[iStart], b[iEnd], b[iStart + 1], b[iEnd + 1]);
730
+ }
731
+
732
+ // If the winding order is wrong, reverse it
733
+ if (((iRing == 0) && (direction > 0)) || ((iRing > 0) && (direction < 0)))
734
+ {
735
+ if (bLog) console.log(`Rewinding polygon ${iPoly}, ring ${iRing}.`);
736
+
737
+ let iFront = iStart;
738
+ let iBack = iEnd;
739
+ for (; iFront < iBack; iFront += 2, iBack -= 2)
740
+ {
741
+ let tmpX = b[iFront];
742
+ let tmpY = b[iFront+1];
743
+ b[iFront] = b[iBack];
744
+ b[iFront+1] = b[iBack+1];
745
+ b[iBack] = tmpX;
746
+ b[iBack+1] = tmpY;
747
+ }
748
+ }
749
+ });
750
+
751
+ return pp;
752
+ }
753
+
754
+ //
755
+ // To figure out which way a ring is wound:
756
+ //
757
+ // Sum over the edges, (x2 − x1) (y2 + y1). If the result is positive the curve
758
+ // is clockwise, if it's negative the curve is counterclockwise. (The result is
759
+ // twice the enclosed area, with a + /- convention.)
760
+ //
761
+ // Source: https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order#1165943
762
+ //
763
+ function twoTimesArea(x2: number, x1: number, y2: number, y1: number): number
764
+ {
765
+ return (x2 - x1) * (y2 + y1);
766
+ }
767
+