@hpcc-js/map 3.5.4 → 3.5.5

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 (95) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +88 -88
  3. package/TopoJSON/BR.json +122 -122
  4. package/TopoJSON/GB_idx.json +1 -1
  5. package/TopoJSON/IE_idx.json +1 -1
  6. package/TopoJSON/ND_idx.json +1 -1
  7. package/TopoJSON/countries.json +257 -257
  8. package/TopoJSON/us-counties.json +16550 -16550
  9. package/TopoJSON/us-states.json +458 -458
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.umd.cjs +1 -1
  13. package/dist/index.umd.cjs.map +1 -1
  14. package/package.json +8 -8
  15. package/src/CanvasPinLayer.ts +99 -99
  16. package/src/CanvasPins.ts +397 -397
  17. package/src/Choropleth.css +26 -26
  18. package/src/Choropleth.ts +203 -203
  19. package/src/ChoroplethContinents.ts +13 -13
  20. package/src/ChoroplethCounties.ts +111 -111
  21. package/src/ChoroplethCountries.ts +100 -100
  22. package/src/ChoroplethStates.ts +103 -103
  23. package/src/ChoroplethStatesHeat.ts +8 -8
  24. package/src/GMap.css +20 -20
  25. package/src/GMap.ts +880 -880
  26. package/src/GMapCounties.ts +93 -93
  27. package/src/GMapGraph.ts +61 -61
  28. package/src/GMapHeat.ts +27 -27
  29. package/src/GMapLayered.ts +94 -94
  30. package/src/GMapPin.ts +115 -115
  31. package/src/GMapPinLine.ts +138 -138
  32. package/src/GeoHash.css +14 -14
  33. package/src/GeoHash.ts +139 -139
  34. package/src/Graph.css +9 -9
  35. package/src/Graph.ts +98 -98
  36. package/src/Graticule.css +12 -12
  37. package/src/Graticule.ts +97 -97
  38. package/src/Heat.ts +87 -87
  39. package/src/IChoropleth.ts +8 -8
  40. package/src/Layer.ts +99 -99
  41. package/src/Layered.css +18 -18
  42. package/src/Layered.ts +206 -206
  43. package/src/Lines.css +8 -8
  44. package/src/Lines.ts +78 -78
  45. package/src/OpenStreet.css +15 -15
  46. package/src/OpenStreet.ts +126 -126
  47. package/src/Pins.css +17 -17
  48. package/src/Pins.ts +350 -350
  49. package/src/Projection.ts +42 -42
  50. package/src/TestHeatMap.ts +8 -8
  51. package/src/TopoJSONChoropleth.ts +125 -125
  52. package/src/Utility.ts +484 -484
  53. package/src/__package__.ts +3 -3
  54. package/src/index.ts +33 -33
  55. package/src/leaflet/AlbersPR.ts +48 -48
  56. package/src/leaflet/Blank.ts +9 -9
  57. package/src/leaflet/Circles.ts +139 -139
  58. package/src/leaflet/ClusterCircles.css +26 -26
  59. package/src/leaflet/ClusterCircles.ts +88 -88
  60. package/src/leaflet/Countries.ts +43 -43
  61. package/src/leaflet/DrawLayer.ts +167 -167
  62. package/src/leaflet/FeatureLayer.ts +138 -138
  63. package/src/leaflet/GMap.ts +44 -44
  64. package/src/leaflet/HeatLayer.ts +77 -77
  65. package/src/leaflet/Icons.ts +60 -60
  66. package/src/leaflet/Leaflet.css +3 -3
  67. package/src/leaflet/Leaflet.ts +239 -239
  68. package/src/leaflet/MapBox.ts +35 -35
  69. package/src/leaflet/Markers.ts +109 -109
  70. package/src/leaflet/OpenStreet.ts +27 -27
  71. package/src/leaflet/Path.ts +138 -138
  72. package/src/leaflet/Pins.ts +73 -73
  73. package/src/leaflet/Polygons.ts +113 -113
  74. package/src/leaflet/Region.ts +138 -138
  75. package/src/leaflet/Text.ts +99 -99
  76. package/src/leaflet/TileLayer.ts +81 -81
  77. package/src/leaflet/TopoJSON.ts +146 -146
  78. package/src/leaflet/US.ts +15 -15
  79. package/src/leaflet/USCounties.ts +43 -43
  80. package/src/leaflet/USStates.ts +41 -41
  81. package/src/leaflet/World.css +3 -3
  82. package/src/leaflet/World.ts +172 -172
  83. package/src/leaflet/index.ts +18 -18
  84. package/src/leaflet/leaflet-shim.ts +18 -18
  85. package/src/leaflet/plugins/BeautifyIcon.css +44 -44
  86. package/src/leaflet/plugins/BeautifyIcon.ts +190 -190
  87. package/src/leaflet/plugins/BeutifyIcon.licence +20 -20
  88. package/src/leaflet/plugins/D3SvgOverlay.css +2 -2
  89. package/src/leaflet/plugins/D3SvgOverlay.licence +20 -20
  90. package/src/leaflet/plugins/D3SvgOverlay.ts +175 -175
  91. package/src/leaflet/plugins/HeatLayer.license +21 -21
  92. package/src/leaflet/plugins/HeatLayer.ts +224 -224
  93. package/src/leaflet/plugins/Leaflet.GoogleMutant.ts +424 -424
  94. package/src/leaflet/plugins/lru_map.ts +352 -352
  95. package/src/test.ts +114 -114
package/src/Utility.ts CHANGED
@@ -1,484 +1,484 @@
1
- import { geoAlbers, geoConicEqualArea, GeoStream, geoStream } from "d3-geo";
2
-
3
- function noop() { }
4
- let x0 = Infinity;
5
- let y0 = x0;
6
- let x1 = -x0;
7
- let y1 = x1;
8
-
9
- function boundsPoint(x, y) {
10
- if (x < x0) x0 = x;
11
- if (x > x1) x1 = x;
12
- if (y < y0) y0 = y;
13
- if (y > y1) y1 = y;
14
- }
15
-
16
- const boundsStream = {
17
- point: boundsPoint,
18
- lineStart: noop,
19
- lineEnd: noop,
20
- polygonStart: noop,
21
- polygonEnd: noop,
22
- result() {
23
- const bounds = [[x0, y0], [x1, y1]];
24
- x1 = y1 = -(y0 = x0 = Infinity);
25
- return bounds;
26
- }
27
- };
28
-
29
- function fitExtent(projection, extent, object) {
30
- const w = extent[1][0] - extent[0][0];
31
- const h = extent[1][1] - extent[0][1];
32
- const clip = projection.clipExtent && projection.clipExtent();
33
-
34
- projection
35
- .scale(150)
36
- .translate([0, 0]);
37
-
38
- if (clip != null) projection.clipExtent(null);
39
-
40
- geoStream(object, projection.stream(boundsStream));
41
-
42
- const b = boundsStream.result();
43
- const k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1]));
44
- const x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2;
45
- const y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
46
-
47
- if (clip != null) projection.clipExtent(clip);
48
-
49
- return projection
50
- .scale(k * 150)
51
- .translate([x, y]);
52
- }
53
-
54
- function fitSize(projection, size, object) {
55
- return fitExtent(projection, [[0, 0], size], object);
56
- }
57
-
58
- // const d3Geo = _d3Geo.geo || _d3Geo.default || _d3Geo;
59
-
60
- // Origonally ased on geohash.js
61
- // Geohash library for Javascript
62
- // (c) 2008 David Troy
63
- // Distributed under the MIT License
64
-
65
- export class Geohash {
66
- /* (Geohash-specific) Base32 map */
67
- private static base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
68
-
69
- /**
70
- * Encodes latitude/longitude to geohash, either to specified precision or to automatically
71
- * evaluated precision.
72
- *
73
- * @param {number} lat - Latitude in degrees.
74
- * @param {number} lon - Longitude in degrees.
75
- * @param {number} [precision] - Number of characters in resulting geohash.
76
- * @returns {string} Geohash of supplied latitude/longitude.
77
- * @throws Invalid geohash.
78
- *
79
- * @example
80
- * var geohash = Geohash.encode(52.205, 0.119, 7); // geohash: "u120fxw"
81
- */
82
- encode(lat, lon, precision) {
83
- // infer precision?
84
- if (typeof precision === "undefined") {
85
- // refine geohash until it matches precision of supplied lat/lon
86
- for (let p = 1; p <= 12; p++) {
87
- const hash = this.encode(lat, lon, p);
88
- const posn = this.decode(hash);
89
- if (posn.lat === lat && posn.lon === lon) return hash;
90
- }
91
- precision = 12; // set to maximum
92
- }
93
-
94
- lat = Number(lat);
95
- lon = Number(lon);
96
- precision = Number(precision);
97
-
98
- if (isNaN(lat) || isNaN(lon) || isNaN(precision)) throw new Error("Invalid geohash");
99
-
100
- let idx = 0; // index into base32 map
101
- let bit = 0; // each char holds 5 bits
102
- let evenBit = true;
103
- let geohash = "";
104
-
105
- let latMin = -90;
106
- let latMax = 90;
107
- let lonMin = -180;
108
- let lonMax = 180;
109
-
110
- while (geohash.length < precision) {
111
- if (evenBit) {
112
- // bisect E-W longitude
113
- const lonMid = (lonMin + lonMax) / 2;
114
- if (lon > lonMid) {
115
- idx = idx * 2 + 1;
116
- lonMin = lonMid;
117
- } else {
118
- idx = idx * 2;
119
- lonMax = lonMid;
120
- }
121
- } else {
122
- // bisect N-S latitude
123
- const latMid = (latMin + latMax) / 2;
124
- if (lat > latMid) {
125
- idx = idx * 2 + 1;
126
- latMin = latMid;
127
- } else {
128
- idx = idx * 2;
129
- latMax = latMid;
130
- }
131
- }
132
- evenBit = !evenBit;
133
-
134
- if (++bit === 5) {
135
- // 5 bits gives us a character: append it and start over
136
- geohash += Geohash.base32.charAt(idx);
137
- bit = 0;
138
- idx = 0;
139
- }
140
- }
141
-
142
- return geohash;
143
- }
144
-
145
- /**
146
- * Decode geohash to latitude/longitude (location is approximate centre of geohash cell,
147
- * to reasonable precision).
148
- *
149
- * @param {string} geohash - Geohash string to be converted to latitude/longitude.
150
- * @returns {{lat:number, lon:number}} (Center of) geohashed location.
151
- * @throws Invalid geohash.
152
- *
153
- * @example
154
- * var latlon = Geohash.decode("u120fxw"); // latlon: { lat: 52.205, lon: 0.1188 }
155
- */
156
- decode(geohash) {
157
-
158
- const bounds = this.bounds(geohash); // <-- the hard work
159
- // now just determine the centre of the cell...
160
-
161
- const latMin = bounds.sw.lat;
162
- const lonMin = bounds.sw.lon;
163
- const latMax = bounds.ne.lat;
164
- const lonMax = bounds.ne.lon;
165
-
166
- // cell centre
167
- let lat = (latMin + latMax) / 2;
168
- let lon = (lonMin + lonMax) / 2;
169
-
170
- // round to close to centre without excessive precision: ⌊2-log10(Δ°)⌋ decimal places
171
- lat = Number(lat.toFixed(Math.floor(2 - Math.log(latMax - latMin) / Math.LN10)));
172
- lon = Number(lon.toFixed(Math.floor(2 - Math.log(lonMax - lonMin) / Math.LN10)));
173
-
174
- return { lat, lon };
175
- }
176
-
177
- /**
178
- * Returns SW/NE latitude/longitude bounds of specified geohash.
179
- *
180
- * @param {string} geohash - Cell that bounds are required of.
181
- * @returns {{sw: {lat: number, lon: number}, ne: {lat: number, lon: number}}}
182
- * @throws Invalid geohash.
183
- */
184
- bounds(geohash) {
185
- if (geohash.length === 0) throw new Error("Invalid geohash");
186
-
187
- geohash = geohash.toLowerCase();
188
-
189
- let evenBit = true;
190
- let latMin = -90;
191
- let latMax = 90;
192
- let lonMin = -180;
193
- let lonMax = 180;
194
-
195
- for (let i = 0; i < geohash.length; i++) {
196
- const chr = geohash.charAt(i);
197
- const idx = Geohash.base32.indexOf(chr);
198
- if (idx === -1) throw new Error("Invalid geohash");
199
-
200
- for (let n = 4; n >= 0; n--) {
201
- const bitN = idx >> n & 1;
202
- if (evenBit) {
203
- // longitude
204
- const lonMid = (lonMin + lonMax) / 2;
205
- if (bitN === 1) {
206
- lonMin = lonMid;
207
- } else {
208
- lonMax = lonMid;
209
- }
210
- } else {
211
- // latitude
212
- const latMid = (latMin + latMax) / 2;
213
- if (bitN === 1) {
214
- latMin = latMid;
215
- } else {
216
- latMax = latMid;
217
- }
218
- }
219
- evenBit = !evenBit;
220
- }
221
- }
222
-
223
- const bounds = {
224
- sw: { lat: latMin, lon: lonMin },
225
- ne: { lat: latMax, lon: lonMax }
226
- };
227
-
228
- return bounds;
229
- }
230
-
231
- /**
232
- * Determines adjacent cell in given direction.
233
- *
234
- * @param geohash - Cell to which adjacent cell is required.
235
- * @param direction - Direction from geohash (N/S/E/W).
236
- * @returns {string} Geocode of adjacent cell.
237
- * @throws Invalid geohash.
238
- */
239
- adjacent(geohash, direction) {
240
- // based on github.com/davetroy/geohash-js
241
-
242
- geohash = geohash.toLowerCase();
243
- direction = direction.toLowerCase();
244
-
245
- if (geohash.length === 0) throw new Error("Invalid geohash");
246
- if ("nsew".indexOf(direction) === -1) throw new Error("Invalid direction");
247
-
248
- const neighbour = {
249
- n: ["p0r21436x8zb9dcf5h7kjnmqesgutwvy", "bc01fg45238967deuvhjyznpkmstqrwx"],
250
- s: ["14365h7k9dcfesgujnmqp0r2twvyx8zb", "238967debc01fg45kmstqrwxuvhjyznp"],
251
- e: ["bc01fg45238967deuvhjyznpkmstqrwx", "p0r21436x8zb9dcf5h7kjnmqesgutwvy"],
252
- w: ["238967debc01fg45kmstqrwxuvhjyznp", "14365h7k9dcfesgujnmqp0r2twvyx8zb"]
253
- };
254
- const border = {
255
- n: ["prxz", "bcfguvyz"],
256
- s: ["028b", "0145hjnp"],
257
- e: ["bcfguvyz", "prxz"],
258
- w: ["0145hjnp", "028b"]
259
- };
260
-
261
- const lastCh = geohash.slice(-1); // last character of hash
262
- let parent = geohash.slice(0, -1); // hash without last character
263
-
264
- const type = geohash.length % 2;
265
-
266
- // check for edge-cases which don"t share common prefix
267
- if (border[direction][type].indexOf(lastCh) !== -1 && parent !== "") {
268
- parent = this.adjacent(parent, direction);
269
- }
270
-
271
- // append letter for direction to parent
272
- return parent + Geohash.base32.charAt(neighbour[direction][type].indexOf(lastCh));
273
- }
274
-
275
- /**
276
- * Returns all 8 adjacent cells to specified geohash.
277
- *
278
- * @param {string} geohash - Geohash neighbours are required of.
279
- * @returns {{n,ne,e,se,s,sw,w,nw: string}}
280
- * @throws Invalid geohash.
281
- */
282
- neighbours(geohash) {
283
- return {
284
- n: this.adjacent(geohash, "n"),
285
- ne: this.adjacent(this.adjacent(geohash, "n"), "e"),
286
- e: this.adjacent(geohash, "e"),
287
- se: this.adjacent(this.adjacent(geohash, "s"), "e"),
288
- s: this.adjacent(geohash, "s"),
289
- sw: this.adjacent(this.adjacent(geohash, "s"), "w"),
290
- w: this.adjacent(geohash, "w"),
291
- nw: this.adjacent(this.adjacent(geohash, "n"), "w")
292
- };
293
- }
294
-
295
- // HPCC Extensions ---
296
- contained(w, n, e, s, precision) {
297
- if (isNaN(n) || n >= 90) n = 89;
298
- if (isNaN(e) || e > 180) e = 180;
299
- if (isNaN(s) || s <= -90) s = -89;
300
- if (isNaN(w) || w < -180) w = -180;
301
- precision = precision || 1;
302
- const geoHashNW = this.encode(n, w, precision);
303
- const geoHashNE = this.encode(n, e, precision);
304
- const geoHashSE = this.encode(s, e, precision);
305
- let currRowHash = geoHashNW;
306
- let col = 0;
307
- let maxCol = -1;
308
- const geoHashes = [geoHashNW, geoHashSE];
309
- let currHash = this.adjacent(geoHashNW, "e");
310
- while (currHash !== geoHashSE) {
311
- geoHashes.push(currHash);
312
- ++col;
313
- if (currHash === geoHashNE || maxCol === col) {
314
- maxCol = col + 1;
315
- col = 0;
316
- currHash = this.adjacent(currRowHash, "s");
317
- currRowHash = currHash;
318
- } else {
319
- currHash = this.adjacent(currHash, "e");
320
- }
321
- }
322
- return geoHashes;
323
- }
324
-
325
- calculateWidthDegrees(n) {
326
- let a;
327
- if (n % 2 === 0) {
328
- a = -1;
329
- } else {
330
- a = -0.5;
331
- }
332
- const result = 180 / Math.pow(2, 2.5 * n + a);
333
- return result;
334
- }
335
-
336
- width(n) {
337
- const parity = n % 2;
338
- return 180 / (2 ^ (((5 * n + parity) / 2) - 1));
339
- }
340
-
341
- }
342
-
343
- function multiplex(streams) {
344
- const n = streams.length;
345
- return {
346
- point(x, y) { let i = -1; while (++i < n) streams[i].point(x, y); },
347
- sphere() { let i = -1; while (++i < n) streams[i].sphere(); },
348
- lineStart() { let i = -1; while (++i < n) streams[i].lineStart(); },
349
- lineEnd() { let i = -1; while (++i < n) streams[i].lineEnd(); },
350
- polygonStart() { let i = -1; while (++i < n) streams[i].polygonStart(); },
351
- polygonEnd() { let i = -1; while (++i < n) streams[i].polygonEnd(); }
352
- };
353
- }
354
-
355
- // A modified d3.geo.albersUsa to include Puerto Rico.
356
- export function albersUsaPr() {
357
- let cache;
358
- let cacheStream;
359
-
360
- const ε = 1e-6;
361
-
362
- const lower48 = geoAlbers();
363
- let lower48Point;
364
-
365
- // EPSG:3338
366
- const alaska = geoConicEqualArea()
367
- .rotate([154, 0])
368
- .center([-2, 58.5])
369
- .parallels([55, 65]);
370
- let alaskaPoint;
371
-
372
- // ESRI:102007
373
- const hawaii = geoConicEqualArea()
374
- .rotate([157, 0])
375
- .center([-3, 19.9])
376
- .parallels([8, 18]);
377
- let hawaiiPoint;
378
-
379
- // XXX? You should check that this is a standard PR projection!
380
- const puertoRico = geoConicEqualArea()
381
- .rotate([66, 0])
382
- .center([0, 18])
383
- .parallels([8, 18]);
384
- let puertoRicoPoint;
385
-
386
- let point;
387
- const pointStream = { point: (x, y) => { point = [x, y]; } } as GeoStream;
388
-
389
- const albersUsa: any = function (coordinates) {
390
- const x = coordinates[0];
391
- const y = coordinates[1];
392
- point = null;
393
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
394
- (lower48Point.point(x, y), point) ||
395
- (alaskaPoint.point(x, y), point) ||
396
- (hawaiiPoint.point(x, y), point) ||
397
- (puertoRicoPoint.point(x, y), point);
398
- return point;
399
- };
400
-
401
- albersUsa.invert = function (coordinates) {
402
- const k = lower48.scale();
403
- const t = lower48.translate();
404
- const x = (coordinates[0] - t[0]) / k;
405
- const y = (coordinates[1] - t[1]) / k;
406
- return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
407
- : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
408
- : y >= 0.204 && y < 0.234 && x >= 0.320 && x < 0.380 ? puertoRico
409
- : lower48).invert(coordinates);
410
- };
411
-
412
- // A naïve multi-projection stream.
413
- // The projections must have mutually exclusive clip regions on the sphere,
414
- // as this will avoid emitting interleaving lines and polygons.
415
- albersUsa.stream = function (stream) {
416
- return cache && cacheStream === stream ? cache : cache = multiplex([
417
- lower48.stream(cacheStream = stream),
418
- alaska.stream(stream),
419
- hawaii.stream(stream),
420
- puertoRico.stream(stream)
421
- ]);
422
- };
423
-
424
- albersUsa.precision = function (_) {
425
- if (!arguments.length) return lower48.precision();
426
- lower48.precision(_);
427
- alaska.precision(_);
428
- hawaii.precision(_);
429
- puertoRico.precision(_);
430
- return reset();
431
- };
432
-
433
- albersUsa.scale = function (_) {
434
- if (!arguments.length) return lower48.scale();
435
- lower48.scale(_);
436
- alaska.scale(_ * 0.35);
437
- hawaii.scale(_);
438
- puertoRico.scale(_);
439
- return albersUsa.translate(lower48.translate());
440
- };
441
-
442
- albersUsa.translate = function (_) {
443
- if (!arguments.length) return lower48.translate();
444
- const k = lower48.scale();
445
- const x = +_[0];
446
- const y = +_[1];
447
-
448
- lower48Point = lower48
449
- .translate(_)
450
- .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
451
- .stream(pointStream);
452
-
453
- alaskaPoint = alaska
454
- .translate([x - 0.307 * k, y + 0.201 * k])
455
- .clipExtent([[x - 0.425 * k + ε, y + 0.120 * k + ε], [x - 0.214 * k - ε, y + 0.234 * k - ε]])
456
- .stream(pointStream);
457
-
458
- hawaiiPoint = hawaii
459
- .translate([x - 0.205 * k, y + 0.212 * k])
460
- .clipExtent([[x - 0.214 * k + ε, y + 0.166 * k + ε], [x - 0.115 * k - ε, y + 0.234 * k - ε]])
461
- .stream(pointStream);
462
-
463
- puertoRicoPoint = puertoRico
464
- .translate([x + 0.350 * k, y + 0.224 * k])
465
- .clipExtent([[x + 0.320 * k, y + 0.204 * k], [x + 0.380 * k, y + 0.234 * k]])
466
- .stream(pointStream);
467
-
468
- return reset();
469
- };
470
-
471
- albersUsa.fitExtent = function (extent, object) {
472
- return fitExtent(albersUsa, extent, object);
473
- };
474
-
475
- albersUsa.fitSize = function (size, object) {
476
- return fitSize(albersUsa, size, object);
477
- };
478
-
479
- function reset() {
480
- cache = cacheStream = null;
481
- return albersUsa;
482
- }
483
- return albersUsa.scale(1070);
484
- }
1
+ import { geoAlbers, geoConicEqualArea, GeoStream, geoStream } from "d3-geo";
2
+
3
+ function noop() { }
4
+ let x0 = Infinity;
5
+ let y0 = x0;
6
+ let x1 = -x0;
7
+ let y1 = x1;
8
+
9
+ function boundsPoint(x, y) {
10
+ if (x < x0) x0 = x;
11
+ if (x > x1) x1 = x;
12
+ if (y < y0) y0 = y;
13
+ if (y > y1) y1 = y;
14
+ }
15
+
16
+ const boundsStream = {
17
+ point: boundsPoint,
18
+ lineStart: noop,
19
+ lineEnd: noop,
20
+ polygonStart: noop,
21
+ polygonEnd: noop,
22
+ result() {
23
+ const bounds = [[x0, y0], [x1, y1]];
24
+ x1 = y1 = -(y0 = x0 = Infinity);
25
+ return bounds;
26
+ }
27
+ };
28
+
29
+ function fitExtent(projection, extent, object) {
30
+ const w = extent[1][0] - extent[0][0];
31
+ const h = extent[1][1] - extent[0][1];
32
+ const clip = projection.clipExtent && projection.clipExtent();
33
+
34
+ projection
35
+ .scale(150)
36
+ .translate([0, 0]);
37
+
38
+ if (clip != null) projection.clipExtent(null);
39
+
40
+ geoStream(object, projection.stream(boundsStream));
41
+
42
+ const b = boundsStream.result();
43
+ const k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1]));
44
+ const x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2;
45
+ const y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
46
+
47
+ if (clip != null) projection.clipExtent(clip);
48
+
49
+ return projection
50
+ .scale(k * 150)
51
+ .translate([x, y]);
52
+ }
53
+
54
+ function fitSize(projection, size, object) {
55
+ return fitExtent(projection, [[0, 0], size], object);
56
+ }
57
+
58
+ // const d3Geo = _d3Geo.geo || _d3Geo.default || _d3Geo;
59
+
60
+ // Origonally ased on geohash.js
61
+ // Geohash library for Javascript
62
+ // (c) 2008 David Troy
63
+ // Distributed under the MIT License
64
+
65
+ export class Geohash {
66
+ /* (Geohash-specific) Base32 map */
67
+ private static base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
68
+
69
+ /**
70
+ * Encodes latitude/longitude to geohash, either to specified precision or to automatically
71
+ * evaluated precision.
72
+ *
73
+ * @param {number} lat - Latitude in degrees.
74
+ * @param {number} lon - Longitude in degrees.
75
+ * @param {number} [precision] - Number of characters in resulting geohash.
76
+ * @returns {string} Geohash of supplied latitude/longitude.
77
+ * @throws Invalid geohash.
78
+ *
79
+ * @example
80
+ * var geohash = Geohash.encode(52.205, 0.119, 7); // geohash: "u120fxw"
81
+ */
82
+ encode(lat, lon, precision) {
83
+ // infer precision?
84
+ if (typeof precision === "undefined") {
85
+ // refine geohash until it matches precision of supplied lat/lon
86
+ for (let p = 1; p <= 12; p++) {
87
+ const hash = this.encode(lat, lon, p);
88
+ const posn = this.decode(hash);
89
+ if (posn.lat === lat && posn.lon === lon) return hash;
90
+ }
91
+ precision = 12; // set to maximum
92
+ }
93
+
94
+ lat = Number(lat);
95
+ lon = Number(lon);
96
+ precision = Number(precision);
97
+
98
+ if (isNaN(lat) || isNaN(lon) || isNaN(precision)) throw new Error("Invalid geohash");
99
+
100
+ let idx = 0; // index into base32 map
101
+ let bit = 0; // each char holds 5 bits
102
+ let evenBit = true;
103
+ let geohash = "";
104
+
105
+ let latMin = -90;
106
+ let latMax = 90;
107
+ let lonMin = -180;
108
+ let lonMax = 180;
109
+
110
+ while (geohash.length < precision) {
111
+ if (evenBit) {
112
+ // bisect E-W longitude
113
+ const lonMid = (lonMin + lonMax) / 2;
114
+ if (lon > lonMid) {
115
+ idx = idx * 2 + 1;
116
+ lonMin = lonMid;
117
+ } else {
118
+ idx = idx * 2;
119
+ lonMax = lonMid;
120
+ }
121
+ } else {
122
+ // bisect N-S latitude
123
+ const latMid = (latMin + latMax) / 2;
124
+ if (lat > latMid) {
125
+ idx = idx * 2 + 1;
126
+ latMin = latMid;
127
+ } else {
128
+ idx = idx * 2;
129
+ latMax = latMid;
130
+ }
131
+ }
132
+ evenBit = !evenBit;
133
+
134
+ if (++bit === 5) {
135
+ // 5 bits gives us a character: append it and start over
136
+ geohash += Geohash.base32.charAt(idx);
137
+ bit = 0;
138
+ idx = 0;
139
+ }
140
+ }
141
+
142
+ return geohash;
143
+ }
144
+
145
+ /**
146
+ * Decode geohash to latitude/longitude (location is approximate centre of geohash cell,
147
+ * to reasonable precision).
148
+ *
149
+ * @param {string} geohash - Geohash string to be converted to latitude/longitude.
150
+ * @returns {{lat:number, lon:number}} (Center of) geohashed location.
151
+ * @throws Invalid geohash.
152
+ *
153
+ * @example
154
+ * var latlon = Geohash.decode("u120fxw"); // latlon: { lat: 52.205, lon: 0.1188 }
155
+ */
156
+ decode(geohash) {
157
+
158
+ const bounds = this.bounds(geohash); // <-- the hard work
159
+ // now just determine the centre of the cell...
160
+
161
+ const latMin = bounds.sw.lat;
162
+ const lonMin = bounds.sw.lon;
163
+ const latMax = bounds.ne.lat;
164
+ const lonMax = bounds.ne.lon;
165
+
166
+ // cell centre
167
+ let lat = (latMin + latMax) / 2;
168
+ let lon = (lonMin + lonMax) / 2;
169
+
170
+ // round to close to centre without excessive precision: ⌊2-log10(Δ°)⌋ decimal places
171
+ lat = Number(lat.toFixed(Math.floor(2 - Math.log(latMax - latMin) / Math.LN10)));
172
+ lon = Number(lon.toFixed(Math.floor(2 - Math.log(lonMax - lonMin) / Math.LN10)));
173
+
174
+ return { lat, lon };
175
+ }
176
+
177
+ /**
178
+ * Returns SW/NE latitude/longitude bounds of specified geohash.
179
+ *
180
+ * @param {string} geohash - Cell that bounds are required of.
181
+ * @returns {{sw: {lat: number, lon: number}, ne: {lat: number, lon: number}}}
182
+ * @throws Invalid geohash.
183
+ */
184
+ bounds(geohash) {
185
+ if (geohash.length === 0) throw new Error("Invalid geohash");
186
+
187
+ geohash = geohash.toLowerCase();
188
+
189
+ let evenBit = true;
190
+ let latMin = -90;
191
+ let latMax = 90;
192
+ let lonMin = -180;
193
+ let lonMax = 180;
194
+
195
+ for (let i = 0; i < geohash.length; i++) {
196
+ const chr = geohash.charAt(i);
197
+ const idx = Geohash.base32.indexOf(chr);
198
+ if (idx === -1) throw new Error("Invalid geohash");
199
+
200
+ for (let n = 4; n >= 0; n--) {
201
+ const bitN = idx >> n & 1;
202
+ if (evenBit) {
203
+ // longitude
204
+ const lonMid = (lonMin + lonMax) / 2;
205
+ if (bitN === 1) {
206
+ lonMin = lonMid;
207
+ } else {
208
+ lonMax = lonMid;
209
+ }
210
+ } else {
211
+ // latitude
212
+ const latMid = (latMin + latMax) / 2;
213
+ if (bitN === 1) {
214
+ latMin = latMid;
215
+ } else {
216
+ latMax = latMid;
217
+ }
218
+ }
219
+ evenBit = !evenBit;
220
+ }
221
+ }
222
+
223
+ const bounds = {
224
+ sw: { lat: latMin, lon: lonMin },
225
+ ne: { lat: latMax, lon: lonMax }
226
+ };
227
+
228
+ return bounds;
229
+ }
230
+
231
+ /**
232
+ * Determines adjacent cell in given direction.
233
+ *
234
+ * @param geohash - Cell to which adjacent cell is required.
235
+ * @param direction - Direction from geohash (N/S/E/W).
236
+ * @returns {string} Geocode of adjacent cell.
237
+ * @throws Invalid geohash.
238
+ */
239
+ adjacent(geohash, direction) {
240
+ // based on github.com/davetroy/geohash-js
241
+
242
+ geohash = geohash.toLowerCase();
243
+ direction = direction.toLowerCase();
244
+
245
+ if (geohash.length === 0) throw new Error("Invalid geohash");
246
+ if ("nsew".indexOf(direction) === -1) throw new Error("Invalid direction");
247
+
248
+ const neighbour = {
249
+ n: ["p0r21436x8zb9dcf5h7kjnmqesgutwvy", "bc01fg45238967deuvhjyznpkmstqrwx"],
250
+ s: ["14365h7k9dcfesgujnmqp0r2twvyx8zb", "238967debc01fg45kmstqrwxuvhjyznp"],
251
+ e: ["bc01fg45238967deuvhjyznpkmstqrwx", "p0r21436x8zb9dcf5h7kjnmqesgutwvy"],
252
+ w: ["238967debc01fg45kmstqrwxuvhjyznp", "14365h7k9dcfesgujnmqp0r2twvyx8zb"]
253
+ };
254
+ const border = {
255
+ n: ["prxz", "bcfguvyz"],
256
+ s: ["028b", "0145hjnp"],
257
+ e: ["bcfguvyz", "prxz"],
258
+ w: ["0145hjnp", "028b"]
259
+ };
260
+
261
+ const lastCh = geohash.slice(-1); // last character of hash
262
+ let parent = geohash.slice(0, -1); // hash without last character
263
+
264
+ const type = geohash.length % 2;
265
+
266
+ // check for edge-cases which don"t share common prefix
267
+ if (border[direction][type].indexOf(lastCh) !== -1 && parent !== "") {
268
+ parent = this.adjacent(parent, direction);
269
+ }
270
+
271
+ // append letter for direction to parent
272
+ return parent + Geohash.base32.charAt(neighbour[direction][type].indexOf(lastCh));
273
+ }
274
+
275
+ /**
276
+ * Returns all 8 adjacent cells to specified geohash.
277
+ *
278
+ * @param {string} geohash - Geohash neighbours are required of.
279
+ * @returns {{n,ne,e,se,s,sw,w,nw: string}}
280
+ * @throws Invalid geohash.
281
+ */
282
+ neighbours(geohash) {
283
+ return {
284
+ n: this.adjacent(geohash, "n"),
285
+ ne: this.adjacent(this.adjacent(geohash, "n"), "e"),
286
+ e: this.adjacent(geohash, "e"),
287
+ se: this.adjacent(this.adjacent(geohash, "s"), "e"),
288
+ s: this.adjacent(geohash, "s"),
289
+ sw: this.adjacent(this.adjacent(geohash, "s"), "w"),
290
+ w: this.adjacent(geohash, "w"),
291
+ nw: this.adjacent(this.adjacent(geohash, "n"), "w")
292
+ };
293
+ }
294
+
295
+ // HPCC Extensions ---
296
+ contained(w, n, e, s, precision) {
297
+ if (isNaN(n) || n >= 90) n = 89;
298
+ if (isNaN(e) || e > 180) e = 180;
299
+ if (isNaN(s) || s <= -90) s = -89;
300
+ if (isNaN(w) || w < -180) w = -180;
301
+ precision = precision || 1;
302
+ const geoHashNW = this.encode(n, w, precision);
303
+ const geoHashNE = this.encode(n, e, precision);
304
+ const geoHashSE = this.encode(s, e, precision);
305
+ let currRowHash = geoHashNW;
306
+ let col = 0;
307
+ let maxCol = -1;
308
+ const geoHashes = [geoHashNW, geoHashSE];
309
+ let currHash = this.adjacent(geoHashNW, "e");
310
+ while (currHash !== geoHashSE) {
311
+ geoHashes.push(currHash);
312
+ ++col;
313
+ if (currHash === geoHashNE || maxCol === col) {
314
+ maxCol = col + 1;
315
+ col = 0;
316
+ currHash = this.adjacent(currRowHash, "s");
317
+ currRowHash = currHash;
318
+ } else {
319
+ currHash = this.adjacent(currHash, "e");
320
+ }
321
+ }
322
+ return geoHashes;
323
+ }
324
+
325
+ calculateWidthDegrees(n) {
326
+ let a;
327
+ if (n % 2 === 0) {
328
+ a = -1;
329
+ } else {
330
+ a = -0.5;
331
+ }
332
+ const result = 180 / Math.pow(2, 2.5 * n + a);
333
+ return result;
334
+ }
335
+
336
+ width(n) {
337
+ const parity = n % 2;
338
+ return 180 / (2 ^ (((5 * n + parity) / 2) - 1));
339
+ }
340
+
341
+ }
342
+
343
+ function multiplex(streams) {
344
+ const n = streams.length;
345
+ return {
346
+ point(x, y) { let i = -1; while (++i < n) streams[i].point(x, y); },
347
+ sphere() { let i = -1; while (++i < n) streams[i].sphere(); },
348
+ lineStart() { let i = -1; while (++i < n) streams[i].lineStart(); },
349
+ lineEnd() { let i = -1; while (++i < n) streams[i].lineEnd(); },
350
+ polygonStart() { let i = -1; while (++i < n) streams[i].polygonStart(); },
351
+ polygonEnd() { let i = -1; while (++i < n) streams[i].polygonEnd(); }
352
+ };
353
+ }
354
+
355
+ // A modified d3.geo.albersUsa to include Puerto Rico.
356
+ export function albersUsaPr() {
357
+ let cache;
358
+ let cacheStream;
359
+
360
+ const ε = 1e-6;
361
+
362
+ const lower48 = geoAlbers();
363
+ let lower48Point;
364
+
365
+ // EPSG:3338
366
+ const alaska = geoConicEqualArea()
367
+ .rotate([154, 0])
368
+ .center([-2, 58.5])
369
+ .parallels([55, 65]);
370
+ let alaskaPoint;
371
+
372
+ // ESRI:102007
373
+ const hawaii = geoConicEqualArea()
374
+ .rotate([157, 0])
375
+ .center([-3, 19.9])
376
+ .parallels([8, 18]);
377
+ let hawaiiPoint;
378
+
379
+ // XXX? You should check that this is a standard PR projection!
380
+ const puertoRico = geoConicEqualArea()
381
+ .rotate([66, 0])
382
+ .center([0, 18])
383
+ .parallels([8, 18]);
384
+ let puertoRicoPoint;
385
+
386
+ let point;
387
+ const pointStream = { point: (x, y) => { point = [x, y]; } } as GeoStream;
388
+
389
+ const albersUsa: any = function (coordinates) {
390
+ const x = coordinates[0];
391
+ const y = coordinates[1];
392
+ point = null;
393
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
394
+ (lower48Point.point(x, y), point) ||
395
+ (alaskaPoint.point(x, y), point) ||
396
+ (hawaiiPoint.point(x, y), point) ||
397
+ (puertoRicoPoint.point(x, y), point);
398
+ return point;
399
+ };
400
+
401
+ albersUsa.invert = function (coordinates) {
402
+ const k = lower48.scale();
403
+ const t = lower48.translate();
404
+ const x = (coordinates[0] - t[0]) / k;
405
+ const y = (coordinates[1] - t[1]) / k;
406
+ return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
407
+ : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
408
+ : y >= 0.204 && y < 0.234 && x >= 0.320 && x < 0.380 ? puertoRico
409
+ : lower48).invert(coordinates);
410
+ };
411
+
412
+ // A naïve multi-projection stream.
413
+ // The projections must have mutually exclusive clip regions on the sphere,
414
+ // as this will avoid emitting interleaving lines and polygons.
415
+ albersUsa.stream = function (stream) {
416
+ return cache && cacheStream === stream ? cache : cache = multiplex([
417
+ lower48.stream(cacheStream = stream),
418
+ alaska.stream(stream),
419
+ hawaii.stream(stream),
420
+ puertoRico.stream(stream)
421
+ ]);
422
+ };
423
+
424
+ albersUsa.precision = function (_) {
425
+ if (!arguments.length) return lower48.precision();
426
+ lower48.precision(_);
427
+ alaska.precision(_);
428
+ hawaii.precision(_);
429
+ puertoRico.precision(_);
430
+ return reset();
431
+ };
432
+
433
+ albersUsa.scale = function (_) {
434
+ if (!arguments.length) return lower48.scale();
435
+ lower48.scale(_);
436
+ alaska.scale(_ * 0.35);
437
+ hawaii.scale(_);
438
+ puertoRico.scale(_);
439
+ return albersUsa.translate(lower48.translate());
440
+ };
441
+
442
+ albersUsa.translate = function (_) {
443
+ if (!arguments.length) return lower48.translate();
444
+ const k = lower48.scale();
445
+ const x = +_[0];
446
+ const y = +_[1];
447
+
448
+ lower48Point = lower48
449
+ .translate(_)
450
+ .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
451
+ .stream(pointStream);
452
+
453
+ alaskaPoint = alaska
454
+ .translate([x - 0.307 * k, y + 0.201 * k])
455
+ .clipExtent([[x - 0.425 * k + ε, y + 0.120 * k + ε], [x - 0.214 * k - ε, y + 0.234 * k - ε]])
456
+ .stream(pointStream);
457
+
458
+ hawaiiPoint = hawaii
459
+ .translate([x - 0.205 * k, y + 0.212 * k])
460
+ .clipExtent([[x - 0.214 * k + ε, y + 0.166 * k + ε], [x - 0.115 * k - ε, y + 0.234 * k - ε]])
461
+ .stream(pointStream);
462
+
463
+ puertoRicoPoint = puertoRico
464
+ .translate([x + 0.350 * k, y + 0.224 * k])
465
+ .clipExtent([[x + 0.320 * k, y + 0.204 * k], [x + 0.380 * k, y + 0.234 * k]])
466
+ .stream(pointStream);
467
+
468
+ return reset();
469
+ };
470
+
471
+ albersUsa.fitExtent = function (extent, object) {
472
+ return fitExtent(albersUsa, extent, object);
473
+ };
474
+
475
+ albersUsa.fitSize = function (size, object) {
476
+ return fitSize(albersUsa, size, object);
477
+ };
478
+
479
+ function reset() {
480
+ cache = cacheStream = null;
481
+ return albersUsa;
482
+ }
483
+ return albersUsa.scale(1070);
484
+ }