mapshaper 0.7.44 → 0.7.46

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/mapshaper.js CHANGED
@@ -78,7 +78,7 @@
78
78
  return obj === Object(obj); // via underscore
79
79
  }
80
80
 
81
- function clamp$2(val, min, max) {
81
+ function clamp$4(val, min, max) {
82
82
  return val < min ? min : (val > max ? max : val);
83
83
  }
84
84
 
@@ -261,36 +261,6 @@
261
261
  };
262
262
  }
263
263
 
264
- // Call @iter on each member of an array (similar to Array#reduce(iter))
265
- // iter: function(memo, item, callback)
266
- // Call @done when all members have been processed or if an error occurs
267
- // done: function(err, memo)
268
- // @memo: Initial value
269
- //
270
- function reduceAsync(arr, memo, iter, done) {
271
- var call = typeof setImmediate == 'undefined' ? setTimeout : setImmediate;
272
- var i=0;
273
- next(null, memo);
274
-
275
- function next(err, memo) {
276
- // Detach next operation from call stack to prevent overflow
277
- // Don't use setTimeout(, 0) if setImmediate is available
278
- // (setTimeout() can introduce a long delay if previous operation was slow,
279
- // as of Node 0.10.32 -- a bug?)
280
- if (err) {
281
- return done(err, null);
282
- }
283
- call(function() {
284
- if (i < arr.length === false) {
285
- done(null, memo);
286
- } else {
287
- iter(memo, arr[i++], next);
288
- }
289
- }, 0);
290
- }
291
- }
292
-
293
-
294
264
  // Append elements of @src array to @dest array
295
265
  function merge$1(dest, src) {
296
266
  if (!isArray(dest) || !isArray(src)) {
@@ -751,7 +721,7 @@
751
721
  function findValueByRank(arr, rank) {
752
722
  if (!arr.length || rank < 1 || rank > arr.length) error$1("[findValueByRank()] invalid input");
753
723
 
754
- rank = clamp$2(rank | 0, 1, arr.length);
724
+ rank = clamp$4(rank | 0, 1, arr.length);
755
725
  var k = rank - 1, // conv. rank to array index
756
726
  n = arr.length,
757
727
  l = 0,
@@ -1125,7 +1095,7 @@
1125
1095
  // self-import and the resulting Rollup circular-dependency warning.
1126
1096
  var utils = {
1127
1097
  addThousandsSep, addslashes, arrayToIndex,
1128
- clamp: clamp$2, cleanNumericString, contains, copyElements, countValues, createBuffer,
1098
+ clamp: clamp$4, cleanNumericString, contains, copyElements, countValues, createBuffer,
1129
1099
  defaults, difference,
1130
1100
  endsWith, every, expandoBuffer, extend: extend$1, extendBuffer,
1131
1101
  find: find$1, findMedian, findQuantile, findRankByValue, findStringPrefix,
@@ -1145,7 +1115,7 @@
1145
1115
  parseIntlNumber, parseNumber: parseNumber$1, parseString, pickOne, pluck,
1146
1116
  pluralSuffix, promisify,
1147
1117
  quicksort: quicksort$1, quicksortPartition,
1148
- range, reduceAsync, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
1118
+ range, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
1149
1119
  replaceArray, rpad, rtrim,
1150
1120
  shuffle, some, sortArrayIndex, sortOn, splitLines, sum: sum$1,
1151
1121
  toArray: toArray$1, toBuffer, trim, trimQuotes,
@@ -2083,12 +2053,12 @@
2083
2053
  // TODO: remove this constant, use actual data from dataset CRS,
2084
2054
  // also consider using ellipsoidal formulas where greater accuracy might be important.
2085
2055
  var R$3 = WGS84.SEMIMAJOR_AXIS;
2086
- var D2R$1 = Math.PI / 180;
2087
- var R2D$1 = 180 / Math.PI;
2056
+ var D2R$7 = Math.PI / 180;
2057
+ var R2D$6 = 180 / Math.PI;
2088
2058
 
2089
2059
  // Equirectangular projection
2090
2060
  function degreesToMeters(deg) {
2091
- return deg * D2R$1 * R$3;
2061
+ return deg * D2R$7 * R$3;
2092
2062
  }
2093
2063
 
2094
2064
  function distance3D(ax, ay, az, bx, by, bz) {
@@ -2104,7 +2074,7 @@
2104
2074
  return dx * dx + dy * dy;
2105
2075
  }
2106
2076
 
2107
- function distance2D(ax, ay, bx, by) {
2077
+ function distance2D$1(ax, ay, bx, by) {
2108
2078
  var dx = ax - bx,
2109
2079
  dy = ay - by;
2110
2080
  return Math.sqrt(dx * dx + dy * dy);
@@ -2235,16 +2205,16 @@
2235
2205
 
2236
2206
  function xyzToLngLat(x, y, z, p) {
2237
2207
  var d = distance3D(0, 0, 0, x, y, z); // normalize
2238
- var lat = Math.asin(z / d) / D2R$1;
2239
- var lng = Math.atan2(y / d, x / d) / D2R$1;
2208
+ var lat = Math.asin(z / d) / D2R$7;
2209
+ var lng = Math.atan2(y / d, x / d) / D2R$7;
2240
2210
  p[0] = lng;
2241
2211
  p[1] = lat;
2242
2212
  }
2243
2213
 
2244
2214
  function lngLatToXYZ(lng, lat, p) {
2245
2215
  var cosLat;
2246
- lng *= D2R$1;
2247
- lat *= D2R$1;
2216
+ lng *= D2R$7;
2217
+ lat *= D2R$7;
2248
2218
  cosLat = Math.cos(lat);
2249
2219
  p[0] = Math.cos(lng) * cosLat * R$3;
2250
2220
  p[1] = Math.sin(lng) * cosLat * R$3;
@@ -2282,7 +2252,7 @@
2282
2252
  }
2283
2253
 
2284
2254
  function cosine(ax, ay, bx, by, cx, cy) {
2285
- var den = distance2D(ax, ay, bx, by) * distance2D(bx, by, cx, cy),
2255
+ var den = distance2D$1(ax, ay, bx, by) * distance2D$1(bx, by, cx, cy),
2286
2256
  cos = 0;
2287
2257
  if (den > 0) {
2288
2258
  cos = ((ax - bx) * (cx - bx) + (ay - by) * (cy - by)) / den;
@@ -2364,9 +2334,9 @@
2364
2334
 
2365
2335
  var Geom = /*#__PURE__*/Object.freeze({
2366
2336
  __proto__: null,
2367
- D2R: D2R$1,
2337
+ D2R: D2R$7,
2368
2338
  R: R$3,
2369
- R2D: R2D$1,
2339
+ R2D: R2D$6,
2370
2340
  bearing: bearing,
2371
2341
  bearing2D: bearing2D,
2372
2342
  containsBounds: containsBounds,
@@ -2374,7 +2344,7 @@
2374
2344
  cosine: cosine,
2375
2345
  cosine3D: cosine3D,
2376
2346
  degreesToMeters: degreesToMeters,
2377
- distance2D: distance2D,
2347
+ distance2D: distance2D$1,
2378
2348
  distance3D: distance3D,
2379
2349
  distanceSq: distanceSq,
2380
2350
  distanceSq3D: distanceSq3D,
@@ -2525,7 +2495,7 @@
2525
2495
  if (spherical && arcs.isPlanar()) {
2526
2496
  error("Expected lat-long coordinates");
2527
2497
  }
2528
- calcLen = spherical ? greatCircleDistance : distance2D;
2498
+ calcLen = spherical ? greatCircleDistance : distance2D$1;
2529
2499
  len = 0;
2530
2500
  for (var i=0, n=path.length; i<n; i++) {
2531
2501
  arcs.forEachArcSegment(path[i], addSegLen);
@@ -3311,8 +3281,8 @@
3311
3281
  // angle and seg length ratio thresholds were found by comparing
3312
3282
  // fast and robust outputs on sample data
3313
3283
  if (innerAngle(ax, ay, bx, by, cx, cy, dx, dy) < 0.1) return true;
3314
- var len1 = distance2D(ax, ay, bx, by);
3315
- var len2 = distance2D(cx, cy, dx, dy);
3284
+ var len1 = distance2D$1(ax, ay, bx, by);
3285
+ var len2 = distance2D$1(cx, cy, dx, dy);
3316
3286
  var ratio = (len1 < len2 ? len1 / len2 : len2 / len1) || 0;
3317
3287
  if (ratio < 0.001) return true;
3318
3288
  return false;
@@ -3405,7 +3375,7 @@
3405
3375
  }
3406
3376
 
3407
3377
  function snapIfCloser(p, minDist, x, y, x2, y2) {
3408
- var dist = distance2D(x, y, x2, y2);
3378
+ var dist = distance2D$1(x, y, x2, y2);
3409
3379
  if (dist < minDist) {
3410
3380
  minDist = dist;
3411
3381
  p[0] = x2;
@@ -6287,7 +6257,2291 @@
6287
6257
  probablyDecimalDegreeBounds: probablyDecimalDegreeBounds
6288
6258
  });
6289
6259
 
6260
+ /*
6261
+ * Polyhedral unfolding code adapted from d3-geo-polygon.
6262
+ *
6263
+ * Copyright 2017-2024 Mike Bostock
6264
+ * ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
6265
+ */
6266
+
6267
+ var D2R$6 = Math.PI / 180;
6268
+ var R2D$5 = 180 / Math.PI;
6269
+ var EPS$2 = 1e-12;
6270
+
6271
+ // Build a forward-only spherical polyhedral projection.
6272
+ //
6273
+ // config.faces contains spherical polygons in degrees. config.parents defines
6274
+ // a spanning tree of attached faces. faceProjector(face) returns a local
6275
+ // projection accepting radians and returning planar coordinates.
6276
+ function createPolyhedralProjection(config) {
6277
+ var faces = config.faces.map(function(coords, i) {
6278
+ return createFace(coords, i, config.faceSites && config.faceSites[i]);
6279
+ });
6280
+ var parents = config.parents;
6281
+ var attachedPairs = new Set();
6282
+ var faceProjector = config.faceProjector;
6283
+
6284
+ faces.forEach(function(face) {
6285
+ face.project = faceProjector(face);
6286
+ });
6287
+
6288
+ faces.forEach(function(face) {
6289
+ initFaceTransform(face.id);
6290
+ });
6291
+
6292
+ function initFaceTransform(faceId) {
6293
+ var parentId = parents[faceId];
6294
+ var face = faces[faceId];
6295
+ if (face.transform) return;
6296
+ if (parentId < 0) {
6297
+ face.transform = identityMatrix();
6298
+ return;
6299
+ }
6300
+ initFaceTransform(parentId);
6301
+ var parent = faces[parentId];
6302
+ var shared = findSharedEdge(face.coords, parent.coords);
6303
+ if (!shared) {
6304
+ throw new Error('Invalid polyhedral face tree');
6305
+ }
6306
+ var childEdge = shared.map(function(p) {
6307
+ return face.project(p[0] * D2R$6, p[1] * D2R$6);
6308
+ });
6309
+ var parentEdge = shared.map(function(p) {
6310
+ return parent.project(p[0] * D2R$6, p[1] * D2R$6);
6311
+ });
6312
+ face.transform = multiplyMatrices(
6313
+ parent.transform,
6314
+ getEdgeTransform(parentEdge, childEdge)
6315
+ );
6316
+ attachedPairs.add(pairKey(faceId, parentId));
6317
+ }
6318
+
6319
+ var rootEdge = findLongestEdge(faces[0].coords);
6320
+ var rootPlanarEdge = rootEdge.map(function(p) {
6321
+ return faces[0].project(p[0] * D2R$6, p[1] * D2R$6);
6322
+ });
6323
+ var sphericalEdgeLength = angularDistance(rootEdge[0], rootEdge[1]);
6324
+ var planarEdgeLength = distance2D(rootPlanarEdge[0], rootPlanarEdge[1]);
6325
+ var scale = sphericalEdgeLength / planarEdgeLength;
6326
+ var planarAngle = (config.angle || 0) * D2R$6;
6327
+ var edgeIndex = indexEdges(faces, attachedPairs);
6328
+ var outline = buildOutline(faces, attachedPairs).map(function(ring) {
6329
+ return ring.map(transformOutputPoint);
6330
+ });
6331
+ var bounds = getBounds(outline);
6332
+ var centerX = (bounds[0] + bounds[2]) / 2;
6333
+ var centerY = (bounds[1] + bounds[3]) / 2;
6334
+
6335
+ // Centering is calculated after scale and planar rotation.
6336
+ outline.forEach(function(ring) {
6337
+ ring.forEach(function(p) {
6338
+ p[0] -= centerX;
6339
+ p[1] -= centerY;
6340
+ });
6341
+ });
6342
+
6343
+ return {
6344
+ faces: faces,
6345
+ outline: outline,
6346
+ forward: forward,
6347
+ findFace: findFace,
6348
+ findTransitionRegion: findTransitionRegion,
6349
+ getTopology: getTopology
6350
+ };
6351
+
6352
+ function forward(lam, phi) {
6353
+ var rotated = rotateRadians(
6354
+ lam,
6355
+ phi,
6356
+ config.rotation[0] * D2R$6,
6357
+ config.rotation[1] * D2R$6,
6358
+ config.rotation[2] * D2R$6
6359
+ );
6360
+ var face = findFaceRotated(rotated[0], rotated[1]);
6361
+ if (!face) return null;
6362
+ var p = face.project(rotated[0], rotated[1]);
6363
+ p = applyMatrix(face.transform, p);
6364
+ p = transformOutputPoint(p);
6365
+ p[0] -= centerX;
6366
+ p[1] -= centerY;
6367
+ return p;
6368
+ }
6369
+
6370
+ function findFace(lam, phi) {
6371
+ var rotated = rotateRadians(
6372
+ lam,
6373
+ phi,
6374
+ config.rotation[0] * D2R$6,
6375
+ config.rotation[1] * D2R$6,
6376
+ config.rotation[2] * D2R$6
6377
+ );
6378
+ var face = findFaceRotated(rotated[0], rotated[1]);
6379
+ return face ? face.id : -1;
6380
+ }
6381
+
6382
+ function findTransitionRegion(lam, phi) {
6383
+ var rotated = rotateRadians(
6384
+ lam,
6385
+ phi,
6386
+ config.rotation[0] * D2R$6,
6387
+ config.rotation[1] * D2R$6,
6388
+ config.rotation[2] * D2R$6
6389
+ );
6390
+ var face = findFaceRotated(rotated[0], rotated[1]);
6391
+ var region;
6392
+ if (!face) return '-1';
6393
+ region = face.project.findRegion ?
6394
+ face.project.findRegion(rotated[0], rotated[1]) :
6395
+ 0;
6396
+ return face.id + ':' + region;
6397
+ }
6398
+
6399
+ function findFaceRotated(lam, phi) {
6400
+ var faceId;
6401
+ if (config.findFace) {
6402
+ faceId = config.findFace(lam, phi);
6403
+ return faceId >= 0 ? faces[faceId] : null;
6404
+ }
6405
+ var p = radiansToVector$1(lam, phi);
6406
+ for (var i = 0; i < faces.length; i++) {
6407
+ if (faceContainsVector(faces[i], p)) return faces[i];
6408
+ }
6409
+ return null;
6410
+ }
6411
+
6412
+ function transformOutputPoint(p) {
6413
+ var x = p[0] * scale;
6414
+ // Local unfolding uses screen-oriented y coordinates to match the D3
6415
+ // Airocean face layout; return conventional projected coordinates (y up).
6416
+ var y = -p[1] * scale;
6417
+ if (planarAngle) {
6418
+ return [
6419
+ x * Math.cos(planarAngle) - y * Math.sin(planarAngle),
6420
+ x * Math.sin(planarAngle) + y * Math.cos(planarAngle)
6421
+ ];
6422
+ }
6423
+ return [x, y];
6424
+ }
6425
+
6426
+ function getTopology(lon0) {
6427
+ var seams = edgeIndex.map(function(edge) {
6428
+ var endpoints = edge.points.map(function(p) {
6429
+ var q = rotateRadians(
6430
+ p[0] * D2R$6,
6431
+ p[1] * D2R$6,
6432
+ config.rotation[0] * D2R$6,
6433
+ config.rotation[1] * D2R$6,
6434
+ config.rotation[2] * D2R$6,
6435
+ true
6436
+ );
6437
+ return [normalizeLongitude$2(q[0] * R2D$5 + lon0), q[1] * R2D$5];
6438
+ });
6439
+ var paths = splitPathAtAntimeridian$2(
6440
+ interpolateGreatCircle(endpoints[0], endpoints[1], 0.05)
6441
+ );
6442
+ paths.forEach(function(path) {
6443
+ // The mask must be wider than the tiny chord error between samples.
6444
+ path.mask_width = 4e-5;
6445
+ });
6446
+ return {
6447
+ type: edge.attached ? 'attached' : 'cut',
6448
+ faces: edge.faces,
6449
+ paths: paths
6450
+ };
6451
+ });
6452
+ var regionPairs = new Map();
6453
+ edgeIndex.forEach(function(edge) {
6454
+ if (edge.faces.length == 2) {
6455
+ regionPairs.set(pairKey(edge.faces[0], edge.faces[1]), edge.attached);
6456
+ }
6457
+ });
6458
+ return {
6459
+ regions: faces.map(function(face) {
6460
+ return {id: face.id};
6461
+ }),
6462
+ seams: seams,
6463
+ findRegion: function(lon, lat) {
6464
+ var lam = normalizeLongitude$2(lon - lon0) * D2R$6;
6465
+ return findFace(lam, lat * D2R$6);
6466
+ },
6467
+ findTransitionRegion: function(lon, lat) {
6468
+ var lam = normalizeLongitude$2(lon - lon0) * D2R$6;
6469
+ return findTransitionRegion(lam, lat * D2R$6);
6470
+ },
6471
+ regionsAreAttached: function(a, b) {
6472
+ return regionPairs.get(pairKey(a, b)) === true;
6473
+ },
6474
+ outline: outline.map(function(ring) {
6475
+ return ring.map(function(p) {
6476
+ return p.concat();
6477
+ });
6478
+ })
6479
+ };
6480
+ }
6481
+ }
6482
+
6483
+ function rotateSphericalRadians(lam, phi, rotation, invert) {
6484
+ return rotateRadians(
6485
+ lam,
6486
+ phi,
6487
+ (rotation[0] || 0) * D2R$6,
6488
+ (rotation[1] || 0) * D2R$6,
6489
+ (rotation[2] || 0) * D2R$6,
6490
+ invert
6491
+ );
6492
+ }
6493
+
6494
+ function createFace(coords, id, site) {
6495
+ var vectors = coords.map(function(p) {
6496
+ return degreesToVector$2(p[0], p[1]);
6497
+ });
6498
+ var inside = normalizeVector$2(vectors.reduce(function(sum, p) {
6499
+ sum[0] += p[0];
6500
+ sum[1] += p[1];
6501
+ sum[2] += p[2];
6502
+ return sum;
6503
+ }, [0, 0, 0]));
6504
+ var edges = [];
6505
+ for (var i = 0; i < vectors.length; i++) {
6506
+ var a = vectors[i];
6507
+ var b = vectors[(i + 1) % vectors.length];
6508
+ var normal = cross$2(a, b);
6509
+ edges.push({
6510
+ normal: normal,
6511
+ sign: dot$2(normal, inside) < 0 ? -1 : 1
6512
+ });
6513
+ }
6514
+ return {
6515
+ id: id,
6516
+ coords: coords,
6517
+ vectors: vectors,
6518
+ edges: edges,
6519
+ centroid: site || vectorToDegrees$2(inside)
6520
+ };
6521
+ }
6522
+
6523
+ function faceContainsVector(face, p) {
6524
+ for (var i = 0; i < face.edges.length; i++) {
6525
+ var edge = face.edges[i];
6526
+ if (dot$2(edge.normal, p) * edge.sign < -EPS$2) return false;
6527
+ }
6528
+ return true;
6529
+ }
6530
+
6531
+ function indexEdges(faces, attachedPairs) {
6532
+ var index = new Map();
6533
+ faces.forEach(function(face) {
6534
+ for (var i = 0; i < face.coords.length; i++) {
6535
+ var a = face.coords[i];
6536
+ var b = face.coords[(i + 1) % face.coords.length];
6537
+ var key = edgeKey(a, b);
6538
+ var edge = index.get(key);
6539
+ if (!edge) {
6540
+ edge = {points: [a, b], faces: []};
6541
+ index.set(key, edge);
6542
+ }
6543
+ edge.faces.push(face.id);
6544
+ }
6545
+ });
6546
+ return Array.from(index.values()).map(function(edge) {
6547
+ edge.attached = edge.faces.length == 2 &&
6548
+ attachedPairs.has(pairKey(edge.faces[0], edge.faces[1]));
6549
+ return edge;
6550
+ });
6551
+ }
6552
+
6553
+ function buildOutline(faces, attachedPairs) {
6554
+ var edges = [];
6555
+ faces.forEach(function(face) {
6556
+ for (var i = 0; i < face.coords.length; i++) {
6557
+ var a = face.coords[i];
6558
+ var b = face.coords[(i + 1) % face.coords.length];
6559
+ var adjacent = findAdjacentFace(faces, face.id, a, b);
6560
+ if (adjacent >= 0 && attachedPairs.has(pairKey(face.id, adjacent))) continue;
6561
+ edges.push([
6562
+ applyMatrix(face.transform, face.project(a[0] * D2R$6, a[1] * D2R$6)),
6563
+ applyMatrix(face.transform, face.project(b[0] * D2R$6, b[1] * D2R$6))
6564
+ ]);
6565
+ }
6566
+ });
6567
+ return connectEdges(edges);
6568
+ }
6569
+
6570
+ function connectEdges(edges) {
6571
+ var unused = edges.concat();
6572
+ var rings = [];
6573
+ while (unused.length) {
6574
+ var edge = unused.pop();
6575
+ var ring = [edge[0], edge[1]];
6576
+ while (!pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
6577
+ var last = ring[ring.length - 1];
6578
+ var found = -1;
6579
+ var next;
6580
+ for (var i = 0; i < unused.length; i++) {
6581
+ if (pointsAlmostEqual(last, unused[i][0])) {
6582
+ found = i;
6583
+ next = unused[i][1];
6584
+ break;
6585
+ }
6586
+ if (pointsAlmostEqual(last, unused[i][1])) {
6587
+ found = i;
6588
+ next = unused[i][0];
6589
+ break;
6590
+ }
6591
+ }
6592
+ if (found < 0) break;
6593
+ unused.splice(found, 1);
6594
+ ring.push(next);
6595
+ }
6596
+ if (ring.length > 3 && pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
6597
+ ring[ring.length - 1] = ring[0].concat();
6598
+ rings.push(ring);
6599
+ }
6600
+ }
6601
+ return rings;
6602
+ }
6603
+
6604
+ function findAdjacentFace(faces, faceId, a, b) {
6605
+ var key = edgeKey(a, b);
6606
+ for (var i = 0; i < faces.length; i++) {
6607
+ if (i == faceId) continue;
6608
+ var coords = faces[i].coords;
6609
+ for (var j = 0; j < coords.length; j++) {
6610
+ if (edgeKey(coords[j], coords[(j + 1) % coords.length]) == key) return i;
6611
+ }
6612
+ }
6613
+ return -1;
6614
+ }
6615
+
6616
+ function findSharedEdge(a, b) {
6617
+ for (var i = 0; i < a.length; i++) {
6618
+ var p = a[i];
6619
+ for (var j = 0; j < b.length; j++) {
6620
+ var q = a[(i + 1) % a.length];
6621
+ var c = b[j];
6622
+ var d = b[(j + 1) % b.length];
6623
+ if (sameSphericalPoint(p, c) && sameSphericalPoint(q, d) ||
6624
+ sameSphericalPoint(p, d) && sameSphericalPoint(q, c)) {
6625
+ return [p, q];
6626
+ }
6627
+ }
6628
+ }
6629
+ return null;
6630
+ }
6631
+
6632
+ function findLongestEdge(coords) {
6633
+ var longest = null;
6634
+ var max = -Infinity;
6635
+ for (var i = 0; i < coords.length; i++) {
6636
+ var edge = [coords[i], coords[(i + 1) % coords.length]];
6637
+ var length = angularDistance(edge[0], edge[1]);
6638
+ if (length > max) {
6639
+ longest = edge;
6640
+ max = length;
6641
+ }
6642
+ }
6643
+ return longest;
6644
+ }
6645
+
6646
+ function getEdgeTransform(dest, src) {
6647
+ var ux = dest[1][0] - dest[0][0];
6648
+ var uy = dest[1][1] - dest[0][1];
6649
+ var vx = src[1][0] - src[0][0];
6650
+ var vy = src[1][1] - src[0][1];
6651
+ var scale = Math.sqrt((ux * ux + uy * uy) / (vx * vx + vy * vy));
6652
+ var angle = Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
6653
+ var cos = Math.cos(angle) * scale;
6654
+ var sin = Math.sin(angle) * scale;
6655
+ return [
6656
+ cos,
6657
+ sin,
6658
+ dest[0][0] - cos * src[0][0] - sin * src[0][1],
6659
+ -sin,
6660
+ cos,
6661
+ dest[0][1] + sin * src[0][0] - cos * src[0][1]
6662
+ ];
6663
+ }
6664
+
6665
+ function identityMatrix() {
6666
+ return [1, 0, 0, 0, 1, 0];
6667
+ }
6668
+
6669
+ function multiplyMatrices(a, b) {
6670
+ return [
6671
+ a[0] * b[0] + a[1] * b[3],
6672
+ a[0] * b[1] + a[1] * b[4],
6673
+ a[0] * b[2] + a[1] * b[5] + a[2],
6674
+ a[3] * b[0] + a[4] * b[3],
6675
+ a[3] * b[1] + a[4] * b[4],
6676
+ a[3] * b[2] + a[4] * b[5] + a[5]
6677
+ ];
6678
+ }
6679
+
6680
+ function applyMatrix(m, p) {
6681
+ return [
6682
+ m[0] * p[0] + m[1] * p[1] + m[2],
6683
+ m[3] * p[0] + m[4] * p[1] + m[5]
6684
+ ];
6685
+ }
6686
+
6687
+ function rotateRadians(lam, phi, deltaLam, deltaPhi, deltaGamma, invert) {
6688
+ if (invert) {
6689
+ var inv = rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, true);
6690
+ return [normalizeRadians$2(inv[0] - deltaLam), inv[1]];
6691
+ }
6692
+ lam = normalizeRadians$2(lam + deltaLam);
6693
+ return rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, false);
6694
+ }
6695
+
6696
+ // Based on d3-geo spherical rotation (ISC license).
6697
+ function rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, invert) {
6698
+ var cosDeltaPhi = Math.cos(deltaPhi);
6699
+ var sinDeltaPhi = Math.sin(deltaPhi);
6700
+ var cosDeltaGamma = Math.cos(deltaGamma);
6701
+ var sinDeltaGamma = Math.sin(deltaGamma);
6702
+ var cosPhi = Math.cos(phi);
6703
+ var x = Math.cos(lam) * cosPhi;
6704
+ var y = Math.sin(lam) * cosPhi;
6705
+ var z = Math.sin(phi);
6706
+ var k;
6707
+ if (invert) {
6708
+ k = z * cosDeltaGamma - y * sinDeltaGamma;
6709
+ return [
6710
+ Math.atan2(y * cosDeltaGamma + z * sinDeltaGamma,
6711
+ x * cosDeltaPhi + k * sinDeltaPhi),
6712
+ Math.asin(clamp$3(k * cosDeltaPhi - x * sinDeltaPhi, -1, 1))
6713
+ ];
6714
+ }
6715
+ k = z * cosDeltaPhi + x * sinDeltaPhi;
6716
+ return [
6717
+ Math.atan2(y * cosDeltaGamma - k * sinDeltaGamma,
6718
+ x * cosDeltaPhi - z * sinDeltaPhi),
6719
+ Math.asin(clamp$3(k * cosDeltaGamma + y * sinDeltaGamma, -1, 1))
6720
+ ];
6721
+ }
6722
+
6723
+ function interpolateGreatCircle(a, b, interval) {
6724
+ var av = degreesToVector$2(a[0], a[1]);
6725
+ var bv = degreesToVector$2(b[0], b[1]);
6726
+ var angle = Math.acos(clamp$3(dot$2(av, bv), -1, 1));
6727
+ var n = Math.max(1, Math.ceil(angle * R2D$5 / interval));
6728
+ var sinAngle = Math.sin(angle);
6729
+ var points = [];
6730
+ for (var i = 0; i <= n; i++) {
6731
+ var t = i / n;
6732
+ var p;
6733
+ if (sinAngle < EPS$2) {
6734
+ p = av;
6735
+ } else {
6736
+ var ka = Math.sin((1 - t) * angle) / sinAngle;
6737
+ var kb = Math.sin(t * angle) / sinAngle;
6738
+ p = [
6739
+ av[0] * ka + bv[0] * kb,
6740
+ av[1] * ka + bv[1] * kb,
6741
+ av[2] * ka + bv[2] * kb
6742
+ ];
6743
+ }
6744
+ points.push(vectorToDegrees$2(normalizeVector$2(p)));
6745
+ }
6746
+ return points;
6747
+ }
6748
+
6749
+ function splitPathAtAntimeridian$2(path) {
6750
+ var paths = [];
6751
+ var part = [path[0]];
6752
+ for (var i = 1; i < path.length; i++) {
6753
+ var a = path[i - 1];
6754
+ var b = path[i];
6755
+ if (Math.abs(a[0] - b[0]) > 180) {
6756
+ var bLon = b[0] + (b[0] < a[0] ? 360 : -360);
6757
+ var edgeLon = a[0] < 0 ? -180 : 180;
6758
+ var t = (edgeLon - a[0]) / (bLon - a[0]);
6759
+ var lat = a[1] + (b[1] - a[1]) * t;
6760
+ part.push([edgeLon, lat]);
6761
+ paths.push(part);
6762
+ part = [[-edgeLon, lat], b];
6763
+ } else {
6764
+ part.push(b);
6765
+ }
6766
+ }
6767
+ if (part.length > 1) paths.push(part);
6768
+ return paths;
6769
+ }
6770
+
6771
+ function getBounds(rings) {
6772
+ var bounds = [Infinity, Infinity, -Infinity, -Infinity];
6773
+ rings.forEach(function(ring) {
6774
+ ring.forEach(function(p) {
6775
+ bounds[0] = Math.min(bounds[0], p[0]);
6776
+ bounds[1] = Math.min(bounds[1], p[1]);
6777
+ bounds[2] = Math.max(bounds[2], p[0]);
6778
+ bounds[3] = Math.max(bounds[3], p[1]);
6779
+ });
6780
+ });
6781
+ return bounds;
6782
+ }
6783
+
6784
+ function angularDistance(a, b) {
6785
+ return Math.acos(clamp$3(dot$2(degreesToVector$2(a[0], a[1]),
6786
+ degreesToVector$2(b[0], b[1])), -1, 1));
6787
+ }
6788
+
6789
+ function degreesToVector$2(lon, lat) {
6790
+ return radiansToVector$1(lon * D2R$6, lat * D2R$6);
6791
+ }
6792
+
6793
+ function radiansToVector$1(lam, phi) {
6794
+ var cosPhi = Math.cos(phi);
6795
+ return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
6796
+ }
6797
+
6798
+ function vectorToDegrees$2(p) {
6799
+ return [
6800
+ Math.atan2(p[1], p[0]) * R2D$5,
6801
+ Math.asin(clamp$3(p[2], -1, 1)) * R2D$5
6802
+ ];
6803
+ }
6804
+
6805
+ function normalizeVector$2(p) {
6806
+ var k = 1 / Math.sqrt(dot$2(p, p));
6807
+ return [p[0] * k, p[1] * k, p[2] * k];
6808
+ }
6809
+
6810
+ function cross$2(a, b) {
6811
+ return [
6812
+ a[1] * b[2] - a[2] * b[1],
6813
+ a[2] * b[0] - a[0] * b[2],
6814
+ a[0] * b[1] - a[1] * b[0]
6815
+ ];
6816
+ }
6817
+
6818
+ function dot$2(a, b) {
6819
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
6820
+ }
6821
+
6822
+ function distance2D(a, b) {
6823
+ var dx = b[0] - a[0];
6824
+ var dy = b[1] - a[1];
6825
+ return Math.sqrt(dx * dx + dy * dy);
6826
+ }
6827
+
6828
+ function sameSphericalPoint(a, b) {
6829
+ return Math.abs(a[0] - b[0]) < EPS$2 && Math.abs(a[1] - b[1]) < EPS$2;
6830
+ }
6831
+
6832
+ function pointsAlmostEqual(a, b) {
6833
+ return Math.abs(a[0] - b[0]) < 1e-9 && Math.abs(a[1] - b[1]) < 1e-9;
6834
+ }
6835
+
6836
+ function pointKey$1(p) {
6837
+ return p[0].toFixed(12) + ',' + p[1].toFixed(12);
6838
+ }
6839
+
6840
+ function edgeKey(a, b) {
6841
+ var ka = pointKey$1(a);
6842
+ var kb = pointKey$1(b);
6843
+ return ka < kb ? ka + '|' + kb : kb + '|' + ka;
6844
+ }
6845
+
6846
+ function pairKey(a, b) {
6847
+ return a < b ? a + '~' + b : b + '~' + a;
6848
+ }
6849
+
6850
+ function normalizeRadians$2(lam) {
6851
+ while (lam > Math.PI) lam -= Math.PI * 2;
6852
+ while (lam < -Math.PI) lam += Math.PI * 2;
6853
+ return lam;
6854
+ }
6855
+
6856
+ function normalizeLongitude$2(lon) {
6857
+ while (lon > 180) lon -= 360;
6858
+ while (lon < -180) lon += 360;
6859
+ return lon;
6860
+ }
6861
+
6862
+ function clamp$3(val, min, max) {
6863
+ return Math.max(min, Math.min(max, val));
6864
+ }
6865
+
6866
+ /*
6867
+ * Buckminster Fuller's Airocean arrangement of the icosahedron.
6868
+ *
6869
+ * Face layout adapted from d3-geo-polygon (ISC license):
6870
+ * https://github.com/d3/d3-geo-polygon/blob/main/src/airocean.js
6871
+ *
6872
+ * The Gray-Fuller raw transform below is based on Robert W. Gray's published
6873
+ * equations and Philippe Rivière's public-domain clean-room implementation:
6874
+ * https://github.com/d3/d3-geo-polygon/blob/main/src/grayfuller.js
6875
+ */
6876
+
6877
+
6878
+ var D2R$5 = Math.PI / 180;
6879
+ var R2D$4 = 180 / Math.PI;
6880
+ var SQRT3$1 = Math.sqrt(3);
6881
+ var GRAY_Z = Math.sqrt(5 + 2 * Math.sqrt(5)) / Math.sqrt(15);
6882
+ var GRAY_EL = Math.sqrt(8) / Math.sqrt(5 + Math.sqrt(5));
6883
+ var GRAY_DVE = Math.sqrt(3 + Math.sqrt(5)) / Math.sqrt(5 + Math.sqrt(5));
6884
+ var ROTATION = [-83.65929, 25.44458, -87.45184];
6885
+ var PARENTS = [
6886
+ -1, 0, 1, 11, 13,
6887
+ 6, 7, 1, 7, 8,
6888
+ 9, 10, 11, 12, 13,
6889
+ 6, 8, 10, 17, 21,
6890
+ 16, 15, 19, 19
6891
+ ];
6892
+ var engines$2 = {};
6893
+
6894
+ function registerDymaxionProjections(mproj) {
6895
+ // Some unit tests import source modules directly in an ESM context where
6896
+ // optional CommonJS dependencies are intentionally unavailable.
6897
+ if (!mproj) return;
6898
+ register('dymaxion', 'Dymaxion (Gray-Fuller facets)', 'fuller');
6899
+ register('dymaxion2', 'Dymaxion (Gnomonic facets)', 'gnomonic');
6900
+
6901
+ function register(id, name, method) {
6902
+ if (mproj.internal.pj_list[id]) return;
6903
+ mproj.pj_add(function(P) {
6904
+ initDymaxion(P, method);
6905
+ }, id, name);
6906
+ }
6907
+ }
6908
+
6909
+ function getDymaxionEngine(method) {
6910
+ method = method || 'fuller';
6911
+ if (!engines$2[method]) {
6912
+ var data = createAiroceanFaces();
6913
+ engines$2[method] = createPolyhedralProjection({
6914
+ faces: data.faces,
6915
+ faceSites: data.sites,
6916
+ parents: PARENTS,
6917
+ rotation: ROTATION,
6918
+ angle: -60,
6919
+ faceProjector: method == 'gnomonic' ?
6920
+ createGnomonicFaceProjector :
6921
+ createGrayFullerFaceProjector
6922
+ });
6923
+ }
6924
+ return engines$2[method];
6925
+ }
6926
+
6927
+ function initDymaxion(P, method) {
6928
+ var engine = getDymaxionEngine(method);
6929
+ P.es = 0;
6930
+ P.inv = null;
6931
+ P.fwd = function(lp, xy) {
6932
+ var p = engine.forward(lp.lam, lp.phi);
6933
+ if (!p) {
6934
+ xy.x = xy.y = Infinity;
6935
+ } else {
6936
+ xy.x = p[0];
6937
+ xy.y = p[1];
6938
+ }
6939
+ };
6940
+ P.__projection_topology = engine.getTopology(P.lam0 * R2D$4);
6941
+ P.__projected_outline = engine.outline;
6942
+ }
6943
+
6944
+ function createAiroceanFaces() {
6945
+ var theta = Math.atan(0.5) * R2D$4;
6946
+ var vertices = [[0, 90], [0, -90]];
6947
+ var faces;
6948
+ var sites;
6949
+ for (var i = 0; i < 10; i++) {
6950
+ vertices.push([(i * 36 + 180) % 360 - 180, i & 1 ? theta : -theta]);
6951
+ }
6952
+ faces = [
6953
+ [0, 3, 11],
6954
+ [0, 5, 3],
6955
+ [0, 7, 5],
6956
+ [0, 9, 7],
6957
+ [0, 11, 9],
6958
+ [2, 11, 3],
6959
+ [3, 4, 2],
6960
+ [4, 3, 5],
6961
+ [5, 6, 4],
6962
+ [6, 5, 7],
6963
+ [7, 8, 6],
6964
+ [8, 7, 9],
6965
+ [9, 10, 8],
6966
+ [10, 9, 11],
6967
+ [11, 2, 10],
6968
+ [1, 2, 4],
6969
+ [1, 4, 6],
6970
+ [1, 6, 8],
6971
+ [1, 8, 10],
6972
+ [1, 10, 2]
6973
+ ].map(function(ids) {
6974
+ return ids.map(function(id) {
6975
+ return vertices[id];
6976
+ });
6977
+ });
6978
+ sites = faces.map(getSphericalCentroid);
6979
+ splitAiroceanFaces(faces, sites);
6980
+ return {faces: faces, sites: sites};
6981
+ }
6982
+
6983
+ function splitAiroceanFaces(faces, sites) {
6984
+ var face = faces[15];
6985
+ var site = sites[15];
6986
+ var original = face.concat();
6987
+ face[0] = site;
6988
+ faces.push([original[0], site, original[2]]);
6989
+ sites.push(site);
6990
+ faces.push([original[0], original[1], site]);
6991
+ sites.push(site);
6992
+
6993
+ face = faces[14];
6994
+ site = sites[14];
6995
+ original = face.concat();
6996
+ var mid = getGreatCircleMidpoint(face[1], face[2]);
6997
+ face[1] = mid;
6998
+ faces.push([original[0], original[1], mid]);
6999
+ sites.push(site);
7000
+
7001
+ face = faces[19];
7002
+ site = sites[19];
7003
+ original = face.concat();
7004
+ face[1] = mid;
7005
+ faces.push([mid, original[0], original[1]]);
7006
+ sites.push(site);
7007
+ }
7008
+
7009
+ function createGrayFullerFaceProjector(face) {
7010
+ var c = face.centroid;
7011
+ var direction = Math.abs(c[1] - 52.62) < 1 ||
7012
+ Math.abs(c[1] + 10.81) < 1 ? 0 : 60;
7013
+ var rotation = [-c[0], -c[1], direction];
7014
+ return function(lam, phi) {
7015
+ var p = rotateSphericalRadians(lam, phi, rotation);
7016
+ var q = grayFullerRaw(p[0], p[1]);
7017
+ // D3's face projections use screen-oriented local y coordinates; retaining
7018
+ // that convention reproduces the published Airocean unfolding matrices.
7019
+ return [q[0], -q[1]];
7020
+ };
7021
+ }
7022
+
7023
+ function createGnomonicFaceProjector(face) {
7024
+ var c = face.centroid;
7025
+ var rotation = [-c[0], -c[1], 0];
7026
+ return function(lam, phi) {
7027
+ var p = rotateSphericalRadians(lam, phi, rotation);
7028
+ var cosPhi = Math.cos(p[1]);
7029
+ var k = cosPhi * Math.cos(p[0]);
7030
+ return [
7031
+ cosPhi * Math.sin(p[0]) / k,
7032
+ -Math.sin(p[1]) / k
7033
+ ];
7034
+ };
7035
+ }
7036
+
7037
+ function grayFullerRaw(lam, phi) {
7038
+ var cosPhi = Math.cos(phi);
7039
+ var s = GRAY_Z / (cosPhi * Math.cos(lam));
7040
+ var x = cosPhi * Math.sin(lam) * s;
7041
+ var y = Math.sin(phi) * s;
7042
+ var a1p = Math.atan2(2 * y / SQRT3$1 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
7043
+ var a2p = Math.atan2(x - y / SQRT3$1 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
7044
+ var a3p = Math.atan2(GRAY_EL / 3 - x - y / SQRT3$1 - GRAY_EL / 2, GRAY_DVE);
7045
+ return [SQRT3$1 * (a2p - a3p), 2 * a1p - a2p - a3p];
7046
+ }
7047
+
7048
+ function getSphericalCentroid(coords) {
7049
+ var sum = [0, 0, 0];
7050
+ coords.forEach(function(p) {
7051
+ var v = degreesToVector$1(p);
7052
+ sum[0] += v[0];
7053
+ sum[1] += v[1];
7054
+ sum[2] += v[2];
7055
+ });
7056
+ return vectorToDegrees$1(normalize(sum));
7057
+ }
7058
+
7059
+ function getGreatCircleMidpoint(a, b) {
7060
+ var av = degreesToVector$1(a);
7061
+ var bv = degreesToVector$1(b);
7062
+ return vectorToDegrees$1(normalize([
7063
+ av[0] + bv[0],
7064
+ av[1] + bv[1],
7065
+ av[2] + bv[2]
7066
+ ]));
7067
+ }
7068
+
7069
+ function degreesToVector$1(p) {
7070
+ var lam = p[0] * D2R$5;
7071
+ var phi = p[1] * D2R$5;
7072
+ var cosPhi = Math.cos(phi);
7073
+ return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
7074
+ }
7075
+
7076
+ function vectorToDegrees$1(p) {
7077
+ return [
7078
+ Math.atan2(p[1], p[0]) * R2D$4,
7079
+ Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$4
7080
+ ];
7081
+ }
7082
+
7083
+ function normalize(p) {
7084
+ var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
7085
+ return [p[0] * k, p[1] * k, p[2] * k];
7086
+ }
7087
+
7088
+ var Dymaxion = /*#__PURE__*/Object.freeze({
7089
+ __proto__: null,
7090
+ getDymaxionEngine: getDymaxionEngine,
7091
+ registerDymaxionProjections: registerDymaxionProjections
7092
+ });
7093
+
7094
+ /*
7095
+ * Vertex-oriented great-circle "slice-and-dice" projection.
7096
+ *
7097
+ * Based on van Leeuwen and Strebe (2006), with the vector formulation
7098
+ * described by Hall et al. (2020) and implemented independently by DGGAL.
7099
+ * https://doi.org/10.1559/152304006779500687
7100
+ * https://doi.org/10.3390/ijgi9050315
7101
+ */
7102
+
7103
+
7104
+ function createGnomonicProjector(center) {
7105
+ var rotation = [-center[0], -center[1], 0];
7106
+ return function(lam, phi) {
7107
+ var p = rotateSphericalRadians(lam, phi, rotation);
7108
+ var cosPhi = Math.cos(p[1]);
7109
+ var k = cosPhi * Math.cos(p[0]);
7110
+ return [
7111
+ cosPhi * Math.sin(p[0]) / k,
7112
+ -Math.sin(p[1]) / k
7113
+ ];
7114
+ };
7115
+ }
7116
+
7117
+ /*
7118
+ * Cahill-Keyes 12-zone forward transform.
7119
+ *
7120
+ * Implemented in Perl by Mary Jo Graça (2011), ported to D3.js by
7121
+ * Enrico Spinielli (2013), and adapted here from d3-geo-polygon.
7122
+ *
7123
+ * Copyright 2017-2024 Mike Bostock
7124
+ * ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
7125
+ */
7126
+
7127
+ var D2R$4 = Math.PI / 180;
7128
+ var R2D$3 = 180 / Math.PI;
7129
+
7130
+ function createCahillKeyesRaw(mg) {
7131
+ return createCahillKeyesTransform(mg, false);
7132
+ }
7133
+
7134
+ function createCahillKeyesTransform(mg, faceOnly) {
7135
+ var CK = {lengthMG: mg};
7136
+ init();
7137
+
7138
+ return function(lambda, phi) {
7139
+ if (faceOnly) {
7140
+ var lon = lambda * R2D$3;
7141
+ var side = lon < 0 ? -1 : lon > 0 ? 1 : 0;
7142
+ var local = mp2xy(Math.abs(lon), Math.abs(phi * R2D$3));
7143
+ return [local[0], side * local[1]];
7144
+ }
7145
+ var res = ll2mp(lambda * R2D$3, phi * R2D$3);
7146
+ var xy = mp2xy(res[0], res[1]);
7147
+ var p = [xy[0], res[2] * xy[1]];
7148
+ return mj2g(p, res[3]);
7149
+ };
7150
+
7151
+ function init() {
7152
+ var k = Math.sqrt(3);
7153
+ var pointN, lengthMB, lengthMN, lengthNG, pointU;
7154
+ var p73a, lF, lT, lM, l, pointV;
7155
+ CK.lengthMA = 0.094 * CK.lengthMG;
7156
+ CK.lengthParallel0to73At0 = CK.lengthMG / 100;
7157
+ CK.lengthParallel73to90At0 =
7158
+ (CK.lengthMG - CK.lengthMA - CK.lengthParallel0to73At0 * 73) / 17;
7159
+ CK.sin60 = k / 2;
7160
+ CK.cos60 = 0.5;
7161
+ CK.pointM = [0, 0];
7162
+ CK.pointG = [CK.lengthMG, 0];
7163
+ pointN = [CK.lengthMG, CK.lengthMG * Math.tan(30 * D2R$4)];
7164
+ CK.pointA = [CK.lengthMA, 0];
7165
+ CK.pointB = lineIntersection(CK.pointM, 30, CK.pointA, 45);
7166
+ CK.lengthAG = distance(CK.pointA, CK.pointG);
7167
+ CK.lengthAB = distance(CK.pointA, CK.pointB);
7168
+ lengthMB = distance(CK.pointM, CK.pointB);
7169
+ lengthMN = distance(CK.pointM, pointN);
7170
+ lengthNG = distance(pointN, CK.pointG);
7171
+ CK.pointD = interpolate(lengthMB, lengthMN, pointN, CK.pointM);
7172
+ CK.pointF = [CK.lengthMG, lengthNG - lengthMB];
7173
+ CK.pointE = [
7174
+ pointN[0] - CK.lengthMA * Math.sin(30 * D2R$4),
7175
+ pointN[1] - CK.lengthMA * Math.cos(30 * D2R$4)
7176
+ ];
7177
+ CK.lengthGF = distance(CK.pointG, CK.pointF);
7178
+ CK.lengthBD = distance(CK.pointB, CK.pointD);
7179
+ CK.lengthGFE = CK.lengthGF + CK.lengthAB;
7180
+ CK.deltaMEq = CK.lengthGFE / 45;
7181
+ CK.lengthAP75 = 15 * CK.lengthParallel73to90At0;
7182
+ CK.lengthAP73 = CK.lengthMG - CK.lengthMA -
7183
+ CK.lengthParallel0to73At0 * 73;
7184
+ pointU = [
7185
+ CK.pointA[0] + CK.lengthAP73 * Math.cos(30 * D2R$4),
7186
+ CK.pointA[1] + CK.lengthAP73 * Math.sin(30 * D2R$4)
7187
+ ];
7188
+ CK.pointT = lineIntersection(pointU, -60, CK.pointB, 30);
7189
+
7190
+ p73a = parallel73(29);
7191
+ lF = p73a.lengthParallel73;
7192
+ lT = lengthTorridSegment(29);
7193
+ lM = lengthMiddleSegment(29);
7194
+ l = 15 * (lT + lM + lF) / 73 - lT;
7195
+ pointV = interpolate(l, lM, jointT(29), jointF(29));
7196
+ CK.pointC = [0, 0];
7197
+ CK.pointC[1] = (
7198
+ pointV[0] * pointV[0] + pointV[1] * pointV[1] -
7199
+ CK.pointD[0] * CK.pointD[0] - CK.pointD[1] * CK.pointD[1]
7200
+ ) / (
7201
+ 2 * (k * pointV[0] + pointV[1] -
7202
+ k * CK.pointD[0] - CK.pointD[1])
7203
+ );
7204
+ CK.pointC[0] = k * CK.pointC[1];
7205
+ CK.radius = distance(CK.pointC, CK.pointD);
7206
+ }
7207
+
7208
+ function ll2mp(lon, lat) {
7209
+ var south = [0, 6, 7, 8, 5];
7210
+ var octant = truncate((lon + 180) / 90 + 1);
7211
+ var meridian = (lon + 720) % 90 - 45;
7212
+ var side = meridian < 0 ? -1 : meridian > 0 ? 1 : 0;
7213
+ meridian = Math.abs(meridian);
7214
+ if (octant === 5) octant = 1;
7215
+ if (lat < 0) octant = south[octant];
7216
+ return [meridian, Math.abs(lat), side, octant];
7217
+ }
7218
+
7219
+ function mp2xy(m, p) {
7220
+ if (m === 0) return p >= 75 ? zoneA(m, p) : zoneB(m, p);
7221
+ if (p >= 75) return zoneC(m, p);
7222
+ if (p === 0) return zoneD(m);
7223
+ if (p >= 73 && m <= 30) return zoneE(m, p);
7224
+ if (m === 45) {
7225
+ return p <= 15 ? zoneF(m, p) :
7226
+ p <= 73 ? zoneG(m, p) : zoneH(m, p);
7227
+ }
7228
+ if (m <= 29) return zoneI(m, p);
7229
+ if (p >= 73) return zoneJ(m, p);
7230
+ var lT = lengthTorridSegment(m);
7231
+ var hit = circleLineIntersection(
7232
+ CK.pointC, CK.radius, jointT(m), jointF(m)
7233
+ );
7234
+ var l15;
7235
+ if (hit[0]) {
7236
+ l15 = lT + distance(jointT(m), hit[1]);
7237
+ } else {
7238
+ hit = circleLineIntersection(
7239
+ CK.pointC, CK.radius, jointE(m), jointT(m)
7240
+ );
7241
+ l15 = lT - distance(jointT(m), hit[1]);
7242
+ }
7243
+ return p <= 15 ? zoneK(m, p, l15) : zoneL(m, p, l15);
7244
+ }
7245
+
7246
+ function zoneA(m, p) {
7247
+ return [CK.pointA[0] + (90 - p) * 104, 0];
7248
+ }
7249
+
7250
+ function zoneB(m, p) {
7251
+ return [CK.pointG[0] - p * 100, 0];
7252
+ }
7253
+
7254
+ function zoneC(m, p) {
7255
+ return radialPoint(CK.pointA, 104 * (90 - p), m);
7256
+ }
7257
+
7258
+ function zoneD(m) {
7259
+ return equator(m);
7260
+ }
7261
+
7262
+ function zoneE(m, p) {
7263
+ return radialPoint(CK.pointA, 1560 + (75 - p) * 100, m);
7264
+ }
7265
+
7266
+ function zoneF(m, p) {
7267
+ return interpolate(p, 15, CK.pointE, CK.pointD);
7268
+ }
7269
+
7270
+ function zoneG(m, p) {
7271
+ return interpolate(p - 15, 58, CK.pointD, CK.pointT);
7272
+ }
7273
+
7274
+ function zoneH(m, p) {
7275
+ var p75 = parallel75(45);
7276
+ var p73 = parallel73(m).parallel73;
7277
+ var lF = distance(CK.pointT, CK.pointB);
7278
+ var lF75 = distance(CK.pointB, p75);
7279
+ var l = (75 - p) * (lF75 + lF) / 2;
7280
+ return l <= lF75 ?
7281
+ interpolate(l, lF75, p75, CK.pointB) :
7282
+ interpolate(l - lF75, lF, CK.pointB, p73);
7283
+ }
7284
+
7285
+ function zoneI(m, p) {
7286
+ var p73a = parallel73(m);
7287
+ var lT = lengthTorridSegment(m);
7288
+ var lM = lengthMiddleSegment(m);
7289
+ var l = p * (lT + lM + p73a.lengthParallel73) / 73;
7290
+ if (l <= lT) return interpolate(l, lT, jointE(m), jointT(m));
7291
+ if (l <= lT + lM) {
7292
+ return interpolate(l - lT, lM, jointT(m), jointF(m));
7293
+ }
7294
+ return interpolate(
7295
+ l - lT - lM,
7296
+ p73a.lengthParallel73,
7297
+ jointF(m),
7298
+ p73a.parallel73
7299
+ );
7300
+ }
7301
+
7302
+ function zoneJ(m, p) {
7303
+ var p75 = parallel75(m);
7304
+ var p73a = parallel73(m);
7305
+ var lF75 = distance(jointF(m), p75);
7306
+ var l = (75 - p) * (lF75 - p73a.lengthParallel73) / 2;
7307
+ return l <= lF75 ?
7308
+ interpolate(l, lF75, p75, jointF(m)) :
7309
+ interpolate(
7310
+ l - lF75,
7311
+ -p73a.lengthParallel73,
7312
+ jointF(m),
7313
+ p73a.parallel73
7314
+ );
7315
+ }
7316
+
7317
+ function zoneK(m, p, l15) {
7318
+ var l = p * l15 / 15;
7319
+ var lT = lengthTorridSegment(m);
7320
+ var lM = lengthMiddleSegment(m);
7321
+ return l <= lT ?
7322
+ interpolate(l, lT, jointE(m), jointT(m)) :
7323
+ interpolate(l - lT, lM, jointT(m), jointF(m));
7324
+ }
7325
+
7326
+ function zoneL(m, p, l15) {
7327
+ var p73a = parallel73(m);
7328
+ var lT = lengthTorridSegment(m);
7329
+ var lM = lengthMiddleSegment(m);
7330
+ var lF = p73a.lengthParallel73;
7331
+ var l = l15 + (p - 15) * (lT + lM + lF - l15) / 58;
7332
+ if (l <= lT) return interpolate(l, lT, jointE(m), jointF(m));
7333
+ if (l <= lT + lM) {
7334
+ return interpolate(l - lT, lM, jointT(m), jointF(m));
7335
+ }
7336
+ return interpolate(
7337
+ l - lT - lM, lF, jointF(m), p73a.parallel73
7338
+ );
7339
+ }
7340
+
7341
+ function equator(m) {
7342
+ var l = CK.deltaMEq * m;
7343
+ return l <= CK.lengthGF ?
7344
+ [CK.pointG[0], l] :
7345
+ interpolate(l - CK.lengthGF, CK.lengthAB, CK.pointF, CK.pointE);
7346
+ }
7347
+
7348
+ function jointE(m) {
7349
+ return equator(m);
7350
+ }
7351
+
7352
+ function jointT(m) {
7353
+ return lineIntersection(CK.pointM, 2 * m / 3, jointE(m), m / 3);
7354
+ }
7355
+
7356
+ function jointF(m) {
7357
+ if (m === 0) return [CK.pointA[0] + CK.lengthAB, 0];
7358
+ return lineIntersection(CK.pointA, m, CK.pointM, 2 * m / 3);
7359
+ }
7360
+
7361
+ function lengthTorridSegment(m) {
7362
+ return distance(jointE(m), jointT(m));
7363
+ }
7364
+
7365
+ function lengthMiddleSegment(m) {
7366
+ return distance(jointT(m), jointF(m));
7367
+ }
7368
+
7369
+ function parallel73(m) {
7370
+ var p73, lF, xy;
7371
+ var jF = jointF(m);
7372
+ if (m <= 30) {
7373
+ p73 = radialPoint(CK.pointA, CK.lengthAP73, m);
7374
+ lF = distance(jF, p73);
7375
+ } else {
7376
+ p73 = lineIntersection(CK.pointT, -60, jF, m);
7377
+ lF = distance(jF, p73);
7378
+ if (m > 44) {
7379
+ xy = lineIntersection(CK.pointT, -60, jF, 2 * m / 3);
7380
+ if (xy[0] > p73[0]) {
7381
+ p73 = xy;
7382
+ lF = -distance(jF, p73);
7383
+ }
7384
+ }
7385
+ }
7386
+ return {parallel73: p73, lengthParallel73: lF};
7387
+ }
7388
+
7389
+ function parallel75(m) {
7390
+ return radialPoint(CK.pointA, CK.lengthAP75, m);
7391
+ }
7392
+
7393
+ function mj2g(xy, octant) {
7394
+ var out;
7395
+ if (octant === 0) {
7396
+ out = rotate(xy, -60);
7397
+ } else if (octant === 1) {
7398
+ out = rotate(xy, -120);
7399
+ out[0] -= CK.lengthMG;
7400
+ } else if (octant === 2) {
7401
+ out = rotate(xy, -60);
7402
+ out[0] -= CK.lengthMG;
7403
+ } else if (octant === 3) {
7404
+ out = rotate(xy, -120);
7405
+ out[0] += CK.lengthMG;
7406
+ } else if (octant === 4) {
7407
+ out = rotate(xy, -60);
7408
+ out[0] += CK.lengthMG;
7409
+ } else if (octant === 5) {
7410
+ out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
7411
+ out[0] += CK.lengthMG;
7412
+ } else if (octant === 6) {
7413
+ out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
7414
+ out[0] -= CK.lengthMG;
7415
+ } else if (octant === 7) {
7416
+ out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
7417
+ out[0] -= CK.lengthMG;
7418
+ } else if (octant === 8) {
7419
+ out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
7420
+ out[0] += CK.lengthMG;
7421
+ }
7422
+ return out;
7423
+ }
7424
+
7425
+ function radialPoint(origin, length, angle) {
7426
+ return [
7427
+ origin[0] + length * Math.cos(angle * D2R$4),
7428
+ origin[1] + length * Math.sin(angle * D2R$4)
7429
+ ];
7430
+ }
7431
+
7432
+ function rotate(xy, angle) {
7433
+ if (angle === -60) {
7434
+ return [
7435
+ xy[0] * CK.cos60 + xy[1] * CK.sin60,
7436
+ -xy[0] * CK.sin60 + xy[1] * CK.cos60
7437
+ ];
7438
+ }
7439
+ if (angle === -120) {
7440
+ return [
7441
+ -xy[0] * CK.cos60 + xy[1] * CK.sin60,
7442
+ -xy[0] * CK.sin60 - xy[1] * CK.cos60
7443
+ ];
7444
+ }
7445
+ var cos = Math.cos(angle * D2R$4);
7446
+ var sin = Math.sin(angle * D2R$4);
7447
+ return [xy[0] * cos - xy[1] * sin, xy[0] * sin + xy[1] * cos];
7448
+ }
7449
+ }
7450
+
7451
+ function createCahillKeyesFaceRaw(mg) {
7452
+ return createCahillKeyesTransform(mg, true);
7453
+ }
7454
+
7455
+ function distance(a, b) {
7456
+ return Math.hypot(a[0] - b[0], a[1] - b[1]);
7457
+ }
7458
+
7459
+ function interpolate(length, total, start, end) {
7460
+ return [
7461
+ start[0] + (end[0] - start[0]) * length / total,
7462
+ start[1] + (end[1] - start[1]) * length / total
7463
+ ];
7464
+ }
7465
+
7466
+ function lineIntersection(p1, slope1, p2, slope2) {
7467
+ var m1 = Math.tan(slope1 * D2R$4);
7468
+ var m2 = Math.tan(slope2 * D2R$4);
7469
+ var x = (m1 * p1[0] - m2 * p2[0] - p1[1] + p2[1]) / (m1 - m2);
7470
+ return [x, m1 * (x - p1[0]) + p1[1]];
7471
+ }
7472
+
7473
+ function circleLineIntersection(center, radius, p1, p2) {
7474
+ var dx = p2[0] - p1[0];
7475
+ var dy = p2[1] - p1[1];
7476
+ var fx = p1[0] - center[0];
7477
+ var fy = p1[1] - center[1];
7478
+ var a = dx * dx + dy * dy;
7479
+ var b = 2 * (dx * fx + dy * fy);
7480
+ var c = fx * fx + fy * fy - radius * radius;
7481
+ var d = b * b - 4 * a * c;
7482
+ if (a === 0 || d < 0) return [0, [0, 0]];
7483
+ var root = Math.sqrt(d);
7484
+ var u1 = (-b + root) / (2 * a);
7485
+ var u2 = (-b - root) / (2 * a);
7486
+ var u = u1 >= 0 && u1 <= 1 ? u1 :
7487
+ u2 >= 0 && u2 <= 1 ? u2 : null;
7488
+ return u === null ? [0, [0, 0]] :
7489
+ [1, [p1[0] + u * dx, p1[1] + u * dy]];
7490
+ }
7491
+
7492
+ function truncate(n) {
7493
+ return n > 0 ? Math.floor(n) : Math.ceil(n);
7494
+ }
7495
+
7496
+ var D2R$3 = Math.PI / 180;
7497
+ var EPS$1 = 1e-12;
7498
+
7499
+ // Create a smooth facet projection from any radial azimuthal projection.
7500
+ // The azimuthal coordinates are warped back to the gnomonic polygon along
7501
+ // the boundary, so adjacent facets retain identical straight edges.
7502
+ function createRadialFacetProjector(coords, options) {
7503
+ options = options || {};
7504
+ var center = options.planarCenter || getSphericalCenter(coords);
7505
+ var gnomonic = createGnomonicProjector(center);
7506
+ var planar = coords.map(function(p) {
7507
+ return gnomonic(p[0] * D2R$3, p[1] * D2R$3);
7508
+ });
7509
+ var edges = createNormalizedEdges(planar);
7510
+ var radial = createRadialFunction(
7511
+ options.radial || 'equal-area',
7512
+ options.radial2,
7513
+ options.radialBlend
7514
+ );
7515
+ var boundaryStrength = options.boundaryStrength || 1;
7516
+ var radialScale = getRadialScale(planar, radial) *
7517
+ (options.radialScale || 1);
7518
+
7519
+ project.radial = options.radial || 'equal-area';
7520
+ project.boundaryStrength = boundaryStrength;
7521
+ return project;
7522
+
7523
+ function project(lam, phi) {
7524
+ var p = gnomonic(lam, phi);
7525
+ var r = Math.hypot(p[0], p[1]);
7526
+ if (r < EPS$1) return [0, 0];
7527
+ var c = Math.atan(r);
7528
+ var azimuthalRadius = radial(c) * radialScale;
7529
+ var boundaryWeight = getBoundaryWeight(p, edges, boundaryStrength);
7530
+ var scale = 1 + boundaryWeight * (azimuthalRadius / r - 1);
7531
+ return [p[0] * scale, p[1] * scale];
7532
+ }
7533
+ }
7534
+
7535
+ function createRadialFunction(radial1, radial2, blend) {
7536
+ var f1 = getRadialFunction(radial1);
7537
+ if (!radial2) return f1;
7538
+ var f2 = getRadialFunction(radial2);
7539
+ var k = Math.max(0, Math.min(1, blend == null ? 0.5 : blend));
7540
+ return function(c) {
7541
+ return f1(c) + (f2(c) - f1(c)) * k;
7542
+ };
7543
+ }
7544
+
7545
+ function getRadialFunction(id) {
7546
+ if (typeof id == 'function') return id;
7547
+ id = normalizeRadialProjectionName(id);
7548
+ if (id == 'aeqd') return function(c) {
7549
+ return c;
7550
+ };
7551
+ if (id == 'stere') return function(c) {
7552
+ return 2 * Math.tan(c / 2);
7553
+ };
7554
+ if (id == 'ortho') return function(c) {
7555
+ return Math.sin(c);
7556
+ };
7557
+ if (id == 'gnom') return function(c) {
7558
+ return Math.tan(c);
7559
+ };
7560
+ // Lambert azimuthal equal-area
7561
+ return function(c) {
7562
+ return 2 * Math.sin(c / 2);
7563
+ };
7564
+ }
7565
+
7566
+ function normalizeRadialProjectionName(id) {
7567
+ id = String(id || '').toLowerCase().replace(/_/g, '-');
7568
+ if (id == 'laea' || id == 'lambert' || id == 'equal-area') return 'laea';
7569
+ if (id == 'aeqd' || id == 'equidistant') return 'aeqd';
7570
+ if (id == 'stere' || id == 'stereographic') return 'stere';
7571
+ if (id == 'ortho' || id == 'orthographic') return 'ortho';
7572
+ if (id == 'gnom' || id == 'gnomonic') return 'gnom';
7573
+ throw new Error('Unsupported facet projection: ' + id);
7574
+ }
7575
+
7576
+ function getRadialScale(planar, radial) {
7577
+ var sum = 0;
7578
+ planar.forEach(function(p) {
7579
+ var r = Math.hypot(p[0], p[1]);
7580
+ sum += r / radial(Math.atan(r));
7581
+ });
7582
+ return sum / planar.length;
7583
+ }
7584
+
7585
+ function createNormalizedEdges(planar) {
7586
+ var center = [0, 0];
7587
+ return planar.map(function(a, i) {
7588
+ var b = planar[(i + 1) % planar.length];
7589
+ var dx = b[0] - a[0];
7590
+ var dy = b[1] - a[1];
7591
+ var centerCross = dx * (center[1] - a[1]) -
7592
+ dy * (center[0] - a[0]);
7593
+ return {
7594
+ a: a,
7595
+ dx: dx,
7596
+ dy: dy,
7597
+ denominator: centerCross
7598
+ };
7599
+ });
7600
+ }
7601
+
7602
+ function getBoundaryWeight(p, edges, strength) {
7603
+ var product = 1;
7604
+ for (var i = 0; i < edges.length; i++) {
7605
+ var edge = edges[i];
7606
+ var cross = edge.dx * (p[1] - edge.a[1]) -
7607
+ edge.dy * (p[0] - edge.a[0]);
7608
+ product *= Math.max(0, cross / edge.denominator);
7609
+ }
7610
+ product = Math.max(0, Math.min(1, product));
7611
+ return 1 - Math.pow(1 - product, strength);
7612
+ }
7613
+
7614
+ function getSphericalCenter(coords) {
7615
+ var sum = coords.reduce(function(memo, p) {
7616
+ var lam = p[0] * D2R$3;
7617
+ var phi = p[1] * D2R$3;
7618
+ var cosPhi = Math.cos(phi);
7619
+ memo[0] += Math.cos(lam) * cosPhi;
7620
+ memo[1] += Math.sin(lam) * cosPhi;
7621
+ memo[2] += Math.sin(phi);
7622
+ return memo;
7623
+ }, [0, 0, 0]);
7624
+ return [
7625
+ Math.atan2(sum[1], sum[0]) / D2R$3,
7626
+ Math.atan2(sum[2], Math.hypot(sum[0], sum[1])) / D2R$3
7627
+ ];
7628
+ }
7629
+
7630
+ /*
7631
+ * Octahedral butterfly projections.
7632
+ *
7633
+ * Face layouts are adapted from d3-geo-polygon (ISC license):
7634
+ * https://github.com/d3/d3-geo-polygon
7635
+ *
7636
+ * Copyright 2017-2024 Mike Bostock
7637
+ * ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
7638
+ */
7639
+
7640
+
7641
+ var D2R$2 = Math.PI / 180;
7642
+ var R2D$2 = 180 / Math.PI;
7643
+ var RADIAL_BOUNDARY_STRENGTH = 1;
7644
+ var OCTAHEDRON = createOctahedron();
7645
+ var BUTTERFLY_PARENTS = [-1, 0, 0, 1, 0, 1, 4, 5];
7646
+ var DEFAULT_LON0 = {
7647
+ butterfly: 157.5,
7648
+ butterfly2: -20,
7649
+ cahill_keyes: -20
7650
+ };
7651
+ var engines$1 = {};
7652
+
7653
+ function registerButterflyProjections(mproj) {
7654
+ if (!mproj) return;
7655
+ register('butterfly', 'Butterfly projection (Pacific aspect)', 'butterfly');
7656
+ register('butterfly2', 'Butterfly projection (Atlantic aspect)', 'butterfly2');
7657
+ register('cahill_keyes', 'Cahill-Keyes butterfly', 'cahill_keyes');
7658
+
7659
+ function register(id, name, method) {
7660
+ if (mproj.internal.pj_list[id]) return;
7661
+ mproj.pj_add(function(P) {
7662
+ initButterfly(P, method);
7663
+ }, id, name);
7664
+ }
7665
+ }
7666
+
7667
+ function getButterflyEngine(method) {
7668
+ var key = method == 'butterfly2' ? 'butterfly' : method;
7669
+ if (!engines$1[key]) {
7670
+ if (key == 'cahill_keyes') {
7671
+ engines$1[key] = createCahillKeyesEngine();
7672
+ } else if (key == 'butterfly') {
7673
+ engines$1[key] = createButterflyEngine();
7674
+ } else {
7675
+ throw new Error('Unknown butterfly projection: ' + method);
7676
+ }
7677
+ }
7678
+ return engines$1[key];
7679
+ }
7680
+
7681
+ function initButterfly(P, method) {
7682
+ var engine = getButterflyEngine(method);
7683
+ if (!P.params.lon_0) {
7684
+ P.lam0 = DEFAULT_LON0[method] * D2R$2;
7685
+ }
7686
+ P.es = 0;
7687
+ P.inv = null;
7688
+ P.fwd = function(lp, xy) {
7689
+ var p = engine.forward(lp.lam, lp.phi);
7690
+ if (!p) {
7691
+ xy.x = xy.y = Infinity;
7692
+ } else {
7693
+ xy.x = p[0];
7694
+ xy.y = p[1];
7695
+ }
7696
+ };
7697
+ P.__projection_topology = engine.getTopology(P.lam0 * R2D$2);
7698
+ if (engine.removeOutlineExtremeConnectors) {
7699
+ P.__remove_outline_extreme_connectors = true;
7700
+ }
7701
+ if (engine.useProjectedOutline !== false) {
7702
+ P.__projected_outline = engine.outline;
7703
+ }
7704
+ }
7705
+
7706
+ function createButterflyEngine() {
7707
+ var faceCenters = [-45, 45, -45, 45, -135, 135, -135, 135];
7708
+ // Reuse the Cahill-Keyes polar incisions and 12-zone octant transform,
7709
+ // then unfold the faces with the butterfly attachment tree.
7710
+ var data = createTruncatedOctahedronFaces(
7711
+ Math.cos(17 * D2R$2),
7712
+ Math.sin(17 * D2R$2),
7713
+ BUTTERFLY_PARENTS
7714
+ );
7715
+ var faceRaw = createCahillKeyesFaceRaw(10000);
7716
+ var engine = createPolyhedralProjection({
7717
+ faces: data.faces,
7718
+ parents: data.parents,
7719
+ rotation: [0, 0, 0],
7720
+ // Face-local CK coordinates point opposite the root face.
7721
+ angle: 150,
7722
+ findFace: createTruncatedFaceFinder(data.cornerNormals),
7723
+ faceProjector: function(face) {
7724
+ var baseId = face.id < 8 ? face.id : data.parents[face.id];
7725
+ var center = faceCenters[baseId] * D2R$2;
7726
+ var raw = function(lam, phi) {
7727
+ return faceRaw(normalizeRadians$1(lam - center), phi);
7728
+ };
7729
+ return createNormalizedFacetProjector(face.coords, raw);
7730
+ }
7731
+ });
7732
+ engine.facetProjection = 'cahill_keyes';
7733
+ return engine;
7734
+ }
7735
+
7736
+ function createNormalizedFacetProjector(coords, raw) {
7737
+ // Remove the global placement and scale of a raw facet while preserving its
7738
+ // internal shape. The polyhedral engine then controls how facets unfold.
7739
+ var points = coords.map(function(p) {
7740
+ return raw(p[0] * D2R$2, p[1] * D2R$2);
7741
+ });
7742
+ var edge = 0;
7743
+ var maxLengthSq = -1;
7744
+ for (var i = 0; i < points.length; i++) {
7745
+ var a = points[i];
7746
+ var b = points[(i + 1) % points.length];
7747
+ var dx = b[0] - a[0];
7748
+ var dy = b[1] - a[1];
7749
+ var lengthSq = dx * dx + dy * dy;
7750
+ if (lengthSq > maxLengthSq) {
7751
+ maxLengthSq = lengthSq;
7752
+ edge = i;
7753
+ }
7754
+ }
7755
+ var origin = points[edge];
7756
+ var end = points[(edge + 1) % points.length];
7757
+ var ux = end[0] - origin[0];
7758
+ var uy = end[1] - origin[1];
7759
+ var center = points.reduce(function(sum, p) {
7760
+ sum[0] += p[0] / points.length;
7761
+ sum[1] += p[1] / points.length;
7762
+ return sum;
7763
+ }, [0, 0]);
7764
+ var side = ux * (center[1] - origin[1]) -
7765
+ uy * (center[0] - origin[0]) < 0 ? -1 : 1;
7766
+ return function(lam, phi) {
7767
+ var p = raw(lam, phi);
7768
+ var x = p[0] - origin[0];
7769
+ var y = p[1] - origin[1];
7770
+ return [
7771
+ (x * ux + y * uy) / maxLengthSq,
7772
+ side * (ux * y - uy * x) / maxLengthSq
7773
+ ];
7774
+ };
7775
+ }
7776
+
7777
+ function createRadialButterflyEngine(options) {
7778
+ options = normalizeRadialFacetOptions(options);
7779
+ var data = createRadialButterflyFaces();
7780
+ var radialOptions = {
7781
+ radial: options.projection,
7782
+ radial2: options.projection2,
7783
+ radialBlend: options.blend,
7784
+ boundaryStrength: RADIAL_BOUNDARY_STRENGTH
7785
+ };
7786
+ var squareProjectors = createSquareProjectors(data.faces, radialOptions);
7787
+ var engine = createPolyhedralProjection({
7788
+ faces: data.faces,
7789
+ parents: data.parents,
7790
+ rotation: [0, 0, 0],
7791
+ angle: -30,
7792
+ findFace: createTruncatedFaceFinder(data.cornerNormals),
7793
+ faceProjector: function(face) {
7794
+ if (face.id < 8) {
7795
+ return createRadialFacetProjector(face.coords, radialOptions);
7796
+ }
7797
+ return squareProjectors.get(pointKey(face.coords[0]));
7798
+ }
7799
+ });
7800
+ engine.radialFacet = {
7801
+ projection: options.projection,
7802
+ projection2: options.projection2,
7803
+ blend: options.blend,
7804
+ boundaryStrength: RADIAL_BOUNDARY_STRENGTH
7805
+ };
7806
+ return engine;
7807
+ }
7808
+
7809
+ function normalizeRadialFacetOptions(options) {
7810
+ options = options || {};
7811
+ var projection2 = options.projection2 ?
7812
+ normalizeRadialProjectionName(options.projection2) :
7813
+ null;
7814
+ var blend = projection2 ?
7815
+ options.blend == null ? 0.5 : Number(options.blend) :
7816
+ 0;
7817
+ if (!Number.isFinite(blend) || blend < 0 || blend > 1) {
7818
+ throw new Error('Facet projection blend must be between 0 and 1');
7819
+ }
7820
+ return {
7821
+ projection: normalizeRadialProjectionName(options.projection || 'laea'),
7822
+ projection2: projection2,
7823
+ blend: blend
7824
+ };
7825
+ }
7826
+
7827
+ function createCahillKeyesEngine() {
7828
+ var data = createTruncatedOctahedronFaces(
7829
+ Math.cos(17 * D2R$2),
7830
+ Math.sin(17 * D2R$2),
7831
+ [-1, 3, 0, 2, 0, 1, 4, 5]
7832
+ );
7833
+ var raw = createCahillKeyesRaw(10000);
7834
+ var engine = createPolyhedralProjection({
7835
+ faces: data.faces,
7836
+ parents: data.parents,
7837
+ rotation: [0, 0, 0],
7838
+ angle: 0,
7839
+ findFace: createTruncatedFaceFinder(data.cornerNormals),
7840
+ faceProjector: function() {
7841
+ return function(lam, phi) {
7842
+ var p = raw(lam, phi);
7843
+ return [p[0], -p[1]];
7844
+ };
7845
+ }
7846
+ });
7847
+ // The 12-zone transform positions all octants directly in its M-profile.
7848
+ // Its face-tree boundary retraces several edges, so derive graticule
7849
+ // footprints through the normal spherical clipping path instead.
7850
+ engine.useProjectedOutline = false;
7851
+ engine.removeOutlineExtremeConnectors = true;
7852
+ return engine;
7853
+ }
7854
+
7855
+ function createRadialButterflyFaces() {
7856
+ return createTruncatedOctahedronFaces(
7857
+ Math.sqrt(0.9),
7858
+ Math.sqrt(0.1),
7859
+ BUTTERFLY_PARENTS
7860
+ );
7861
+ }
7862
+
7863
+ function createTruncatedOctahedronFaces(edgeWeight, vertexWeight, baseParents) {
7864
+ var cornerNormals = [];
7865
+ var faces = OCTAHEDRON.map(function(face) {
7866
+ var vectors = face.map(degreesToVector);
7867
+ var hexagon = [];
7868
+ var a = vectors[vectors.length - 1];
7869
+ vectors.forEach(function(b) {
7870
+ hexagon.push(
7871
+ vectorToDegrees(normalizeVector$1(addScaledVectors(
7872
+ a, edgeWeight, b, vertexWeight
7873
+ ))),
7874
+ vectorToDegrees(normalizeVector$1(addScaledVectors(
7875
+ b, edgeWeight, a, vertexWeight
7876
+ )))
7877
+ );
7878
+ a = b;
7879
+ });
7880
+ return hexagon;
7881
+ });
7882
+ canonicalizeVertices(faces);
7883
+ faces.forEach(function(hexagon) {
7884
+ var normals = [];
7885
+ for (var i = 0; i < 3; i++) {
7886
+ normals.push(crossVectors(
7887
+ degreesToVector(hexagon[(i * 2 + 2) % 6]),
7888
+ degreesToVector(hexagon[(i * 2 + 1) % 6])
7889
+ ));
7890
+ }
7891
+ cornerNormals.push(normals);
7892
+ });
7893
+ var parents = baseParents.concat();
7894
+ faces.slice().forEach(function(hexagon, j) {
7895
+ var face = OCTAHEDRON[j];
7896
+ for (var i = 0; i < 3; i++) {
7897
+ faces.push([
7898
+ face[i],
7899
+ hexagon[(i * 2 + 2) % 6],
7900
+ hexagon[(i * 2 + 1) % 6]
7901
+ ]);
7902
+ parents.push(j);
7903
+ }
7904
+ });
7905
+ return {faces: faces, parents: parents, cornerNormals: cornerNormals};
7906
+ }
7907
+
7908
+ function createTruncatedFaceFinder(cornerNormals) {
7909
+ return function(lam, phi) {
7910
+ var cosPhi = Math.cos(phi);
7911
+ var p = [cosPhi * Math.cos(lam), cosPhi * Math.sin(lam), Math.sin(phi)];
7912
+ var face = lam < -Math.PI / 2 ?
7913
+ phi < 0 ? 6 : 4 :
7914
+ lam < 0 ?
7915
+ phi < 0 ? 2 : 0 :
7916
+ lam < Math.PI / 2 ?
7917
+ phi < 0 ? 3 : 1 :
7918
+ phi < 0 ? 7 : 5;
7919
+ var normals = cornerNormals[face];
7920
+ if (dotVectors(normals[0], p) < 0) return 8 + 3 * face;
7921
+ if (dotVectors(normals[1], p) < 0) return 8 + 3 * face + 1;
7922
+ if (dotVectors(normals[2], p) < 0) return 8 + 3 * face + 2;
7923
+ return face;
7924
+ };
7925
+ }
7926
+
7927
+ function canonicalizeVertices(faces) {
7928
+ var index = new Map();
7929
+ faces.forEach(function(face) {
7930
+ face.forEach(function(p, i) {
7931
+ var v = degreesToVector(p);
7932
+ var key = v.map(function(x) {
7933
+ return x.toFixed(12);
7934
+ }).join(',');
7935
+ if (index.has(key)) {
7936
+ face[i] = index.get(key);
7937
+ } else {
7938
+ index.set(key, p);
7939
+ }
7940
+ });
7941
+ });
7942
+ }
7943
+
7944
+ function createSquareProjectors(faces, radialOptions) {
7945
+ var verticesByCenter = new Map();
7946
+ var projectors = new Map();
7947
+ faces.slice(8).forEach(function(face) {
7948
+ var key = pointKey(face[0]);
7949
+ var vertices = verticesByCenter.get(key);
7950
+ if (!vertices) {
7951
+ vertices = new Map();
7952
+ verticesByCenter.set(key, vertices);
7953
+ }
7954
+ vertices.set(pointKey(face[1]), face[1]);
7955
+ vertices.set(pointKey(face[2]), face[2]);
7956
+ });
7957
+ verticesByCenter.forEach(function(vertices, key) {
7958
+ var center = key.split(',').map(Number);
7959
+ var gnomonic = createGnomonicProjector(center);
7960
+ var coords = Array.from(vertices.values()).sort(function(a, b) {
7961
+ return planarAngle(a, gnomonic) - planarAngle(b, gnomonic);
7962
+ });
7963
+ projectors.set(key, createRadialFacetProjector(coords, {
7964
+ planarCenter: center,
7965
+ radial: radialOptions.radial,
7966
+ radial2: radialOptions.radial2,
7967
+ radialBlend: radialOptions.radialBlend,
7968
+ boundaryStrength: radialOptions.boundaryStrength
7969
+ }));
7970
+ });
7971
+ return projectors;
7972
+ }
7973
+
7974
+ function planarAngle(p, project) {
7975
+ var q = project(p[0] * D2R$2, p[1] * D2R$2);
7976
+ return Math.atan2(q[1], q[0]);
7977
+ }
7978
+
7979
+ function createOctahedron() {
7980
+ var vertices = [
7981
+ [0, 90],
7982
+ [-90, 0],
7983
+ [0, 0],
7984
+ [90, 0],
7985
+ [180, 0],
7986
+ [0, -90]
7987
+ ];
7988
+ return [
7989
+ [0, 2, 1],
7990
+ [0, 3, 2],
7991
+ [5, 1, 2],
7992
+ [5, 2, 3],
7993
+ [0, 1, 4],
7994
+ [0, 4, 3],
7995
+ [5, 4, 1],
7996
+ [5, 3, 4]
7997
+ ].map(function(ids) {
7998
+ return ids.map(function(id) {
7999
+ return vertices[id];
8000
+ });
8001
+ });
8002
+ }
8003
+
8004
+ function pointKey(p) {
8005
+ return p[0] + ',' + p[1];
8006
+ }
8007
+
8008
+ function degreesToVector(p) {
8009
+ var lam = p[0] * D2R$2;
8010
+ var phi = p[1] * D2R$2;
8011
+ var cosPhi = Math.cos(phi);
8012
+ return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
8013
+ }
8014
+
8015
+ function vectorToDegrees(p) {
8016
+ return [
8017
+ Math.atan2(p[1], p[0]) * R2D$2,
8018
+ Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$2
8019
+ ];
8020
+ }
8021
+
8022
+ function addScaledVectors(a, ka, b, kb) {
8023
+ return [
8024
+ a[0] * ka + b[0] * kb,
8025
+ a[1] * ka + b[1] * kb,
8026
+ a[2] * ka + b[2] * kb
8027
+ ];
8028
+ }
8029
+
8030
+ function crossVectors(a, b) {
8031
+ return [
8032
+ a[1] * b[2] - a[2] * b[1],
8033
+ a[2] * b[0] - a[0] * b[2],
8034
+ a[0] * b[1] - a[1] * b[0]
8035
+ ];
8036
+ }
8037
+
8038
+ function dotVectors(a, b) {
8039
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
8040
+ }
8041
+
8042
+ function normalizeVector$1(p) {
8043
+ var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
8044
+ return [p[0] * k, p[1] * k, p[2] * k];
8045
+ }
8046
+
8047
+ function normalizeRadians$1(lam) {
8048
+ return (lam + Math.PI * 3) % (Math.PI * 2) - Math.PI;
8049
+ }
8050
+
8051
+ var ButterflyProjections = /*#__PURE__*/Object.freeze({
8052
+ __proto__: null,
8053
+ createRadialButterflyEngine: createRadialButterflyEngine,
8054
+ getButterflyEngine: getButterflyEngine,
8055
+ registerButterflyProjections: registerButterflyProjections
8056
+ });
8057
+
8058
+ /*
8059
+ * Forward implementation of Hajime Narukawa's 2022 mathematical
8060
+ * approximation of the AuthaGraph construction.
8061
+ *
8062
+ * Facet formula:
8063
+ * H. Narukawa, "Formulation of AuthaGraph Map Projection" (2022)
8064
+ * https://www.jstage.jst.go.jp/article/jjca/60/1/60_1/_article/-char/en
8065
+ *
8066
+ * The rectangular facet routing follows the public Imago arrangement by
8067
+ * Justin Kunimune. The face transform itself uses Narukawa's published
8068
+ * equations, not Imago's power-law approximation.
8069
+ */
8070
+
8071
+ var D2R$1 = Math.PI / 180;
8072
+ var R2D$1 = 180 / Math.PI;
8073
+ var HALF_PI = Math.PI / 2;
8074
+ var SQRT2 = Math.sqrt(2);
8075
+ var SQRT3 = Math.sqrt(3);
8076
+ var ASIN_ONE_THIRD = Math.asin(1 / 3);
8077
+ var EDGE_SCALE = Math.acos(-1 / 3) / 2;
8078
+ var XMIN = -2 * SQRT3;
8079
+ var XMAX = 2 * SQRT3;
8080
+ var YMIN = -1.5;
8081
+ var YMAX = 1.5;
8082
+ var BLOCK_HEIGHT = 2 * SQRT3;
8083
+ var LAYOUT_SHIFT = 1.16;
8084
+ var EPS = 1e-12;
8085
+ var engines = {};
8086
+
8087
+ // The four tetrahedron vertices published by Narukawa, in latitude-longitude
8088
+ // order. The extra precision preserves the regular tetrahedron to about 1e-9
8089
+ // in vector dot products.
8090
+ var GEOGRAPHIC_VERTICES = [
8091
+ [76.8810628, 149.4509913],
8092
+ [-27.9527772, 97.3570035],
8093
+ [-6.6370473, -18.8522325],
8094
+ [-22.9282364, -133.2827588]
8095
+ ];
8096
+
8097
+ // D3 Imago's vertex-oriented tetrahedral block. Each entry contains the
8098
+ // spherical center, local meridian, planar rotation and planar center.
8099
+ var FACET_DEFS = [
8100
+ [0, SQRT3, HALF_PI, 0, 0, -HALF_PI],
8101
+ [0, -SQRT3, -ASIN_ONE_THIRD, 0, Math.PI, HALF_PI],
8102
+ [3, 0, -ASIN_ONE_THIRD, 2 * Math.PI / 3, Math.PI, 5 * Math.PI / 6],
8103
+ [-3, 0, -ASIN_ONE_THIRD, -2 * Math.PI / 3, Math.PI, Math.PI / 6]
8104
+ ];
8105
+
8106
+ function registerNarukawa2022Projection(mproj) {
8107
+ if (!mproj || mproj.internal.pj_list.narukawa2022) return;
8108
+ mproj.pj_add(function(P) {
8109
+ initNarukawa2022(P);
8110
+ }, 'narukawa2022', 'Narukawa 2022 tetrahedral world map');
8111
+ }
8112
+
8113
+ function getNarukawa2022Engine() {
8114
+ if (!engines.default) {
8115
+ engines.default = createNarukawa2022Engine();
8116
+ }
8117
+ return engines.default;
8118
+ }
8119
+
8120
+ function initNarukawa2022(P) {
8121
+ var engine = getNarukawa2022Engine();
8122
+ P.es = 0;
8123
+ P.inv = null;
8124
+ P.fwd = function(lp, xy) {
8125
+ var p = engine.forward(lp.lam, lp.phi);
8126
+ xy.x = p[0];
8127
+ xy.y = p[1];
8128
+ };
8129
+ P.__projection_topology = engine.getTopology(P.lam0 * R2D$1);
8130
+ P.__projected_outline = engine.outline;
8131
+ }
8132
+
8133
+ function createNarukawa2022Engine() {
8134
+ var orientation = createPublishedOrientation();
8135
+ var facets = FACET_DEFS.map(function(def, i) {
8136
+ return {
8137
+ id: i,
8138
+ x: def[0],
8139
+ y: def[1],
8140
+ lat: def[2],
8141
+ lon: def[3],
8142
+ meridian: def[4],
8143
+ rotation: def[5]
8144
+ };
8145
+ });
8146
+ var outline = [[
8147
+ [XMIN * EDGE_SCALE, YMIN * EDGE_SCALE],
8148
+ [XMAX * EDGE_SCALE, YMIN * EDGE_SCALE],
8149
+ [XMAX * EDGE_SCALE, YMAX * EDGE_SCALE],
8150
+ [XMIN * EDGE_SCALE, YMAX * EDGE_SCALE],
8151
+ [XMIN * EDGE_SCALE, YMIN * EDGE_SCALE]
8152
+ ]];
8153
+
8154
+ return {
8155
+ forward: forward,
8156
+ inverse: inverse,
8157
+ outline: outline,
8158
+ getTopology: getTopology
8159
+ };
8160
+
8161
+ function forward(lam, phi) {
8162
+ var state = projectState(lam, phi);
8163
+ return [state.x * EDGE_SCALE, state.y * EDGE_SCALE];
8164
+ }
8165
+
8166
+ // Internal inverse used only for tracing the geographic rectangular cut.
8167
+ // P.inv remains null because the full Mapshaper inverse path is unsupported.
8168
+ function inverse(x, y) {
8169
+ var p = invertLayout(x / EDGE_SCALE, y / EDGE_SCALE, facets);
8170
+ if (!p) return null;
8171
+ return fromCanonical(p[0], p[1], orientation);
8172
+ }
8173
+
8174
+ function projectState(lam, phi) {
8175
+ var p = toCanonical(lam, phi, orientation);
8176
+ var state = projectCanonical(p[0], p[1], facets);
8177
+ state.region = encodeRegion(state);
8178
+ return state;
8179
+ }
8180
+
8181
+ function getTopology(lon0) {
8182
+ var paths = createCutPaths(inverse, lon0);
8183
+ return {
8184
+ regions: createRegionIndex(),
8185
+ seams: [{
8186
+ type: 'cut',
8187
+ paths: paths
8188
+ }],
8189
+ findRegion: findRegion,
8190
+ findTransitionRegion: findRegion,
8191
+ outline: outline.map(function(ring) {
8192
+ return ring.map(function(p) {
8193
+ return p.concat();
8194
+ });
8195
+ })
8196
+ };
8197
+
8198
+ function findRegion(lon, lat) {
8199
+ var lam = normalizeRadians((lon - lon0) * D2R$1);
8200
+ return projectState(lam, lat * D2R$1).region;
8201
+ }
8202
+ }
8203
+ }
8204
+
8205
+ function projectCanonical(lam, phi, facets) {
8206
+ var facet = findForwardFacet(lam, phi, facets);
8207
+ var relative = obliquifySpherical(phi, lam, facet);
8208
+ var sector = Math.floor((relative[1] + Math.PI / 3) / (2 * Math.PI / 3));
8209
+ var base = sector * 2 * Math.PI / 3;
8210
+ var polar = narukawaFaceForward(relative[1] - base, relative[0]);
8211
+ var angle = polar[1] + facet.rotation + base / 2;
8212
+ var x = polar[0] * Math.cos(angle) + facet.x;
8213
+ var y = polar[0] * Math.sin(angle) + facet.y;
8214
+ var oob = 0;
8215
+ var folded = 0;
8216
+ var wrap = 0;
8217
+
8218
+ if (Math.abs(x) > 3 + EPS) {
8219
+ x = 2 * facet.x - x;
8220
+ y = -y;
8221
+ oob = 1;
8222
+ } else if (Math.abs(y) > SQRT3 + EPS) {
8223
+ x = -x;
8224
+ y = BLOCK_HEIGHT * Math.sign(y) - y;
8225
+ oob = 2;
8226
+ }
8227
+
8228
+ var qx = y;
8229
+ var qy = -x;
8230
+ if (qy > EPS) {
8231
+ qx = BLOCK_HEIGHT - qx;
8232
+ qy = -qy;
8233
+ folded = 1;
8234
+ }
8235
+ qx += LAYOUT_SHIFT;
8236
+ if (qx < 0) {
8237
+ qx += 2 * BLOCK_HEIGHT;
8238
+ wrap = 1;
8239
+ }
8240
+ x = qx - BLOCK_HEIGHT;
8241
+ y = qy + 1.5;
8242
+ x = clamp$2(x, XMIN, XMAX);
8243
+ y = clamp$2(y, YMIN, YMAX);
8244
+ return {
8245
+ x: x,
8246
+ y: y,
8247
+ facet: facet.id,
8248
+ sector: mod(sector, 3),
8249
+ oob: oob,
8250
+ folded: folded,
8251
+ wrap: wrap
8252
+ };
8253
+ }
8254
+
8255
+ function findForwardFacet(lam, phi, facets) {
8256
+ var best = null;
8257
+ var bestLat = -Infinity;
8258
+ for (var i = 0; i < 4; i++) {
8259
+ var facet = facets[i];
8260
+ var relative = obliquifySpherical(phi, lam, facet);
8261
+ if (relative[0] > bestLat) {
8262
+ bestLat = relative[0];
8263
+ best = facet;
8264
+ }
8265
+ }
8266
+ return best;
8267
+ }
8268
+
8269
+ function narukawaFaceForward(lam, phi) {
8270
+ var a = lam - Math.asin(Math.sin(lam) / SQRT3);
8271
+ var theta = Math.atan(2 * SQRT3 / Math.PI * a);
8272
+ var denominator = 2 + SQRT2 * Math.tan(phi);
8273
+ var q = denominator > 0 ? (2 + Math.cos(lam)) / denominator : 0;
8274
+ var r = q * SQRT3 / Math.cos(theta);
8275
+ return [r, theta];
8276
+ }
8277
+
8278
+ function narukawaFaceInverse(r, theta) {
8279
+ var target = Math.tan(theta) * Math.PI / (2 * SQRT3);
8280
+ var lo = -Math.PI / 3;
8281
+ var hi = Math.PI / 3;
8282
+ var lam;
8283
+ for (var i = 0; i < 55; i++) {
8284
+ lam = (lo + hi) / 2;
8285
+ var a = lam - Math.asin(Math.sin(lam) / SQRT3);
8286
+ if (a < target) lo = lam;
8287
+ else hi = lam;
8288
+ }
8289
+ lam = (lo + hi) / 2;
8290
+ var q = r * Math.cos(theta) / SQRT3;
8291
+ var phi = q < EPS ? HALF_PI :
8292
+ Math.atan(((2 + Math.cos(lam)) / q - 2) / SQRT2);
8293
+ return [phi, lam];
8294
+ }
8295
+
8296
+ function invertLayout(x, y, facets) {
8297
+ var qx = x + BLOCK_HEIGHT;
8298
+ var qy = y - 1.5;
8299
+ var normalizedX = (qx - LAYOUT_SHIFT) / BLOCK_HEIGHT;
8300
+ if (normalizedX > 1.5) normalizedX -= 2;
8301
+ if (normalizedX > 0.5) {
8302
+ normalizedX = 1 - normalizedX;
8303
+ qy *= -1;
8304
+ }
8305
+ x = -qy;
8306
+ y = normalizedX * BLOCK_HEIGHT;
8307
+
8308
+ var facet = findInverseFacet(x, y, facets);
8309
+ var dx = x - facet.x;
8310
+ var dy = y - facet.y;
8311
+ var r = Math.hypot(dx, dy);
8312
+ var theta = normalizeRadians(Math.atan2(dy, dx) - facet.rotation);
8313
+ var base = Math.floor((theta + Math.PI / 6) / (Math.PI / 3)) *
8314
+ Math.PI / 3;
8315
+ var relative = narukawaFaceInverse(r, theta - base);
8316
+ relative[1] += base * 2;
8317
+ var p = deobliquifySpherical(relative[0], relative[1], facet);
8318
+ return [p[1], p[0]];
8319
+ }
8320
+
8321
+ function findInverseFacet(x, y, facets) {
8322
+ var best = null;
8323
+ var minDistance = Infinity;
8324
+ facets.forEach(function(facet) {
8325
+ var d = Math.hypot(x - facet.x, y - facet.y);
8326
+ if (d < minDistance) {
8327
+ minDistance = d;
8328
+ best = facet;
8329
+ }
8330
+ });
8331
+ return best;
8332
+ }
8333
+
8334
+ function createPublishedOrientation() {
8335
+ // Match the D3 Imago arrangement: the third published vertex is the
8336
+ // canonical southern vertex at longitude zero.
8337
+ var north = latLonToVector(GEOGRAPHIC_VERTICES[0]);
8338
+ var south = latLonToVector(GEOGRAPHIC_VERTICES[2]);
8339
+ var tangent = normalizeVector(subtract(
8340
+ south,
8341
+ multiply(north, dot$1(south, north))
8342
+ ));
8343
+ var x = tangent;
8344
+ var y = cross$1(north, tangent);
8345
+ return {x: x, y: y, z: north};
8346
+ }
8347
+
8348
+ function toCanonical(lam, phi, orientation) {
8349
+ var v = radiansToVector(lam, phi);
8350
+ return [
8351
+ Math.atan2(dot$1(v, orientation.y), dot$1(v, orientation.x)),
8352
+ Math.asin(clamp$2(dot$1(v, orientation.z), -1, 1))
8353
+ ];
8354
+ }
8355
+
8356
+ function fromCanonical(lam, phi, orientation) {
8357
+ var v = radiansToVector(lam, phi);
8358
+ var p = [
8359
+ orientation.x[0] * v[0] + orientation.y[0] * v[1] + orientation.z[0] * v[2],
8360
+ orientation.x[1] * v[0] + orientation.y[1] * v[1] + orientation.z[1] * v[2],
8361
+ orientation.x[2] * v[0] + orientation.y[2] * v[1] + orientation.z[2] * v[2]
8362
+ ];
8363
+ return [Math.atan2(p[1], p[0]), Math.asin(clamp$2(p[2], -1, 1))];
8364
+ }
8365
+
8366
+ // Adapted from Justin Kunimune's Imago implementation.
8367
+ function obliquifySpherical(lat, lon, pole) {
8368
+ var lat0 = pole.lat;
8369
+ var lon0 = pole.lon;
8370
+ var theta0 = pole.meridian;
8371
+ var lat1;
8372
+ var lon1;
8373
+ if (Math.abs(lat0 - HALF_PI) < EPS) {
8374
+ lat1 = lat;
8375
+ lon1 = lon - lon0;
8376
+ } else {
8377
+ lat1 = Math.asin(clamp$2(
8378
+ Math.sin(lat0) * Math.sin(lat) +
8379
+ Math.cos(lat0) * Math.cos(lat) * Math.cos(lon0 - lon),
8380
+ -1,
8381
+ 1
8382
+ ));
8383
+ var denominator = Math.cos(lat1);
8384
+ var value = denominator < EPS ? 1 :
8385
+ (Math.cos(lat0) * Math.sin(lat) -
8386
+ Math.sin(lat0) * Math.cos(lat) * Math.cos(lon0 - lon)) /
8387
+ denominator;
8388
+ lon1 = Math.acos(clamp$2(value, -1, 1)) - Math.PI;
8389
+ if (Math.sin(lon - lon0) > 0) lon1 = -lon1;
8390
+ }
8391
+ return [lat1, normalizeRadians(lon1 - theta0)];
8392
+ }
8393
+
8394
+ function deobliquifySpherical(lat, lon, pole) {
8395
+ var lat0 = pole.lat;
8396
+ var lon0 = pole.lon;
8397
+ lon += pole.meridian;
8398
+ var latOut = Math.asin(clamp$2(
8399
+ Math.sin(lat0) * Math.sin(lat) -
8400
+ Math.cos(lat0) * Math.cos(lon) * Math.cos(lat),
8401
+ -1,
8402
+ 1
8403
+ ));
8404
+ var lonOut;
8405
+ if (Math.abs(lat0 - HALF_PI) < EPS) {
8406
+ lonOut = lon + lon0;
8407
+ } else {
8408
+ var value = Math.sin(lat) / Math.cos(lat0) / Math.cos(latOut) -
8409
+ Math.tan(lat0) * Math.tan(latOut);
8410
+ if (Math.sin(lon) > 0) lonOut = lon0 + Math.acos(clamp$2(value, -1, 1));
8411
+ else lonOut = lon0 - Math.acos(clamp$2(value, -1, 1));
8412
+ }
8413
+ return [latOut, normalizeRadians(lonOut)];
8414
+ }
8415
+
8416
+ function createCutPaths(inverse, lon0) {
8417
+ // The rectangular edges become moderately curved geographic seams. Sampling
8418
+ // at about 0.03 degrees prevents straight mask chords from missing crossings.
8419
+ var n = 6000;
8420
+ var epsilon = 1e-8;
8421
+ var edges = [
8422
+ [[XMIN + epsilon, YMIN], [XMIN + epsilon, YMAX]],
8423
+ [[XMIN, YMAX - epsilon], [XMAX, YMAX - epsilon]],
8424
+ [[XMIN, YMIN + epsilon], [XMAX, YMIN + epsilon]]
8425
+ ];
8426
+ var paths = edges.reduce(function(memo, edge) {
8427
+ var path = [];
8428
+ for (var i = 0; i <= n; i++) {
8429
+ var t = i / n;
8430
+ var x = edge[0][0] + (edge[1][0] - edge[0][0]) * t;
8431
+ var y = edge[0][1] + (edge[1][1] - edge[0][1]) * t;
8432
+ var p = inverse(x * EDGE_SCALE, y * EDGE_SCALE);
8433
+ path.push([
8434
+ normalizeLongitude$1(p[0] * R2D$1 + lon0),
8435
+ p[1] * R2D$1
8436
+ ]);
8437
+ }
8438
+ return memo.concat(splitPathAtAntimeridian$1(path));
8439
+ }, []);
8440
+ return paths.map(function(part) {
8441
+ part.mask_width = 4e-5;
8442
+ return part;
8443
+ });
8444
+ }
8445
+
8446
+ function splitPathAtAntimeridian$1(path) {
8447
+ var paths = [];
8448
+ var part = [path[0]];
8449
+ for (var i = 1; i < path.length; i++) {
8450
+ var a = path[i - 1];
8451
+ var b = path[i];
8452
+ if (Math.abs(a[0] - b[0]) > 180) {
8453
+ var adjusted = b[0] + (b[0] < a[0] ? 360 : -360);
8454
+ var edge = a[0] < 0 ? -180 : 180;
8455
+ var t = (edge - a[0]) / (adjusted - a[0]);
8456
+ var lat = a[1] + (b[1] - a[1]) * t;
8457
+ part.push([edge, lat]);
8458
+ paths.push(part);
8459
+ part = [[-edge, lat], b];
8460
+ } else {
8461
+ part.push(b);
8462
+ }
8463
+ }
8464
+ if (part.length > 1) paths.push(part);
8465
+ return paths;
8466
+ }
8467
+
8468
+ function encodeRegion(state) {
8469
+ return ((((state.facet * 3 + state.sector) * 3 + state.oob) * 2 +
8470
+ state.folded) * 2 + state.wrap);
8471
+ }
8472
+
8473
+ function createRegionIndex() {
8474
+ var regions = [];
8475
+ for (var i = 0; i < 144; i++) {
8476
+ regions.push({id: i});
8477
+ }
8478
+ return regions;
8479
+ }
8480
+
8481
+ function latLonToVector(p) {
8482
+ return radiansToVector(p[1] * D2R$1, p[0] * D2R$1);
8483
+ }
8484
+
8485
+ function radiansToVector(lam, phi) {
8486
+ var cosPhi = Math.cos(phi);
8487
+ return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
8488
+ }
8489
+
8490
+ function subtract(a, b) {
8491
+ return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
8492
+ }
8493
+
8494
+ function multiply(a, k) {
8495
+ return [a[0] * k, a[1] * k, a[2] * k];
8496
+ }
8497
+
8498
+ function normalizeVector(a) {
8499
+ var k = 1 / Math.hypot(a[0], a[1], a[2]);
8500
+ return multiply(a, k);
8501
+ }
8502
+
8503
+ function cross$1(a, b) {
8504
+ return [
8505
+ a[1] * b[2] - a[2] * b[1],
8506
+ a[2] * b[0] - a[0] * b[2],
8507
+ a[0] * b[1] - a[1] * b[0]
8508
+ ];
8509
+ }
8510
+
8511
+ function dot$1(a, b) {
8512
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
8513
+ }
8514
+
8515
+ function mod(a, n) {
8516
+ return (a % n + n) % n;
8517
+ }
8518
+
8519
+ function normalizeRadians(lam) {
8520
+ while (lam > Math.PI) lam -= 2 * Math.PI;
8521
+ while (lam < -Math.PI) lam += 2 * Math.PI;
8522
+ return lam;
8523
+ }
8524
+
8525
+ function normalizeLongitude$1(lon) {
8526
+ while (lon > 180) lon -= 360;
8527
+ while (lon < -180) lon += 360;
8528
+ return lon;
8529
+ }
8530
+
8531
+ function clamp$2(value, min, max) {
8532
+ return Math.max(min, Math.min(max, value));
8533
+ }
8534
+
8535
+ var Narukawa2022 = /*#__PURE__*/Object.freeze({
8536
+ __proto__: null,
8537
+ getNarukawa2022Engine: getNarukawa2022Engine,
8538
+ registerNarukawa2022Projection: registerNarukawa2022Projection
8539
+ });
8540
+
6290
8541
  var mproj$1 = require$1('mproj');
8542
+ registerDymaxionProjections(mproj$1);
8543
+ registerButterflyProjections(mproj$1);
8544
+ registerNarukawa2022Projection(mproj$1);
6291
8545
 
6292
8546
  var asyncLoader = null;
6293
8547
 
@@ -6456,9 +8710,18 @@
6456
8710
  if (!defn) {
6457
8711
  stop$1("Unknown projection definition:", str);
6458
8712
  }
8713
+ if (utils.isString(defn)) {
8714
+ defn = addDefaultProjectionParams(defn);
8715
+ }
6459
8716
  return defn;
6460
8717
  }
6461
8718
 
8719
+ function addDefaultProjectionParams(defn) {
8720
+ var oceanic = /(^| )\+proj=(igh_o|imoll_o)(?= |$)/.test(defn);
8721
+ var hasLon0 = /(^| )\+lon_0=/.test(defn);
8722
+ return oceanic && !hasLon0 ? defn + ' +lon_0=-160' : defn;
8723
+ }
8724
+
6462
8725
  function looksLikeInitString(str) {
6463
8726
  return /^(esri|epsg|nad83|nad27):[0-9]+$/i.test(String(str));
6464
8727
  }
@@ -9266,7 +11529,7 @@
9266
11529
  function layerNameIsUnsafeFilename(name) {
9267
11530
  name = String(name);
9268
11531
  // path separators (directory traversal on any OS) or NUL
9269
- if (/[\/\\\0]/.test(name)) return true;
11532
+ if (/[/\\\0]/.test(name)) return true;
9270
11533
  // Windows drive-relative prefix, e.g. "C:foo" can write to another drive
9271
11534
  if (/^[A-Za-z]:/.test(name)) return true;
9272
11535
  return false;
@@ -39161,7 +41424,9 @@ ${svg}
39161
41424
  return finalizeImportedDataset(dataset, dataFmt, data, opts);
39162
41425
  }
39163
41426
 
39164
- async function importContentAsync(obj, opts) {
41427
+ // Canonical content import interface. Always resolves to an array because some
41428
+ // formats (notably GeoPackage) may contain datasets that cannot be merged.
41429
+ async function importDatasetsFromContent(obj, opts) {
39165
41430
  var dataset, dataFmt, data;
39166
41431
  opts = opts || {};
39167
41432
  if (obj.fgb) {
@@ -39185,14 +41450,16 @@ ${svg}
39185
41450
  data = obj[dataFmt];
39186
41451
  dataset = await importImageRaster(obj, opts);
39187
41452
  } else {
39188
- return importContent(obj, opts);
39189
- }
39190
- if (Array.isArray(dataset)) {
39191
- return dataset.map(function(ds) {
39192
- return finalizeImportedDataset(ds, dataFmt, data, opts);
39193
- });
41453
+ return normalizeImportedDatasets$1(importContent(obj, opts));
39194
41454
  }
39195
- return finalizeImportedDataset(dataset, dataFmt, data, opts);
41455
+ return normalizeImportedDatasets$1(dataset).map(function(ds) {
41456
+ return finalizeImportedDataset(ds, dataFmt, data, opts);
41457
+ });
41458
+ }
41459
+
41460
+ // Compatibility facade for callers that expect one dataset when possible.
41461
+ async function importContentAsync(obj, opts) {
41462
+ return denormalizeImportedDatasets$1(await importDatasetsFromContent(obj, opts));
39196
41463
  }
39197
41464
 
39198
41465
  // Deprecated (included for compatibility with older tests)
@@ -39203,6 +41470,14 @@ ${svg}
39203
41470
  return importContent(input, opts);
39204
41471
  }
39205
41472
 
41473
+ function normalizeImportedDatasets$1(datasetOrArray) {
41474
+ return Array.isArray(datasetOrArray) ? datasetOrArray : [datasetOrArray];
41475
+ }
41476
+
41477
+ function denormalizeImportedDatasets$1(datasets) {
41478
+ return datasets.length == 1 ? datasets[0] : datasets;
41479
+ }
41480
+
39206
41481
 
39207
41482
  function importShapefile(obj, opts) {
39208
41483
  var shpSrc = obj.shp.content || obj.shp.filename, // read from a file if (binary) content is missing
@@ -39285,6 +41560,7 @@ ${svg}
39285
41560
  __proto__: null,
39286
41561
  importContent: importContent,
39287
41562
  importContentAsync: importContentAsync,
41563
+ importDatasetsFromContent: importDatasetsFromContent,
39288
41564
  importFileContent: importFileContent
39289
41565
  });
39290
41566
 
@@ -39453,8 +41729,7 @@ ${svg}
39453
41729
  var dataset, datasets, target;
39454
41730
 
39455
41731
  if (opts.stdin) {
39456
- dataset = await importFileAsync('/dev/stdin', opts);
39457
- datasets = normalizeImportedDatasets(dataset);
41732
+ datasets = await importDatasetsFromFile('/dev/stdin', opts);
39458
41733
  catalog.addDatasets(datasets);
39459
41734
  if (datasets.length > 1) {
39460
41735
  catalog.setDefaultTargets(datasets.map(function(ds) {
@@ -39492,11 +41767,11 @@ ${svg}
39492
41767
  }
39493
41768
 
39494
41769
  if (files.length == 1) {
39495
- dataset = await importFileAsync(files[0], opts);
41770
+ datasets = await importDatasetsFromFile(files[0], opts);
39496
41771
  } else {
39497
41772
  dataset = await importFilesTogetherAsync(files, opts);
41773
+ datasets = [dataset];
39498
41774
  }
39499
- datasets = normalizeImportedDatasets(dataset);
39500
41775
  datasets = validateAndCleanGpkgSelection(datasets, opts);
39501
41776
 
39502
41777
  if (opts.merge_files && files.length > 1) {
@@ -39613,9 +41888,12 @@ ${svg}
39613
41888
  });
39614
41889
  }
39615
41890
 
39616
- // Let the web UI replace importFile() with a browser-friendly version
41891
+ // Let the web UI replace path-based imports with a browser-friendly resolver.
39617
41892
  function replaceImportFile(func) {
39618
41893
  _importFile = func;
41894
+ _importDatasetsFromFile = async function(path, opts) {
41895
+ return normalizeImportedDatasets(await func(path, opts));
41896
+ };
39619
41897
  }
39620
41898
 
39621
41899
  function importFile(path, opts) {
@@ -39623,69 +41901,29 @@ ${svg}
39623
41901
  }
39624
41902
 
39625
41903
  async function importFileAsync(path, opts) {
39626
- return _importFileAsync(path, opts);
41904
+ return denormalizeImportedDatasets(await importDatasetsFromFile(path, opts));
39627
41905
  }
39628
41906
 
39629
- var _importFile = function(path, opts) {
39630
- var fileType = guessInputFileType(path),
39631
- input = {},
39632
- encoding = opts && opts.encoding || null,
39633
- cache = opts && opts.input || null,
39634
- cached = cache && (path in cache),
39635
- content;
39636
-
39637
- cli.checkFileExists(path, cache);
39638
-
39639
- if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf' ||
39640
- fileType == 'gpkg') && !cached) {
39641
- // these file types are read incrementally
39642
- content = null;
39643
-
39644
- } else if (fileType && isSupportedBinaryInputType(path)) {
39645
- content = cli.readFile(path, null, cache);
39646
- if (utils.isString(content)) {
39647
- // Fix for issue #264 (applyCommands() input is file path instead of binary content)
39648
- stop$1('Expected binary content, received a string');
39649
- }
39650
-
39651
- } else if (fileType) { // string type, e.g. kml, geojson
39652
- content = cli.readFile(path, encoding || 'utf-8', cache);
39653
-
39654
- } else if (getFileExtension(path) == 'gz') {
39655
- var pathgz = path;
39656
- path = pathgz.replace(/\.gz$/, '');
39657
- fileType = guessInputFileType(path);
39658
- if (!fileType) {
39659
- stop$1('Unrecognized file type:', path);
39660
- }
39661
- content = gunzipSync(cli.readFile(pathgz, null, cache), path);
39662
-
39663
- } else { // type can't be inferred from filename -- try reading as text
39664
- content = cli.readFile(path, encoding || 'utf-8', cache);
39665
- fileType = guessInputContentType(content);
39666
- if (fileType == 'text' && content.indexOf('\ufffd') > -1) {
39667
- // invalidate string data that contains the 'replacement character'
39668
- fileType = null;
39669
- }
39670
- }
41907
+ // Canonical path import interface.
41908
+ async function importDatasetsFromFile(path, opts) {
41909
+ return _importDatasetsFromFile(path, opts);
41910
+ }
39671
41911
 
39672
- if (!fileType) {
39673
- stop$1(getUnsupportedFileMessage(path));
39674
- }
39675
- input[fileType] = {filename: path, content: content};
39676
- content = null; // for g.c.
39677
- if (fileType == 'shp' || fileType == 'dbf') {
39678
- readShapefileAuxFiles(path, input, cache);
39679
- } else if (isRasterImageInputType(fileType)) {
39680
- readRasterImageAuxFiles(path, input, cache);
39681
- }
39682
- if (fileType == 'shp' && !input.dbf) {
39683
- message(utils.format("[%s] .dbf file is missing - shapes imported without attribute data.", path));
39684
- }
41912
+ var _importFile = function(path, opts) {
41913
+ var input = prepareImportFile(path, opts);
39685
41914
  return importContent(input, opts);
39686
41915
  };
39687
41916
 
39688
- async function _importFileAsync(path, opts) {
41917
+ var _importDatasetsFromFile = async function(path, opts) {
41918
+ var input = prepareImportFile(path, opts);
41919
+ return importDatasetsFromContent(input, opts);
41920
+ };
41921
+
41922
+ // Read a file (or cache entry) and collect any sidecars into the normalized
41923
+ // content-group shape consumed by the content importers.
41924
+ // File acquisition is currently synchronous in both import paths; only some
41925
+ // format parsers require asynchronous work.
41926
+ function prepareImportFile(path, opts) {
39689
41927
  var fileType = guessInputFileType(path),
39690
41928
  input = {},
39691
41929
  encoding = opts && opts.encoding || null,
@@ -39695,7 +41933,8 @@ ${svg}
39695
41933
 
39696
41934
  cli.checkFileExists(path, cache);
39697
41935
 
39698
- if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf') && !cached) {
41936
+ if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf' ||
41937
+ fileType == 'gpkg') && !cached) {
39699
41938
  // these file types are read incrementally
39700
41939
  content = null;
39701
41940
 
@@ -39738,37 +41977,10 @@ ${svg}
39738
41977
  if (fileType == 'shp' && !input.dbf) {
39739
41978
  message(utils.format("[%s] .dbf file is missing - shapes imported without attribute data.", path));
39740
41979
  }
39741
- return fileType == 'gpkg' || fileType == 'fgb' || fileType == 'parquet' || fileType == 'geotiff' || isRasterImageInputType(fileType) ? importContentAsync(input, opts) : importContent(input, opts);
39742
- }
39743
-
39744
- // Import multiple files to a single dataset
39745
- function importFilesTogether(files, opts) {
39746
- var unbuiltTopology = false;
39747
- var datasets = files.reduce(function(memo, fname) {
39748
- // import without topology or snapping
39749
- var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
39750
- var imported = normalizeImportedDatasets(importFile(fname, importOpts));
39751
- // check if dataset contains non-topological paths
39752
- // TODO: may also need to rebuild topology if multiple topojson files are merged
39753
- imported.forEach(function(dataset) {
39754
- if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
39755
- unbuiltTopology = true;
39756
- }
39757
- memo.push(dataset);
39758
- });
39759
- return memo;
39760
- }, []);
39761
- var combined = mergeDatasets(datasets);
39762
- // Build topology, if needed
39763
- // TODO: consider updating topology of TopoJSON files instead of concatenating arcs
39764
- // (but problem of mismatched coordinates due to quantization in input files.)
39765
- if (unbuiltTopology && !opts.no_topology) {
39766
- cleanPathsAfterImport(combined, opts);
39767
- buildTopology(combined);
39768
- }
39769
- return combined;
41980
+ return input;
39770
41981
  }
39771
41982
 
41983
+ // Import multiple files to a single dataset.
39772
41984
  async function importFilesTogetherAsync(files, opts) {
39773
41985
  var unbuiltTopology = false;
39774
41986
  var datasets = [];
@@ -39776,7 +41988,7 @@ ${svg}
39776
41988
  for (var fname of files) {
39777
41989
  // import without topology or snapping
39778
41990
  var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
39779
- var imported = normalizeImportedDatasets(await importFileAsync(fname, importOpts));
41991
+ var imported = await importDatasetsFromFile(fname, importOpts);
39780
41992
  imported.forEach(function(dataset) {
39781
41993
  if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
39782
41994
  unbuiltTopology = true;
@@ -39838,6 +42050,10 @@ ${svg}
39838
42050
  return Array.isArray(datasetOrArray) ? datasetOrArray : [datasetOrArray];
39839
42051
  }
39840
42052
 
42053
+ function denormalizeImportedDatasets(datasets) {
42054
+ return datasets.length == 1 ? datasets[0] : datasets;
42055
+ }
42056
+
39841
42057
  function normalizeImportedTarget(datasets) {
39842
42058
  if (datasets.length == 1) return datasets[0];
39843
42059
  return {
@@ -39937,9 +42153,9 @@ ${svg}
39937
42153
 
39938
42154
  var FileImport = /*#__PURE__*/Object.freeze({
39939
42155
  __proto__: null,
42156
+ importDatasetsFromFile: importDatasetsFromFile,
39940
42157
  importFile: importFile,
39941
42158
  importFileAsync: importFileAsync,
39942
- importFilesTogether: importFilesTogether,
39943
42159
  importFilesTogetherAsync: importFilesTogetherAsync,
39944
42160
  replaceImportFile: replaceImportFile
39945
42161
  });
@@ -39969,31 +42185,37 @@ ${svg}
39969
42185
  return /[$][{]/.test(name);
39970
42186
  }
39971
42187
 
39972
- function findCommandSource(sourceName, catalog, opts) {
42188
+ async function findCommandSourceAsync(sourceName, catalog, opts) {
39973
42189
  var source = catalog.findSingleLayer(sourceName);
39974
- var sourceDataset;
42190
+ var sourceDatasets;
39975
42191
  if (!source) {
39976
- // assuming opts.source is a filename
39977
- // don't need to build topology, because:
39978
- // join -- don't need topology
39979
- // clip/erase -- topology is built later, when datasets are combined
39980
- sourceDataset = importFile(sourceName, utils.defaults({no_topology: true}, opts));
39981
- if (!sourceDataset) {
39982
- stop$1(utils.format('Unable to find source [%s]', sourceName));
39983
- } else if (sourceDataset.layers.length > 1) {
39984
- stop$1('Multiple-layer sources are not supported');
39985
- }
39986
- // mark as disposable to indicate that data can be mutated
39987
- source = {dataset: sourceDataset, layer: sourceDataset.layers[0], disposable: true};
42192
+ // Source-file topology is built later if it is needed by the command.
42193
+ sourceDatasets = await importDatasetsFromFile(sourceName, utils.defaults({no_topology: true}, opts));
42194
+ source = getImportedSource(sourceDatasets, sourceName);
39988
42195
  }
39989
42196
  return source;
39990
42197
  }
39991
42198
 
42199
+ function getImportedSource(sourceDatasets, sourceName) {
42200
+ if (sourceDatasets.length != 1) {
42201
+ if (sourceDatasets.length > 1) {
42202
+ stop$1('Multiple-dataset sources are not supported');
42203
+ }
42204
+ stop$1(utils.format('Unable to find source [%s]', sourceName));
42205
+ }
42206
+ var sourceDataset = sourceDatasets[0];
42207
+ if (sourceDataset.layers.length > 1) {
42208
+ stop$1('Multiple-layer sources are not supported');
42209
+ }
42210
+ // mark as disposable to indicate that data can be mutated
42211
+ return {dataset: sourceDataset, layer: sourceDataset.layers[0], disposable: true};
42212
+ }
42213
+
39992
42214
  var SourceUtils = /*#__PURE__*/Object.freeze({
39993
42215
  __proto__: null,
39994
42216
  convertInterpolatedName: convertInterpolatedName,
39995
42217
  convertSourceName: convertSourceName,
39996
- findCommandSource: findCommandSource
42218
+ findCommandSourceAsync: findCommandSourceAsync
39997
42219
  });
39998
42220
 
39999
42221
  // Catalog contains zero or more multi-layer datasets
@@ -41375,7 +43597,7 @@ ${svg}
41375
43597
 
41376
43598
  function getPlanarFilter(interval) {
41377
43599
  return function(a, b) {
41378
- return distance2D(a[0], a[1], b[0], b[1]) <= interval;
43600
+ return distance2D$1(a[0], a[1], b[0], b[1]) <= interval;
41379
43601
  };
41380
43602
  }
41381
43603
 
@@ -41808,7 +44030,7 @@ ${svg}
41808
44030
  }
41809
44031
 
41810
44032
  function probeIsExposed(index, skipA, skipB, vx, vy, p) {
41811
- var r = distance2D(vx, vy, p[0], p[1]);
44033
+ var r = distance2D$1(vx, vy, p[0], p[1]);
41812
44034
  if (!(r > 0)) return false;
41813
44035
  var r2 = r * r * 0.98 * 0.98;
41814
44036
  var px = p[0], py = p[1];
@@ -41848,6 +44070,13 @@ ${svg}
41848
44070
  return new GeographicLib.Geodesic.Geodesic(P.a, f);
41849
44071
  }
41850
44072
 
44073
+ function getGeodesicDistanceFunction(P) {
44074
+ var geod = getGeodesic(P);
44075
+ return function(lng, lat, lng2, lat2) {
44076
+ return geod.Inverse(lat, lng, lat2, lng2).s12;
44077
+ };
44078
+ }
44079
+
41851
44080
  function interpolatePoint2D(ax, ay, bx, by, k) {
41852
44081
  var j = 1 - k;
41853
44082
  return [ax * j + bx * k, ay * j + by * k];
@@ -41950,6 +44179,7 @@ ${svg}
41950
44179
  bearingDegrees2D: bearingDegrees2D,
41951
44180
  getBearingFunction: getBearingFunction,
41952
44181
  getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
44182
+ getGeodesicDistanceFunction: getGeodesicDistanceFunction,
41953
44183
  getGeodeticSegmentFunction: getGeodeticSegmentFunction,
41954
44184
  getInterpolationFunction: getInterpolationFunction,
41955
44185
  getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
@@ -43390,7 +45620,7 @@ ${svg}
43390
45620
 
43391
45621
  function stopIfBufferReachesPole(shape, dist) {
43392
45622
  var maxAbsLat = 0;
43393
- var angularDist = dist / R$3 * R2D$1;
45623
+ var angularDist = dist / R$3 * R2D$6;
43394
45624
  (shape || []).forEach(function(path) {
43395
45625
  latLngPathIter.init(path);
43396
45626
  while (latLngPathIter.hasNext()) {
@@ -43881,11 +46111,11 @@ ${svg}
43881
46111
  if (a[0] === b[0] && a[1] === b[1]) continue;
43882
46112
  bearing = bearingDegrees2D(a[0], a[1], b[0], b[1]);
43883
46113
  probe = getOffsetPoint(a[0], a[1], bearing - 90, dist);
43884
- rUnits = distance2D(a[0], a[1], probe[0], probe[1]);
46114
+ rUnits = distance2D$1(a[0], a[1], probe[0], probe[1]);
43885
46115
  // perpendicular stab lines every ~0.35r along the band, so cuts
43886
46116
  // affecting only part of a long segment cannot slip between them
43887
46117
  m = Math.max(1, Math.min(60,
43888
- Math.ceil(distance2D(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
46118
+ Math.ceil(distance2D$1(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
43889
46119
  for (ti = 0; ti < m; ti++) {
43890
46120
  t = (ti + 0.5) / m;
43891
46121
  base = [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
@@ -43919,14 +46149,14 @@ ${svg}
43919
46149
  function findAtRiskSegments(verts, dist) {
43920
46150
  var n = verts.length;
43921
46151
  var probe0 = getOffsetPoint(verts[0][0], verts[0][1], 0, dist);
43922
- var rUnits = distance2D(verts[0][0], verts[0][1], probe0[0], probe0[1]);
46152
+ var rUnits = distance2D$1(verts[0][0], verts[0][1], probe0[0], probe0[1]);
43923
46153
  var cumLen = new Float64Array(n);
43924
46154
  var riskVert = new Uint8Array(n);
43925
46155
  var any = false;
43926
46156
  var i, j, k;
43927
46157
  for (i = 1; i < n; i++) {
43928
46158
  cumLen[i] = cumLen[i - 1] +
43929
- distance2D(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
46159
+ distance2D$1(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
43930
46160
  }
43931
46161
  // the ring's start radial and cap-closing radial can cut nearby bands
43932
46162
  riskVert[0] = riskVert[n - 1] = 1;
@@ -43998,7 +46228,7 @@ ${svg}
43998
46228
  j = ids[k];
43999
46229
  if (j <= i) continue;
44000
46230
  var pathDist = cumLen[j] - cumLen[i];
44001
- var d = distance2D(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
46231
+ var d = distance2D$1(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
44002
46232
  // 0.8 flags vertex pairs whose intervening path bends by
44003
46233
  // ~130 degrees or more (a circle's chord/arc ratio is 2/pi,
44004
46234
  // so even gentle loops qualify)
@@ -44050,7 +46280,7 @@ ${svg}
44050
46280
  // at the near end is seeded by an upward ray over those same edges.
44051
46281
  function getRingStabber(rings, dist) {
44052
46282
  var probe0 = getOffsetPoint(rings[0][0][0], rings[0][0][1], 0, dist);
44053
- var slabW = distance2D(rings[0][0][0], rings[0][0][1], probe0[0], probe0[1]);
46283
+ var slabW = distance2D$1(rings[0][0][0], rings[0][0][1], probe0[0], probe0[1]);
44054
46284
  var ex = [], ey = [], fx = [], fy = []; // edge data
44055
46285
  var slabs = new Map();
44056
46286
  rings.forEach(function(ring) {
@@ -44150,8 +46380,8 @@ ${svg}
44150
46380
  var eps = dist * 1e-4;
44151
46381
  var aIn = getOffsetPoint(a[0], a[1], bearing, eps);
44152
46382
  var bIn = getOffsetPoint(b[0], b[1], bearing - 180, eps);
44153
- if (distance2D(a[0], a[1], b[0], b[1]) <
44154
- distance2D(a[0], a[1], aIn[0], aIn[1]) * 3) {
46383
+ if (distance2D$1(a[0], a[1], b[0], b[1]) <
46384
+ distance2D$1(a[0], a[1], aIn[0], aIn[1]) * 3) {
44155
46385
  return; // segment too short to inset
44156
46386
  }
44157
46387
  rings.push([
@@ -44548,7 +46778,7 @@ ${svg}
44548
46778
  }
44549
46779
 
44550
46780
  function shallowAngleJoin(a, b, cx, cy, dist) {
44551
- var gap = distance2D(a[0], a[1], b[0], b[1]);
46781
+ var gap = distance2D$1(a[0], a[1], b[0], b[1]);
44552
46782
  var radius = getJoinExtensionDistance(cx, cy, a, b, dist);
44553
46783
  if (gap > radius * 1e-3) return null;
44554
46784
  return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
@@ -45507,12 +47737,12 @@ ${svg}
45507
47737
  }
45508
47738
 
45509
47739
  function projectBufferPoint(x, y, spherical) {
45510
- return spherical ? [x * D2R$1 * R$3, Math.log(Math.tan(Math.PI / 4 + y * D2R$1 / 2)) * R$3] :
47740
+ return spherical ? [x * D2R$7 * R$3, Math.log(Math.tan(Math.PI / 4 + y * D2R$7 / 2)) * R$3] :
45511
47741
  [x, y];
45512
47742
  }
45513
47743
 
45514
47744
  function unprojectBufferPoint(x, y, spherical) {
45515
- return spherical ? [x / (D2R$1 * R$3), (2 * Math.atan(Math.exp(y / R$3)) - Math.PI / 2) / (D2R$1)] :
47745
+ return spherical ? [x / (D2R$7 * R$3), (2 * Math.atan(Math.exp(y / R$3)) - Math.PI / 2) / (D2R$7)] :
45516
47746
  [x, y];
45517
47747
  }
45518
47748
 
@@ -50183,7 +52413,7 @@ ${svg}
50183
52413
  var maxAbsLat = Math.max(Math.abs(bounds.ymin), Math.abs(bounds.ymax));
50184
52414
  var maxPositiveDistance = getMaxPositiveBufferDistance(lyr, dataset, opts);
50185
52415
  if (!(maxPositiveDistance > 0)) return false;
50186
- return maxAbsLat + maxPositiveDistance / R$3 * R2D$1 >= 90 - POLAR_BUFFER_MARGIN_DEGREES;
52416
+ return maxAbsLat + maxPositiveDistance / R$3 * R2D$6 >= 90 - POLAR_BUFFER_MARGIN_DEGREES;
50187
52417
  }
50188
52418
 
50189
52419
  function getMaxPositiveBufferDistance(lyr, dataset, opts) {
@@ -51435,7 +53665,7 @@ ${svg}
51435
53665
  }
51436
53666
 
51437
53667
  function getCoordinateDistance(distance, arcs) {
51438
- return arcs.isPlanar() ? distance : distance / R$3 * R2D$1;
53668
+ return arcs.isPlanar() ? distance : distance / R$3 * R2D$6;
51439
53669
  }
51440
53670
 
51441
53671
  // @shapeIndex: chunk-bounds index of the source shape (see buildShapeSegmentIndex)
@@ -52103,7 +54333,8 @@ ${svg}
52103
54333
  // TODO: consider projections that may or may not be aligned,
52104
54334
  // depending on parameters
52105
54335
  if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
52106
- 'ocea,omerc,tmerc,etmerc,nicol')) {
54336
+ 'ocea,omerc,tmerc,etmerc,nicol,dymaxion,dymaxion2,butterfly,butterfly2,' +
54337
+ 'cahill_keyes,narukawa2022')) {
52107
54338
  return false;
52108
54339
  }
52109
54340
  if (isAzimuthal(P)) {
@@ -52155,16 +54386,16 @@ ${svg}
52155
54386
  }
52156
54387
 
52157
54388
  function getRotationFunction2(rotation, inv) {
52158
- var a = (rotation[0] || 0) * D2R$1,
52159
- b = (rotation[1] || 0) * D2R$1,
52160
- c = (rotation[2] || 0) * D2R$1;
54389
+ var a = (rotation[0] || 0) * D2R$7,
54390
+ b = (rotation[1] || 0) * D2R$7,
54391
+ c = (rotation[2] || 0) * D2R$7;
52161
54392
  return function(p) {
52162
- p[0] *= D2R$1;
52163
- p[1] *= D2R$1;
54393
+ p[0] *= D2R$7;
54394
+ p[1] *= D2R$7;
52164
54395
  var rotate = inv ? rotatePointInv : rotatePoint;
52165
54396
  rotate(p, a, b, c);
52166
- p[0] *= R2D$1;
52167
- p[1] *= R2D$1;
54397
+ p[0] *= R2D$6;
54398
+ p[1] *= R2D$6;
52168
54399
  return p;
52169
54400
  };
52170
54401
  }
@@ -52623,6 +54854,9 @@ ${svg}
52623
54854
  };
52624
54855
 
52625
54856
  function getProjectionTopology(P) {
54857
+ if (P && P.__projection_topology) {
54858
+ return P.__projection_topology;
54859
+ }
52626
54860
  var defn = P && projectionTopologies[getCrsSlug(P)];
52627
54861
  if (!defn) return null;
52628
54862
  var lon0 = P.lam0 * 180 / Math.PI;
@@ -52646,6 +54880,11 @@ ${svg}
52646
54880
  }
52647
54881
 
52648
54882
  function isInterruptedProjection(P) {
54883
+ if (P && P.__projection_topology) {
54884
+ return P.__projection_topology.seams.some(function(o) {
54885
+ return o.type == 'cut';
54886
+ });
54887
+ }
52649
54888
  var defn = P && projectionTopologies[getCrsSlug(P)];
52650
54889
  return !!defn && defn.seams.some(function(o) {
52651
54890
  return o.type == 'cut';
@@ -52719,6 +54958,9 @@ ${svg}
52719
54958
 
52720
54959
  // Return projected polygon extent of both clipped and unclipped projections
52721
54960
  function getPolygonDataset$1(src, dest, opts) {
54961
+ if (dest.__projected_outline) {
54962
+ return getCustomProjectedOutline(dest, 'polygon');
54963
+ }
52722
54964
  // use clipping area if projection is clipped
52723
54965
  var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
52724
54966
  if (!dataset) {
@@ -52734,6 +54976,9 @@ ${svg}
52734
54976
 
52735
54977
  // Return projected outline of clipped projections
52736
54978
  function getOutlineDataset(src, dest, opts) {
54979
+ if (dest.__projected_outline) {
54980
+ return getCustomProjectedOutline(dest, 'polyline');
54981
+ }
52737
54982
  var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
52738
54983
  if (!dataset && isInterruptedProjection(dest)) {
52739
54984
  dataset = getBoundingRectangle(dest, {clip_bbox: [-180, -90, 180, 90]});
@@ -52745,10 +54990,33 @@ ${svg}
52745
54990
  removeTinyFootprintRings(dataset);
52746
54991
  }
52747
54992
  dataset.layers[0].geometry_type = 'polyline';
54993
+ if (dest.__remove_outline_extreme_connectors) {
54994
+ dataset = removeOutlineExtremeConnectors(dataset);
54995
+ }
52748
54996
  }
52749
54997
  return dataset || null;
52750
54998
  }
52751
54999
 
55000
+ function getCustomProjectedOutline(P, geometryType) {
55001
+ var rings = P.__projected_outline.map(function(ring) {
55002
+ return ring.map(function(p) {
55003
+ return [
55004
+ P.fr_meter * (P.a * p[0] + P.x0),
55005
+ P.fr_meter * (P.a * p[1] + P.y0)
55006
+ ];
55007
+ });
55008
+ });
55009
+ var dataset = importGeoJSON({
55010
+ type: rings.length == 1 ? 'Polygon' : 'MultiPolygon',
55011
+ coordinates: rings.length == 1 ? rings : rings.map(function(ring) {
55012
+ return [ring];
55013
+ })
55014
+ });
55015
+ dataset.layers[0].geometry_type = geometryType;
55016
+ dataset.info.crs = P;
55017
+ return dataset;
55018
+ }
55019
+
52752
55020
  // Narrow pre-projection gutters can leave zero-area rings after a rotated
52753
55021
  // world boundary is cleaned. Remove only rings that are negligible relative
52754
55022
  // to the main footprint; legitimate disconnected lobes are retained.
@@ -52770,6 +55038,54 @@ ${svg}
52770
55038
  dissolveArcs(dataset);
52771
55039
  }
52772
55040
 
55041
+ // Polygon clipping closes open interrupted-projection boundaries to form valid
55042
+ // rings. In Cahill-Keyes, some closures connect unrelated polar facet images.
55043
+ // Remove only long, nearly horizontal segments at the map's vertical extent.
55044
+ function removeOutlineExtremeConnectors(dataset) {
55045
+ var bounds = getDatasetBounds(dataset);
55046
+ var width = bounds.width();
55047
+ var height = bounds.height();
55048
+ var edgeTolerance = height * 1e-3;
55049
+ var flatTolerance = height * 1e-4;
55050
+ var arcs = dataset.arcs;
55051
+ var lyr = dataset.layers[0];
55052
+ var paths = [];
55053
+ var removed = 0;
55054
+ lyr.shapes.forEach(function(shp) {
55055
+ (shp || []).forEach(function(path) {
55056
+ var points = [];
55057
+ var iter = arcs.getShapeIter(path);
55058
+ while (iter.hasNext()) {
55059
+ points.push([iter.x, iter.y]);
55060
+ }
55061
+ var part = [points[0]];
55062
+ for (var i = 1; i < points.length; i++) {
55063
+ var a = points[i - 1];
55064
+ var b = points[i];
55065
+ var atTop = Math.min(a[1], b[1]) >= bounds.ymax - edgeTolerance;
55066
+ var atBottom = Math.max(a[1], b[1]) <= bounds.ymin + edgeTolerance;
55067
+ var isFlat = Math.abs(a[1] - b[1]) <= flatTolerance;
55068
+ var isWide = Math.abs(a[0] - b[0]) >= width * 0.02;
55069
+ if ((atTop || atBottom) && isFlat && isWide) {
55070
+ if (part.length > 1) paths.push(part);
55071
+ part = [b];
55072
+ removed++;
55073
+ } else {
55074
+ part.push(b);
55075
+ }
55076
+ }
55077
+ if (part.length > 1) paths.push(part);
55078
+ });
55079
+ });
55080
+ if (removed === 0) return dataset;
55081
+ var cleaned = importGeoJSON({
55082
+ type: paths.length == 1 ? 'LineString' : 'MultiLineString',
55083
+ coordinates: paths.length == 1 ? paths[0] : paths
55084
+ });
55085
+ cleaned.info = dataset.info;
55086
+ return cleaned;
55087
+ }
55088
+
52773
55089
  function getBoundingRectangle(dest, opts) {
52774
55090
  var bbox = opts.clip_bbox || getDefaultClipBBox(dest);
52775
55091
  var rotation = getRotationParams(dest);
@@ -52949,19 +55265,79 @@ ${svg}
52949
55265
  var pathLayers = dataset.layers.filter(layerHasPaths);
52950
55266
  var cutPaths, geojson, clip;
52951
55267
  if (!topology || pathLayers.length === 0) return false;
55268
+ insertProjectionTopologyVertices(dataset, topology);
52952
55269
  cutPaths = topology.seams
52953
55270
  .filter(function(o) { return o.type == 'cut'; })
52954
55271
  .reduce(function(memo, o) {
55272
+ if (o.paths) return memo.concat(o.paths);
52955
55273
  return memo.concat(getCutSeamPaths(o.coordinates));
52956
55274
  }, []);
52957
55275
  if (cutPaths.length === 0) return false;
52958
55276
  geojson = getCutMaskGeoJSON(cutPaths);
52959
55277
  if (geojson.features.length === 0) return false;
52960
55278
  clip = importGeoJSON(geojson);
55279
+ if (topology.findRegion) {
55280
+ // Polyhedral cut masks meet and overlap at facet vertices. Union them before
55281
+ // clipping so overlapping source polygons are not interpreted as holes.
55282
+ addIntersectionCuts(clip, {});
55283
+ clip.layers[0] = dissolvePolygonLayer2(clip.layers[0], clip, {quiet: true});
55284
+ }
52961
55285
  clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
52962
55286
  return true;
52963
55287
  }
52964
55288
 
55289
+ // Add a vertex wherever a path changes projection regions. This preserves a
55290
+ // bend at attached polyhedral edges without opening a gap; cut edges are
55291
+ // subsequently disconnected by the narrow seam masks below.
55292
+ function insertProjectionTopologyVertices(dataset, topology) {
55293
+ if (!dataset.arcs || !topology.findRegion) return;
55294
+ var findRegion = topology.findTransitionRegion || topology.findRegion;
55295
+ editArcs(dataset.arcs, function(append, x, y, prevX, prevY, i) {
55296
+ if (i > 0) {
55297
+ findRegionTransitions(prevX, prevY, x, y, findRegion).forEach(append);
55298
+ }
55299
+ append([x, y]);
55300
+ });
55301
+ }
55302
+
55303
+ function findRegionTransitions(ax, ay, bx, by, findRegion) {
55304
+ var interval = 0.5;
55305
+ var n = Math.max(1, Math.ceil(Math.max(Math.abs(bx - ax), Math.abs(by - ay)) / interval));
55306
+ var points = [];
55307
+ var t0 = 0;
55308
+ var region0 = findRegion(ax, ay);
55309
+ for (var i = 1; i <= n; i++) {
55310
+ var t1 = i / n;
55311
+ var x1 = ax + (bx - ax) * t1;
55312
+ var y1 = ay + (by - ay) * t1;
55313
+ var region1 = findRegion(x1, y1);
55314
+ if (region0 != region1 && region0 >= 0 && region1 >= 0) {
55315
+ var t = findRegionBoundary(ax, ay, bx, by, t0, region0, t1, findRegion);
55316
+ var p = [ax + (bx - ax) * t, ay + (by - ay) * t];
55317
+ if (!points.length || Math.abs(t - points[points.length - 1].t) > 1e-10) {
55318
+ p.t = t;
55319
+ points.push(p);
55320
+ }
55321
+ }
55322
+ t0 = t1;
55323
+ region0 = region1;
55324
+ }
55325
+ return points;
55326
+ }
55327
+
55328
+ function findRegionBoundary(ax, ay, bx, by, ta, regionA, tb, findRegion) {
55329
+ for (var i = 0; i < 45; i++) {
55330
+ var tm = (ta + tb) / 2;
55331
+ var regionM = findRegion(ax + (bx - ax) * tm, ay + (by - ay) * tm);
55332
+ if (regionM == regionA) {
55333
+ ta = tm;
55334
+ } else {
55335
+ tb = tm;
55336
+ }
55337
+ }
55338
+ return (ta + tb) / 2;
55339
+ }
55340
+
52965
55341
  function clampDataset(dataset, bbox) {
52966
55342
  transformPoints(dataset, function(x, y) {
52967
55343
  return [utils.clamp(x, bbox[0], bbox[2]), utils.clamp(y, bbox[1], bbox[3])];
@@ -53016,8 +55392,8 @@ ${svg}
53016
55392
  // their projected edges follow the projection instead of becoming straight
53017
55393
  // chords between seam endpoints.
53018
55394
  function getCutMaskGeoJSON(paths) {
53019
- var e = 1e-8;
53020
55395
  var features = paths.map(function(coords) {
55396
+ var e = coords.mask_width || 1e-8;
53021
55397
  var ring = getCutMaskRing(densifyPathByInterval(coords, 0.5), e);
53022
55398
  return {
53023
55399
  type: 'Feature',
@@ -53030,6 +55406,33 @@ ${svg}
53030
55406
  }).filter(function(feature) {
53031
55407
  return feature.geometry.coordinates[0].length > 3;
53032
55408
  });
55409
+ // Give finite seams a small end cap. Without this, a path passing exactly
55410
+ // through a polyhedral vertex can touch only the boundary of several masks
55411
+ // and remain connected across an interruption.
55412
+ paths.forEach(function(coords) {
55413
+ var endpoints = [coords[0], coords[coords.length - 1]];
55414
+ endpoints.forEach(function(p) {
55415
+ var e2 = Math.max(1e-7, (coords.mask_width || 0) * 2);
55416
+ var xmin = Math.max(-180, p[0] - e2);
55417
+ var xmax = Math.min(180, p[0] + e2);
55418
+ var ymin = Math.max(-90, p[1] - e2);
55419
+ var ymax = Math.min(90, p[1] + e2);
55420
+ features.push({
55421
+ type: 'Feature',
55422
+ properties: {},
55423
+ geometry: {
55424
+ type: 'Polygon',
55425
+ coordinates: [[
55426
+ [xmin, ymin],
55427
+ [xmax, ymin],
55428
+ [xmax, ymax],
55429
+ [xmin, ymax],
55430
+ [xmin, ymin]
55431
+ ]]
55432
+ }
55433
+ });
55434
+ });
55435
+ });
53033
55436
  return {
53034
55437
  type: 'FeatureCollection',
53035
55438
  features: features
@@ -53048,8 +55451,8 @@ ${svg}
53048
55451
  if (len === 0) continue;
53049
55452
  var nx = -dy / len * width;
53050
55453
  var ny = dx / len * width;
53051
- left.push([coords[i][0] + nx, coords[i][1] + ny]);
53052
- right.push([coords[i][0] - nx, coords[i][1] - ny]);
55454
+ left.push([utils.clamp(coords[i][0] + nx, -180, 180), coords[i][1] + ny]);
55455
+ right.push([utils.clamp(coords[i][0] - nx, -180, 180), coords[i][1] - ny]);
53053
55456
  }
53054
55457
  var ring = left.concat(right.reverse());
53055
55458
  if (ring.length > 0) ring.push(ring[0].concat());
@@ -57459,7 +59862,7 @@ ${svg}
57459
59862
  function getSplitLineFunction(crs, dashLen, gapLen, opts) {
57460
59863
  var planar = !!opts.planar;
57461
59864
  var interpolate = getInterpolationFunction(planar ? null : crs);
57462
- var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
59865
+ var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D$1;
57463
59866
  var inDash, parts2, interval, scale;
57464
59867
  function addPart(coords) {
57465
59868
  if (inDash) parts2.push(coords);
@@ -58330,7 +60733,7 @@ ${svg}
58330
60733
  // Isometric ("Mercator-stretched") latitude in degrees-in; returns +/-Infinity at
58331
60734
  // the poles.
58332
60735
  function isometricLatitude(latDeg) {
58333
- return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$1 / 2));
60736
+ return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$7 / 2));
58334
60737
  }
58335
60738
 
58336
60739
  // Rhumb-line (loxodrome) interpolation, fraction k of the way from A to B.
@@ -58355,9 +60758,9 @@ ${svg}
58355
60758
  // Rhumb-line distance in meters, using the raw longitude delta (so a full-parallel
58356
60759
  // sweep returns the parallel's length rather than 0).
58357
60760
  function rhumbDistance(a, b) {
58358
- var phi1 = a[1] * D2R$1, phi2 = b[1] * D2R$1;
60761
+ var phi1 = a[1] * D2R$7, phi2 = b[1] * D2R$7;
58359
60762
  var dPhi = phi2 - phi1;
58360
- var dLambda = (b[0] - a[0]) * D2R$1; // raw
60763
+ var dLambda = (b[0] - a[0]) * D2R$7; // raw
58361
60764
  var dPsi = Math.log(Math.tan(Math.PI / 4 + phi2 / 2) / Math.tan(Math.PI / 4 + phi1 / 2));
58362
60765
  var q = Math.abs(dPsi) > 1e-12 ? dPhi / dPsi : Math.cos(phi1);
58363
60766
  return Math.sqrt(dPhi * dPhi + q * q * dLambda * dLambda) * R$3;
@@ -62022,9 +64425,11 @@ ${svg}
62022
64425
  // project graticule to match dataset
62023
64426
  destInfo = getDatasetCrsInfo(dataset);
62024
64427
  if (!destInfo.crs) stop$1("Coordinate system is unknown, unable to create a graticule");
62025
- graticule = boundary ?
62026
- createProjectedPolygon(destInfo.crs, opts) :
62027
- createProjectedGraticule(destInfo.crs, opts);
64428
+ graticule = opts.outline ?
64429
+ createProjectedOutline(destInfo.crs, opts) :
64430
+ opts.polygon ?
64431
+ createProjectedPolygon(destInfo.crs, opts) :
64432
+ createProjectedGraticule(destInfo.crs, opts);
62028
64433
  setDatasetCrsInfo(graticule, destInfo);
62029
64434
  } else {
62030
64435
  graticule = boundary ?
@@ -62049,6 +64454,12 @@ ${svg}
62049
64454
  return getPolygonDataset$1(src, dest, opts);
62050
64455
  }
62051
64456
 
64457
+ function createProjectedOutline(dest, opts) {
64458
+ var src = parseCrsString$1('wgs84');
64459
+ return getOutlineDataset(src, dest, opts) ||
64460
+ getPolygonDataset$1(src, dest, opts);
64461
+ }
64462
+
62052
64463
  function createUnprojectedGraticule(opts) {
62053
64464
  var src = parseCrsString$1('wgs84');
62054
64465
  var graticule = importGeoJSON(createGraticule(src, false, opts));
@@ -62130,12 +64541,15 @@ ${svg}
62130
64541
  // extended: meridian extends to pole
62131
64542
  function createMeridian(x, extended) {
62132
64543
  var y0 = ystep <= 15 ? ystep : 0;
64544
+ // An exact pole can have several images in a polyhedral projection.
64545
+ // The separate outline supplies the missing, negligible end interval.
64546
+ var poleInset = outlined ? 1e-4 : 0;
62133
64547
  createMeridianPart(x, -90 + y0, 90 - y0);
62134
64548
  if (extended && y0 > 0) {
62135
64549
  // adding extensions as separate parts, so if the polar coordinates
62136
64550
  // fail to project, at least the rest of the meridian line will remain
62137
- createMeridianPart(x, -90, -90 + y0);
62138
- createMeridianPart(x, 90 - y0, 90);
64551
+ createMeridianPart(x, -90 + poleInset, -90 + y0);
64552
+ createMeridianPart(x, 90 - y0, 90 - poleInset);
62139
64553
  }
62140
64554
  }
62141
64555
 
@@ -62145,7 +64559,14 @@ ${svg}
62145
64559
  }
62146
64560
 
62147
64561
  function createParallel(y) {
62148
- var coords = densifyPathByInterval([[-180, y], [180, y]], precision);
64562
+ // Avoid joining distinct images of the antimeridian (and the degenerate
64563
+ // polar parallels) when a projected outline is generated separately.
64564
+ var edgeInset = outlined ? 1e-4 : 0;
64565
+ if (outlined && Math.abs(y) == 90) return;
64566
+ var coords = densifyPathByInterval([
64567
+ [-180 + edgeInset, y],
64568
+ [180 - edgeInset, y]
64569
+ ], precision);
62149
64570
  parallels.push(graticuleFeature(coords, {type: 'parallel', value: y}));
62150
64571
  }
62151
64572
  }
@@ -62744,7 +65165,7 @@ ${svg}
62744
65165
  if (halfLen > 0 === false) {
62745
65166
  return [a, b];
62746
65167
  }
62747
- var segLen = distance2D(a, b, c, d);
65168
+ var segLen = distance2D$1(a, b, c, d);
62748
65169
  var k;
62749
65170
  if (partialLen + segLen >= halfLen) {
62750
65171
  k = (halfLen - partialLen) / segLen;
@@ -64665,7 +67086,7 @@ ${svg}
64665
67086
 
64666
67087
  // Source: https://diego.assencio.com/?index=8d6ca3d82151bad815f78addf9b5c1c6
64667
67088
  function twoCircleIntersection(c1, r1, c2, r2) {
64668
- var d = distance2D(c1[0], c1[1], c2[0], c2[1]);
67089
+ var d = distance2D$1(c1[0], c1[1], c2[0], c2[1]);
64669
67090
  if (d >= r1 + r2) return 0;
64670
67091
  var r1sq = r1 * r1,
64671
67092
  r2sq = r2 * r2,
@@ -65498,6 +67919,9 @@ ${svg}
65498
67919
  var commands;
65499
67920
  try {
65500
67921
  commands = parseCommands(commandStr);
67922
+ if (opts && opts.validate_commands) {
67923
+ opts.validate_commands(commands);
67924
+ }
65501
67925
  } catch(e) {
65502
67926
  e.message = 'Error in command file ' + file + ': ' + e.message;
65503
67927
  throw e;
@@ -65519,6 +67943,9 @@ ${svg}
65519
67943
  }
65520
67944
  if (c.name == 'run') {
65521
67945
  c.options._run_depth = depth;
67946
+ if (opts && opts.validate_commands) {
67947
+ c.options.validate_commands = opts.validate_commands;
67948
+ }
65522
67949
  }
65523
67950
  });
65524
67951
 
@@ -65556,6 +67983,9 @@ ${svg}
65556
67983
  ctx = getBaseContext();
65557
67984
  // io proxy adds ability to add datasets dynamically in a required function
65558
67985
  ctx.io = getIOProxy();
67986
+ if (opts.input) {
67987
+ Object.assign(ctx.io._cache, opts.input);
67988
+ }
65559
67989
  tmp = await evalTemplateExpression(opts.expression, targets, ctx);
65560
67990
  if (tmp && !utils.isString(tmp)) {
65561
67991
  stop$1('Expected a string containing mapshaper commands; received:', tmp);
@@ -65564,6 +67994,9 @@ ${svg}
65564
67994
  // truncate message (command might include a large GeoJSON string in an -i command)
65565
67995
  message(`command: [${truncateString(tmp, 150)}]`);
65566
67996
  commands = parseCommands(tmp);
67997
+ if (opts.validate_commands) {
67998
+ opts.validate_commands(commands);
67999
+ }
65567
68000
 
65568
68001
  // TODO: remove duplication with mapshaper-run-commands.mjs
65569
68002
  var outputArr = opts && opts.output || null;
@@ -65578,6 +68011,9 @@ ${svg}
65578
68011
  cmd.name == 'info' && cmd.options.save_to)) {
65579
68012
  cmd.options.output = outputArr;
65580
68013
  }
68014
+ if (cmd.name == 'run' && opts.validate_commands) {
68015
+ cmd.options.validate_commands = opts.validate_commands;
68016
+ }
65581
68017
  });
65582
68018
 
65583
68019
  await utils.promisify(runParsedCommands)(commands, job);
@@ -66660,7 +69096,7 @@ ${svg}
66660
69096
  if (totalSegs < 1) return 0;
66661
69097
  var MAX_SAMPLES = 100000;
66662
69098
  var stride = Math.ceil(totalSegs / MAX_SAMPLES);
66663
- var distFn = spherical ? greatCircleDistance : distance2D;
69099
+ var distFn = spherical ? greatCircleDistance : distance2D$1;
66664
69100
  var lens = [];
66665
69101
  var counter = 0;
66666
69102
  arcs.forEach3(function(xx, yy) {
@@ -68114,7 +70550,7 @@ ${svg}
68114
70550
  if (Object.prototype.hasOwnProperty.call(command, '_sourceOverride')) {
68115
70551
  source = command._sourceOverride;
68116
70552
  } else if (opts.source) {
68117
- source = findCommandSource(convertSourceName(opts.source, targets), job.catalog, opts);
70553
+ source = await findCommandSourceAsync(convertSourceName(opts.source, targets), job.catalog, opts);
68118
70554
  }
68119
70555
 
68120
70556
  // identify command target/input (for postprocessing)
@@ -68520,7 +70956,7 @@ ${svg}
68520
70956
  var disposableSourceTargetIndex = -1;
68521
70957
 
68522
70958
  if (command.options.source && !sourceNameIsInterpolated(command.options.source)) {
68523
- source = resolveSourceForTargets(command, job, targets);
70959
+ source = await resolveSourceForTargets(command, job, targets);
68524
70960
  items = moveSourceTargetLast(items, source);
68525
70961
  disposableSourceTargetIndex = findLastDisposableSourceTarget(
68526
70962
  items,
@@ -68555,10 +70991,10 @@ ${svg}
68555
70991
  return job;
68556
70992
  }
68557
70993
 
68558
- function resolveSourceForTargets(command, job, targets) {
70994
+ async function resolveSourceForTargets(command, job, targets) {
68559
70995
  job.startCommand(command);
68560
70996
  try {
68561
- return findCommandSource(
70997
+ return await findCommandSourceAsync(
68562
70998
  convertSourceName(command.options.source, targets),
68563
70999
  job.catalog,
68564
71000
  command.options
@@ -68660,7 +71096,7 @@ ${svg}
68660
71096
  return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
68661
71097
  }
68662
71098
 
68663
- var version = "0.7.44";
71099
+ var version = "0.7.46";
68664
71100
 
68665
71101
  // Parse command line args into commands and run them
68666
71102
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -68690,7 +71126,6 @@ ${svg}
68690
71126
  if (err) {
68691
71127
  return callback(err);
68692
71128
  }
68693
- if (opts.legacy) return callback(null, toLegacyOutputFormat(outputArr));
68694
71129
  return callback(null, toOutputFormat(outputArr));
68695
71130
  });
68696
71131
  if (opts.promise) return opts.promise;
@@ -68732,9 +71167,6 @@ ${svg}
68732
71167
  opts.callback = arg1;
68733
71168
  } else if (utils.isFunction(arg2)) {
68734
71169
  opts.callback = arg2;
68735
- // identify legacy input format (used by some tests)
68736
- opts.legacy = arg1 && guessInputContentType(arg1) != null;
68737
- opts.input = arg1;
68738
71170
  } else {
68739
71171
  // if no callback, create a promise and a callback for resolving the promise
68740
71172
  opts.promise = new Promise(function(resolve, reject) {
@@ -68744,7 +71176,7 @@ ${svg}
68744
71176
  };
68745
71177
  });
68746
71178
  }
68747
- if (!opts.legacy && utils.isObject(arg1)) {
71179
+ if (utils.isObject(arg1)) {
68748
71180
  if (arg1.xl) {
68749
71181
  // options for runCommandsXL()
68750
71182
  opts.options = arg1;
@@ -68777,12 +71209,6 @@ ${svg}
68777
71209
  return callback(e);
68778
71210
  }
68779
71211
 
68780
- if (opts.legacy) {
68781
- message("Warning: deprecated input format");
68782
- commands = convertLegacyCommands(commands, inputObj);
68783
- inputObj = null;
68784
- }
68785
-
68786
71212
  if (commands.length === 0) {
68787
71213
  return callback(new UserError("No commands to run"));
68788
71214
  }
@@ -68819,13 +71245,22 @@ ${svg}
68819
71245
  // (e.g. `mapshaper *.shp batch-mode -o out.json` writing all inputs to the
68820
71246
  // same target on disk).
68821
71247
  var sharedOutputs = [];
68822
- utils.reduceAsync(batches, null, nextGroup, done);
71248
+ runBatches().then(function(job) {
71249
+ done(null, job);
71250
+ }, done);
68823
71251
 
68824
- function nextGroup(prevJob, commands, next) {
68825
- runParsedCommands(commands, new Job(null, {output_files: sharedOutputs}), function(err, job) {
68826
- err = handleNonFatalError(err);
68827
- next(err, job);
68828
- });
71252
+ async function runBatches() {
71253
+ var job;
71254
+ for (var batch of batches) {
71255
+ try {
71256
+ job = await runParsedCommands(batch, new Job(null, {output_files: sharedOutputs}));
71257
+ } catch (e) {
71258
+ var fatalError = handleNonFatalError(e);
71259
+ if (fatalError) throw fatalError;
71260
+ job = undefined;
71261
+ }
71262
+ }
71263
+ return job;
68829
71264
  }
68830
71265
 
68831
71266
  function done(err, job) {
@@ -68839,33 +71274,6 @@ ${svg}
68839
71274
  }
68840
71275
 
68841
71276
 
68842
- function toLegacyOutputFormat(arr) {
68843
- if (arr.length > 1) {
68844
- // Return an array if multiple files are output
68845
- return utils.pluck(arr, 'content');
68846
- }
68847
- if (arr.length == 1) {
68848
- // Return content if a single file is output
68849
- return arr[0].content;
68850
- }
68851
- return null;
68852
- }
68853
-
68854
- function convertLegacyCommands(arr, inputObj) {
68855
- var i = utils.find(arr, function(cmd) {return cmd.name == 'i';});
68856
- var o = utils.find(arr, function(cmd) {return cmd.name == 'o';});
68857
- if (!i) {
68858
- i = {name: 'i', options: {}};
68859
- arr.unshift(i);
68860
- }
68861
- i.options.files = ['__input__'];
68862
- i.options.input = {__input__: inputObj};
68863
- if (!o) {
68864
- arr.push({name: 'o', options: {}});
68865
- }
68866
- return arr;
68867
- }
68868
-
68869
71277
  // TODO: rewrite tests and remove this function
68870
71278
  function testCommands(argv, done) {
68871
71279
  _runCommands(argv, {}, function(err, job) {
@@ -68883,9 +71291,20 @@ ${svg}
68883
71291
  // Execute a sequence of parsed commands
68884
71292
  // @commands Array of parsed commands
68885
71293
  // @job: Job object containing previously imported data
68886
- // @done: function([error], [job])
71294
+ // @done: optional function([error], [job]); returns a Promise if omitted
68887
71295
  //
68888
71296
  function runParsedCommands(commands, job, done) {
71297
+ var promise = runParsedCommandsAsync(commands, job);
71298
+ if (done) {
71299
+ promise.then(function(job) {
71300
+ done(null, job);
71301
+ }, done);
71302
+ return;
71303
+ }
71304
+ return promise;
71305
+ }
71306
+
71307
+ async function runParsedCommandsAsync(commands, job) {
68889
71308
  if (!job) job = new Job();
68890
71309
  commands = readAndRemoveSettings(job, commands);
68891
71310
  if (!runningInBrowser()) {
@@ -68893,26 +71312,16 @@ ${svg}
68893
71312
  }
68894
71313
  commands = runAndRemoveInfoCommands(commands);
68895
71314
  if (commands.length === 0) {
68896
- return done(null);
71315
+ return;
68897
71316
  }
68898
71317
  // we're no longer using the same Job for all batches -- no reset needed
68899
71318
  // // resetting closes any unterminated -if blocks from a previous command sequence
68900
71319
  // resetControlFlow(job);
68901
- utils.reduceAsync(commands, job, nextCommand, done);
68902
-
68903
- function nextCommand(job, cmd, next) {
68904
- var resolved;
68905
- try {
68906
- resolved = maybeInterpolateCommand(cmd, job);
68907
- } catch(e) {
68908
- return next(e);
68909
- }
68910
- runCommand(resolved, job).then(function(result) {
68911
- next(null, result);
68912
- }).catch(function(e) {
68913
- next(e);
68914
- });
71320
+ for (var command of commands) {
71321
+ var resolved = maybeInterpolateCommand(command, job);
71322
+ job = await runCommand(resolved, job);
68915
71323
  }
71324
+ return job;
68916
71325
  }
68917
71326
 
68918
71327
  // Late-binding interpolation: just before each command runs, replace any
@@ -70524,6 +72933,9 @@ ${svg}
70524
72933
  ClipPoints,
70525
72934
  Colorizer,
70526
72935
  CustomProjections,
72936
+ Dymaxion,
72937
+ ButterflyProjections,
72938
+ Narukawa2022,
70527
72939
  DataAggregation,
70528
72940
  DatasetUtils,
70529
72941
  DataUtils,