@math.gl/geospatial 5.0.0-beta.1 → 5.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/crs/crs-identifier.d.ts +23 -0
  2. package/dist/crs/crs-identifier.d.ts.map +1 -0
  3. package/dist/crs/crs-identifier.js +74 -0
  4. package/dist/crs/crs-identifier.js.map +1 -0
  5. package/dist/crs/index.cjs +48 -0
  6. package/dist/crs/index.cjs.map +2 -2
  7. package/dist/crs/index.d.ts +1 -0
  8. package/dist/crs/index.d.ts.map +1 -1
  9. package/dist/crs/index.js +1 -0
  10. package/dist/crs/index.js.map +1 -1
  11. package/dist/discontinuous/cap-map.d.ts.map +1 -1
  12. package/dist/discontinuous/cap-map.js +197 -13
  13. package/dist/discontinuous/cap-map.js.map +1 -1
  14. package/dist/discontinuous/elastic/geometry.d.ts +1 -1
  15. package/dist/discontinuous/elastic/geometry.d.ts.map +1 -1
  16. package/dist/discontinuous/elastic/geometry.js +7 -7
  17. package/dist/discontinuous/elastic/geometry.js.map +1 -1
  18. package/dist/discontinuous/elastic/map.d.ts.map +1 -1
  19. package/dist/discontinuous/elastic/map.js +263 -23
  20. package/dist/discontinuous/elastic/map.js.map +1 -1
  21. package/dist/discontinuous/elastic/source-arc.d.ts +10 -0
  22. package/dist/discontinuous/elastic/source-arc.d.ts.map +1 -0
  23. package/dist/discontinuous/elastic/source-arc.js +111 -0
  24. package/dist/discontinuous/elastic/source-arc.js.map +1 -0
  25. package/dist/discontinuous/global-polygons.d.ts +1 -1
  26. package/dist/discontinuous/global-polygons.d.ts.map +1 -1
  27. package/dist/discontinuous/global-polygons.js +49 -1
  28. package/dist/discontinuous/global-polygons.js.map +1 -1
  29. package/dist/discontinuous/map-engine.d.ts +9 -0
  30. package/dist/discontinuous/map-engine.d.ts.map +1 -1
  31. package/dist/discontinuous/map-layout.d.ts +21 -0
  32. package/dist/discontinuous/map-layout.d.ts.map +1 -1
  33. package/dist/discontinuous/map-layout.js +116 -1
  34. package/dist/discontinuous/map-layout.js.map +1 -1
  35. package/dist/discontinuous/map-transition.d.ts.map +1 -1
  36. package/dist/discontinuous/map-transition.js +66 -3
  37. package/dist/discontinuous/map-transition.js.map +1 -1
  38. package/dist/discontinuous/mercator-map.d.ts +2 -2
  39. package/dist/discontinuous/mercator-map.d.ts.map +1 -1
  40. package/dist/discontinuous/mercator-map.js +123 -11
  41. package/dist/discontinuous/mercator-map.js.map +1 -1
  42. package/dist/discontinuous/polygon-constraints.d.ts +16 -0
  43. package/dist/discontinuous/polygon-constraints.d.ts.map +1 -0
  44. package/dist/discontinuous/polygon-constraints.js +157 -0
  45. package/dist/discontinuous/polygon-constraints.js.map +1 -0
  46. package/dist/discontinuous/prepare-features.d.ts +27 -0
  47. package/dist/discontinuous/prepare-features.d.ts.map +1 -1
  48. package/dist/discontinuous/prepare-features.js +106 -5
  49. package/dist/discontinuous/prepare-features.js.map +1 -1
  50. package/dist/discontinuous/rotated-map.d.ts +9 -0
  51. package/dist/discontinuous/rotated-map.d.ts.map +1 -1
  52. package/dist/discontinuous/source-arc.d.ts +4 -0
  53. package/dist/discontinuous/source-arc.d.ts.map +1 -0
  54. package/dist/discontinuous/source-arc.js +23 -0
  55. package/dist/discontinuous/source-arc.js.map +1 -0
  56. package/dist/discontinuous/world-kernels.d.ts +1 -1
  57. package/dist/discontinuous.cjs +1047 -75
  58. package/dist/discontinuous.cjs.map +3 -3
  59. package/dist/index.cjs +48 -0
  60. package/dist/index.cjs.map +2 -2
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/turf.cjs +69 -5
  66. package/dist/turf.cjs.map +2 -2
  67. package/dist/turf.d.ts +9 -0
  68. package/dist/turf.d.ts.map +1 -1
  69. package/dist/turf.js +67 -0
  70. package/dist/turf.js.map +1 -1
  71. package/dist/workers/index.cjs +1075 -79
  72. package/dist/workers/index.cjs.map +3 -3
  73. package/dist/workers/operations.cjs +1049 -77
  74. package/dist/workers/operations.cjs.map +3 -3
  75. package/package.json +5 -5
  76. package/src/crs/crs-identifier.ts +86 -0
  77. package/src/crs/index.ts +1 -0
  78. package/src/discontinuous/cap-map.ts +248 -19
  79. package/src/discontinuous/elastic/geometry.ts +18 -7
  80. package/src/discontinuous/elastic/map.ts +334 -30
  81. package/src/discontinuous/elastic/source-arc.ts +129 -0
  82. package/src/discontinuous/global-polygons.ts +56 -2
  83. package/src/discontinuous/map-layout.ts +165 -2
  84. package/src/discontinuous/map-transition.ts +67 -3
  85. package/src/discontinuous/mercator-map.ts +151 -12
  86. package/src/discontinuous/polygon-constraints.ts +184 -0
  87. package/src/discontinuous/prepare-features.ts +154 -6
  88. package/src/discontinuous/source-arc.ts +31 -0
  89. package/src/index.ts +1 -0
  90. package/src/turf.ts +86 -0
@@ -286,6 +286,10 @@ var DiscontinuousMap = class {
286
286
  for (let i = 1; i < points.length; i++)
287
287
  validateArc(points[i - 1], points[i]);
288
288
  const results = [];
289
+ const cumulative = options.pathDistances ? [0] : void 0;
290
+ if (cumulative)
291
+ for (let i = 1; i < points.length; i++)
292
+ cumulative.push(cumulative[i - 1] + angle(points[i - 1], points[i]));
289
293
  for (const r of this.compiled) {
290
294
  let current = null;
291
295
  for (let i = 1; i < points.length; i++) {
@@ -294,18 +298,42 @@ var DiscontinuousMap = class {
294
298
  current = null;
295
299
  continue;
296
300
  }
301
+ const reason = (p, endpoint, isEndpoint) => {
302
+ if (isEndpoint && angle(p, endpoint) < 1e-12)
303
+ return "source-endpoint";
304
+ const kinds = r.normals.map((normal, edge2) => {
305
+ var _a;
306
+ return Math.abs(dot(normal, p)) < 1e-9 ? (_a = this.seams.find((s) => s.regions.some((id, i2) => id === r.definition.id && s.edges[i2] === edge2))) == null ? void 0 : _a.kind : void 0;
307
+ }).filter(Boolean);
308
+ return kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : kinds.includes("join") ? "continuous-chart-join" : "coverage-boundary";
309
+ };
297
310
  const sampled = this.sample(r, clipped[0], clipped[1], settings);
298
311
  if (sampled.length < 2)
299
312
  continue;
300
313
  if (!current || angle(geographic(current.sourceCoordinates[current.sourceCoordinates.length - 1]), clipped[0]) > EPS || !this.sameProjectedPoint(current.coordinates[current.coordinates.length - 1], this.project(r, sampled[0]))) {
301
314
  current = { regionId: r.definition.id, coordinates: [], sourceCoordinates: [] };
315
+ if (options.pathProvenance)
316
+ current.provenance = { segments: [], parameters: [], vertices: [], angularDistances: [] };
317
+ if (current.provenance && options.pathBoundaries)
318
+ current.provenance.startReason = reason(clipped[0], points[0], i === 1);
302
319
  results.push(current);
303
320
  } else
304
321
  sampled.shift();
305
322
  for (const p of sampled) {
306
323
  current.coordinates.push(this.project(r, p));
307
324
  current.sourceCoordinates.push(coordinates(p));
325
+ if (current.provenance) {
326
+ const length = angle(points[i - 1], points[i]);
327
+ const t = length ? Math.min(1, Math.max(0, angle(points[i - 1], p) / length)) : 0;
328
+ current.provenance.segments.push(i - 1);
329
+ current.provenance.parameters.push(t);
330
+ current.provenance.vertices.push(angle(points[i - 1], p) < 1e-12 ? i - 1 : angle(points[i], p) < 1e-12 ? i : 4294967295);
331
+ if (cumulative)
332
+ current.provenance.angularDistances.push(cumulative[i - 1] + t * length);
333
+ }
308
334
  }
335
+ if (current.provenance && options.pathBoundaries)
336
+ current.provenance.endReason = reason(clipped[1], points[points.length - 1], i === points.length - 1);
309
337
  if (!this.contains(r, points[i]))
310
338
  current = null;
311
339
  }
@@ -402,26 +430,91 @@ var DiscontinuousMap = class {
402
430
  coordinates: [],
403
431
  sourceCoordinates: []
404
432
  };
433
+ if (options.polygonEdgeProvenance)
434
+ output.edgeProvenance = [];
405
435
  polygon.forEach((ring, index) => {
406
436
  const sphere = ring.map((p) => unit(add(center3, add(scale(x, p[0]), scale(y, p[1])))));
407
437
  const points = [];
438
+ const edges = [];
439
+ const classify = (a, b) => {
440
+ if (angle(a, b) < 64 * Number.EPSILON)
441
+ return {
442
+ kind: "continuous-chart-join",
443
+ ring: 4294967295,
444
+ segment: 4294967295,
445
+ t0: 0,
446
+ t1: 0
447
+ };
448
+ const candidates = [];
449
+ vectors.forEach((source, ring2) => source.forEach((u, segment) => {
450
+ const v = source[(segment + 1) % source.length], length = angle(u, v);
451
+ if (length > 1e-12 && [a, b].every((p) => Math.abs(dot(unit(cross(u, v)), p)) < 1e-12 && Math.abs(angle(u, p) + angle(p, v) - length) < 1e-10))
452
+ candidates.push({ ring: ring2, segment });
453
+ }));
454
+ if (candidates.length > 1)
455
+ throw new Error("Ambiguous polygon boundary provenance");
456
+ if (candidates.length) {
457
+ const { ring: ring2, segment } = candidates[0], source = vectors[ring2], u = source[segment], length = angle(u, source[(segment + 1) % source.length]);
458
+ return {
459
+ kind: "source-boundary",
460
+ ring: ring2,
461
+ segment,
462
+ t0: Math.min(1, angle(u, a) / length),
463
+ t1: Math.min(1, angle(u, b) / length)
464
+ };
465
+ }
466
+ const kinds = r.normals.map((n, edge2) => {
467
+ var _a;
468
+ return [a, b].every((p) => Math.abs(dot(n, p)) < 1e-9) ? (_a = this.seams.find((s) => s.regions.some((id, i) => id === r.definition.id && s.edges[i] === edge2))) == null ? void 0 : _a.kind : void 0;
469
+ }).filter(Boolean);
470
+ if (!kinds.length)
471
+ throw new Error("Unknown polygon clipping constraint");
472
+ return {
473
+ kind: kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : "continuous-chart-join",
474
+ ring: 4294967295,
475
+ segment: 4294967295,
476
+ t0: 0,
477
+ t1: 0
478
+ };
479
+ };
408
480
  for (let i = 1; i < sphere.length; i++) {
481
+ const origin2 = options.polygonEdgeProvenance && !sphere[i - 1].every((value, k2) => value === sphere[i][k2]) ? classify(sphere[i - 1], sphere[i]) : void 0;
409
482
  const sampled = this.sample(r, sphere[i - 1], sphere[i], settings);
410
483
  for (const point of sampled) {
411
484
  const previous = points[points.length - 1];
412
- if (!previous || !point.every((value, k2) => value === previous[k2]))
485
+ if (!previous || !point.every((value, k2) => value === previous[k2])) {
486
+ if (previous && origin2) {
487
+ const edge2 = { ...origin2 };
488
+ if (edge2.kind === "source-boundary") {
489
+ const source = vectors[edge2.ring], u = source[edge2.segment], length = angle(u, source[(edge2.segment + 1) % source.length]);
490
+ edge2.t0 = Math.min(1, angle(u, previous) / length);
491
+ edge2.t1 = Math.min(1, angle(u, point) / length);
492
+ }
493
+ edges.push(edge2);
494
+ }
413
495
  points.push(point);
496
+ }
414
497
  }
415
498
  }
416
499
  if (points.length < 3)
417
500
  return;
418
501
  if (points[points.length - 1].every((value, k2) => value === points[0][k2]))
419
502
  points.pop();
503
+ if (options.polygonEdgeProvenance && edges.length < points.length)
504
+ edges.push(classify(points[points.length - 1], points[0]));
420
505
  points.push(points[0]);
421
506
  const xy = points.map((p) => this.project(r, p));
422
507
  if (ringOrientation(xy) > 0 !== (index === 0)) {
423
508
  xy.reverse();
424
509
  points.reverse();
510
+ edges.reverse();
511
+ for (const e of edges)
512
+ [e.t0, e.t1] = [e.t1, e.t0];
513
+ }
514
+ if (output.edgeProvenance) {
515
+ if (edges.length !== points.length - 1)
516
+ throw new Error("Mismatched polygon edge provenance");
517
+ output.edgeProvenance.push(edges);
425
518
  }
426
519
  output.coordinates.push(xy);
427
520
  output.sourceCoordinates.push(points.map(coordinates));
@@ -2873,8 +2966,49 @@ function cutSphericalPolygon(rings, options) {
2873
2966
  }
2874
2967
  function projectSphericalPolygon(map, rings, options) {
2875
2968
  const result = [];
2969
+ if (options.polygonEdgeProvenance && (!map.regions || !map.seams))
2970
+ throw new Error("Atlas provenance requires map region and seam metadata");
2876
2971
  for (const part of cutSphericalPolygon(rings, options))
2877
- result.push(...map.projectPolygon(part.rings, options));
2972
+ for (const polygon of map.projectPolygon(part.rings, options)) {
2973
+ if (options.polygonEdgeProvenance) {
2974
+ if (!polygon.edgeProvenance)
2975
+ throw new Error("Map backend does not support polygon edge provenance");
2976
+ const region = map.regions.find((r) => r.id === polygon.regionId);
2977
+ polygon.edgeProvenance = polygon.edgeProvenance.map((edges, ring) => edges.map((edge2, i) => {
2978
+ if (edge2.kind !== "source-boundary")
2979
+ return edge2;
2980
+ const parent = part.edges[edge2.ring][edge2.segment];
2981
+ if (parent.kind === "source")
2982
+ return {
2983
+ ...edge2,
2984
+ ring: parent.ringIndex,
2985
+ segment: parent.edgeIndex,
2986
+ t0: parent.startFraction + edge2.t0 * (parent.endFraction - parent.startFraction),
2987
+ t1: parent.startFraction + edge2.t1 * (parent.endFraction - parent.startFraction)
2988
+ };
2989
+ const a = geographic(polygon.sourceCoordinates[ring][i]), b = geographic(polygon.sourceCoordinates[ring][i + 1]), kinds = [];
2990
+ region.boundary.forEach((p, j) => {
2991
+ const normal = cross(geographic(p), geographic(region.boundary[(j + 1) % region.boundary.length]));
2992
+ const length = Math.hypot(...normal);
2993
+ if (length < 1e-12)
2994
+ return;
2995
+ if ([a, b].every((v) => Math.abs(dot(normal, v)) / length < 1e-10)) {
2996
+ const seam = map.seams.find((s) => s.regions.some((id, k2) => id === region.id && s.edges[k2] === j));
2997
+ if (seam)
2998
+ kinds.push(seam.kind);
2999
+ }
3000
+ });
3001
+ return {
3002
+ kind: kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : "continuous-chart-join",
3003
+ ring: 4294967295,
3004
+ segment: 4294967295,
3005
+ t0: 0,
3006
+ t1: 0
3007
+ };
3008
+ }));
3009
+ }
3010
+ result.push(polygon);
3011
+ }
2878
3012
  return result;
2879
3013
  }
2880
3014
 
@@ -2911,33 +3045,13 @@ var lerp = (a, b, t) => [
2911
3045
  a[0] + (b[0] - a[0]) * t,
2912
3046
  a[1] + (b[1] - a[1]) * t
2913
3047
  ];
2914
- function clipSegment(a, b, polygons) {
2915
- const ts = [0, 1], dx = b[0] - a[0], dy = b[1] - a[1];
2916
- for (const rings of polygons)
2917
- for (const ring of rings)
2918
- for (let i = 1; i < ring.length; i++) {
2919
- const c = ring[i - 1], d = ring[i], ex = d[0] - c[0], ey = d[1] - c[1], det = dx * ey - dy * ex;
2920
- if (Math.abs(det) < 1e-14)
2921
- continue;
2922
- const t = ((c[0] - a[0]) * ey - (c[1] - a[1]) * ex) / det;
2923
- const u = ((c[0] - a[0]) * dy - (c[1] - a[1]) * dx) / det;
2924
- if (t > 0 && t < 1 && u >= -1e-10 && u <= 1 + 1e-10)
2925
- ts.push(t);
2926
- }
2927
- ts.sort((a2, b2) => a2 - b2);
2928
- const result = [];
2929
- for (let i = 1; i < ts.length; i++)
2930
- if (ts[i] - ts[i - 1] > 1e-12 && contains(polygons, lerp(a, b, (ts[i] + ts[i - 1]) / 2)))
2931
- result.push([lerp(a, b, ts[i - 1]), lerp(a, b, ts[i])]);
2932
- return result;
2933
- }
2934
3048
  function sampling2(options) {
2935
3049
  const step = options.maxAngularStep ?? 2, tolerance = options.tolerance ?? 1e-4;
2936
3050
  if (!Number.isFinite(step) || step <= 0 || step > 90 || !Number.isFinite(tolerance) || tolerance <= 0)
2937
3051
  throw new Error("Invalid map sampling options");
2938
3052
  return { step, tolerance };
2939
3053
  }
2940
- function sample(line, project, options, cellStep) {
3054
+ function sample(line, project, options, cellStep, interpolate) {
2941
3055
  const { step, tolerance } = sampling2(options), source = [], xy = [];
2942
3056
  const emit = (p, q) => {
2943
3057
  if (source.length >= 1e5)
@@ -2948,23 +3062,23 @@ function sample(line, project, options, cellStep) {
2948
3062
  const at = (p) => {
2949
3063
  const q = project(p);
2950
3064
  if (!q)
2951
- throw new Error("Elastic grid is undefined inside its coverage");
3065
+ throw new Error(`Elastic grid is undefined inside its coverage at ${p}`);
2952
3066
  return q;
2953
3067
  };
2954
- const visit = (a, b, qa, qb, depth) => {
2955
- const mid = lerp(a, b, 0.5), qm = at(mid), chord2 = lerp(qa, qb, 0.5);
3068
+ const visit = (a, b, qa, qb, depth, segment, t0, t1) => {
3069
+ const tm = (t0 + t1) / 2, mid = interpolate ? interpolate(segment, tm) : lerp(a, b, 0.5), qm = at(mid), chord2 = lerp(qa, qb, 0.5);
2956
3070
  if (Math.hypot(b[0] - a[0], b[1] - a[1]) > Math.min(step, cellStep / 2) || Math.hypot(qm[0] - chord2[0], qm[1] - chord2[1]) > tolerance) {
2957
3071
  if (depth >= 24)
2958
3072
  throw new Error("Elastic sampling limit exceeded");
2959
- visit(a, mid, qa, qm, depth + 1);
2960
- visit(mid, b, qm, qb, depth + 1);
3073
+ visit(a, mid, qa, qm, depth + 1, segment, t0, tm);
3074
+ visit(mid, b, qm, qb, depth + 1, segment, tm, t1);
2961
3075
  } else
2962
3076
  emit(b, qb);
2963
3077
  };
2964
3078
  if (line.length) {
2965
3079
  emit(line[0], at(line[0]));
2966
3080
  for (let i = 1; i < line.length; i++)
2967
- visit(line[i - 1], line[i], at(line[i - 1]), at(line[i]), 0);
3081
+ visit(line[i - 1], line[i], at(line[i - 1]), at(line[i]), 0, i - 1, 0, 1);
2968
3082
  }
2969
3083
  return { source, xy };
2970
3084
  }
@@ -3001,6 +3115,65 @@ function indexDomain(polygons) {
3001
3115
  }
3002
3116
 
3003
3117
  // dist/discontinuous/map-transition.js
3118
+ var directWorldProjections = /* @__PURE__ */ new Set([
3119
+ "gilbert",
3120
+ "aitoff",
3121
+ "august",
3122
+ "baker",
3123
+ "boggs",
3124
+ "bromley",
3125
+ "collignon",
3126
+ "craster",
3127
+ "eckert1",
3128
+ "eckert2",
3129
+ "eckert3",
3130
+ "eckert4",
3131
+ "eckert5",
3132
+ "eckert6",
3133
+ "fahey",
3134
+ "foucaut",
3135
+ "ginzburg4",
3136
+ "ginzburg5",
3137
+ "ginzburg6",
3138
+ "ginzburg8",
3139
+ "ginzburg9",
3140
+ "homolosine",
3141
+ "kavrayskiy7",
3142
+ "larrivee",
3143
+ "laskowski",
3144
+ "miller",
3145
+ "mollweide",
3146
+ "mt-flat-polar-parabolic",
3147
+ "mt-flat-polar-quartic",
3148
+ "mt-flat-polar-sinusoidal",
3149
+ "natural-earth2",
3150
+ "nell-hammer",
3151
+ "patterson",
3152
+ "robinson",
3153
+ "sinusoidal",
3154
+ "times",
3155
+ "van-der-grinten",
3156
+ "van-der-grinten2",
3157
+ "van-der-grinten3",
3158
+ "van-der-grinten4",
3159
+ "wagner4",
3160
+ "wagner6",
3161
+ "winkel3",
3162
+ "bonne",
3163
+ "bottomley",
3164
+ "cylindrical-equal-area",
3165
+ "cylindrical-stereographic",
3166
+ "foucaut-sinusoidal",
3167
+ "hammer",
3168
+ "hill",
3169
+ "hufnagel",
3170
+ "hyperelliptical",
3171
+ "lagrange",
3172
+ "loximuthal",
3173
+ "wagner7",
3174
+ "quartic-authalic",
3175
+ "wagner"
3176
+ ]);
3004
3177
  var MapTransitionGeometry = class {
3005
3178
  constructor(parts, strategy, radius) {
3006
3179
  this.strategy = strategy;
@@ -3232,7 +3405,7 @@ function fullCoverage(map) {
3232
3405
  const overlaps = domains.some((domain, i) => domains.slice(i + 1).some((other) => import_polygon_clipping3.default.intersection(domain, other).length > 0));
3233
3406
  covered = completeSections && !overlaps && import_polygon_clipping3.default.difference(world, ...domains).length === 0;
3234
3407
  } else if (d.type === "world") {
3235
- covered = ["sinusoidal", "mollweide", "robinson"].includes(d.projection);
3408
+ covered = directWorldProjections.has(d.projection) && Object.keys(d.parameters ?? {}).length === 0;
3236
3409
  }
3237
3410
  coverageCache.set(map, covered);
3238
3411
  return covered;
@@ -3282,6 +3455,16 @@ function prepareMapFeatures(map, features, options = {}) {
3282
3455
  throw new Error("Expected a feature array");
3283
3456
  if (options.polygonOutlines !== void 0 && typeof options.polygonOutlines !== "boolean")
3284
3457
  throw new Error("polygonOutlines must be boolean");
3458
+ const provenance = options.provenance;
3459
+ if (provenance !== void 0 && (!provenance || typeof provenance !== "object" || Array.isArray(provenance)))
3460
+ throw new Error("Unsupported provenance policy");
3461
+ if ((provenance == null ? void 0 : provenance.distances) !== void 0 && (!provenance.distances || typeof provenance.distances !== "object"))
3462
+ throw new Error("Unsupported provenance distance policy");
3463
+ if (provenance && (typeof provenance !== "object" || Object.keys(provenance).some((k2) => !["sourceSegments", "boundaries", "distances", "polygonEdges"].includes(k2)) || [provenance.sourceSegments, provenance.boundaries, provenance.polygonEdges].some((v) => v !== void 0 && typeof v !== "boolean") || provenance.distances && (provenance.distances.metric !== "spherical" || !Number.isFinite(provenance.distances.radius) || provenance.distances.radius <= 0)))
3464
+ throw new Error("Unsupported provenance policy");
3465
+ const edgeKinds = [], edgeRings = [], edgeSegments = [], edgeParameters = [];
3466
+ const lineage = Boolean((provenance == null ? void 0 : provenance.sourceSegments) || (provenance == null ? void 0 : provenance.boundaries) || (provenance == null ? void 0 : provenance.distances));
3467
+ const segments = [], parameters = [], originalVertices = [], distances = [], startReasons = [], endReasons = [];
3285
3468
  const regionIds = map.regions.map((r) => r.id), regionIndices = new Map(regionIds.map((id, i) => [id, i]));
3286
3469
  const points = vertices(), paths = vertices(), polygons = vertices();
3287
3470
  const pointParts = [], pathParts = [], polygonParts = [];
@@ -3302,7 +3485,27 @@ function prepareMapFeatures(map, features, options = {}) {
3302
3485
  const isRing = ringIndex !== void 0;
3303
3486
  if (isRing && !closed(line))
3304
3487
  line.push([...line[0]]);
3305
- for (const p of map.projectLine(line, options.sampling)) {
3488
+ for (const p of map.projectLine(line, {
3489
+ ...options.sampling,
3490
+ pathProvenance: lineage,
3491
+ pathDistances: Boolean(provenance == null ? void 0 : provenance.distances),
3492
+ pathBoundaries: Boolean(provenance == null ? void 0 : provenance.boundaries)
3493
+ })) {
3494
+ if (lineage) {
3495
+ if (!p.provenance)
3496
+ throw new Error("Map backend does not support path provenance");
3497
+ if (provenance == null ? void 0 : provenance.sourceSegments) {
3498
+ segments.push(...p.provenance.segments);
3499
+ parameters.push(...p.provenance.parameters);
3500
+ originalVertices.push(...p.provenance.vertices);
3501
+ }
3502
+ if (provenance == null ? void 0 : provenance.distances)
3503
+ distances.push(...p.provenance.angularDistances.map((d) => d * provenance.distances.radius));
3504
+ if (provenance == null ? void 0 : provenance.boundaries) {
3505
+ startReasons.push(p.provenance.startReason);
3506
+ endReasons.push(p.provenance.endReason);
3507
+ }
3508
+ }
3306
3509
  append(paths, p.coordinates, p.sourceCoordinates);
3307
3510
  pathParts.push({
3308
3511
  ...part(featureIndex, geometryPath, p.regionId),
@@ -3314,12 +3517,41 @@ function prepareMapFeatures(map, features, options = {}) {
3314
3517
  }
3315
3518
  };
3316
3519
  const addPolygon = (input, featureIndex, geometryPath) => {
3520
+ var _a;
3317
3521
  if (!input.length)
3318
3522
  return;
3319
3523
  const rings = input.map((r) => r.map(position));
3320
- const parts = options.sphericalCuts ? map.projectGlobalPolygon(rings, { ...options.sampling, ...options.sphericalCuts }) : map.projectPolygon(rings, options.sampling);
3524
+ const parts = options.sphericalCuts ? map.projectGlobalPolygon(rings, {
3525
+ ...options.sampling,
3526
+ ...options.sphericalCuts,
3527
+ polygonEdgeProvenance: provenance == null ? void 0 : provenance.polygonEdges
3528
+ }) : map.projectPolygon(rings, {
3529
+ ...options.sampling,
3530
+ polygonEdgeProvenance: provenance == null ? void 0 : provenance.polygonEdges
3531
+ });
3321
3532
  for (const p of parts) {
3322
3533
  for (let i = 0; i < p.coordinates.length; i++) {
3534
+ if (provenance == null ? void 0 : provenance.polygonEdges) {
3535
+ const edges = (_a = p.edgeProvenance) == null ? void 0 : _a[i];
3536
+ if (!edges)
3537
+ throw new Error("Map backend does not support polygon edge provenance");
3538
+ const kinds = [
3539
+ "source-boundary",
3540
+ "coverage-boundary",
3541
+ "chart-cut",
3542
+ "continuous-chart-join"
3543
+ ];
3544
+ for (const edge2 of edges) {
3545
+ edgeKinds.push(kinds.indexOf(edge2.kind) + 1);
3546
+ edgeRings.push(edge2.ring);
3547
+ edgeSegments.push(edge2.segment);
3548
+ edgeParameters.push(edge2.t0, edge2.t1);
3549
+ }
3550
+ edgeKinds.push(0);
3551
+ edgeRings.push(4294967295);
3552
+ edgeSegments.push(4294967295);
3553
+ edgeParameters.push(0, 0);
3554
+ }
3323
3555
  append(polygons, p.coordinates[i], p.sourceCoordinates[i]);
3324
3556
  ringStarts.push(polygons.positions.length / 2);
3325
3557
  }
@@ -3383,12 +3615,38 @@ function prepareMapFeatures(map, features, options = {}) {
3383
3615
  featureIds,
3384
3616
  regionIds,
3385
3617
  points: { ...pack(points), parts: pointParts },
3386
- paths: { ...pack(paths), parts: pathParts, pathStarts: new Uint32Array(pathStarts) },
3618
+ paths: {
3619
+ ...pack(paths),
3620
+ parts: pathParts,
3621
+ pathStarts: new Uint32Array(pathStarts),
3622
+ ...lineage ? {
3623
+ provenance: {
3624
+ ...(provenance == null ? void 0 : provenance.sourceSegments) ? {
3625
+ sourceSegmentIndices: new Uint32Array(segments),
3626
+ sourceSegmentParameters: new Float64Array(parameters),
3627
+ sourceVertexIndices: new Uint32Array(originalVertices)
3628
+ } : {},
3629
+ ...(provenance == null ? void 0 : provenance.distances) ? {
3630
+ sourceDistances: new Float64Array(distances),
3631
+ distanceRadius: provenance.distances.radius
3632
+ } : {},
3633
+ ...(provenance == null ? void 0 : provenance.boundaries) ? { startReasons, endReasons } : {}
3634
+ }
3635
+ } : {}
3636
+ },
3387
3637
  polygons: {
3388
3638
  ...pack(polygons),
3389
3639
  parts: polygonParts,
3390
3640
  ringStarts: new Uint32Array(ringStarts),
3391
- polygonStarts: new Uint32Array(polygonStarts)
3641
+ polygonStarts: new Uint32Array(polygonStarts),
3642
+ ...(provenance == null ? void 0 : provenance.polygonEdges) ? {
3643
+ edgeProvenance: {
3644
+ kinds: new Uint8Array(edgeKinds),
3645
+ sourceRingIndices: new Uint32Array(edgeRings),
3646
+ sourceSegmentIndices: new Uint32Array(edgeSegments),
3647
+ sourceParameterRanges: new Float64Array(edgeParameters)
3648
+ }
3649
+ } : {}
3392
3650
  }
3393
3651
  };
3394
3652
  }
@@ -3396,6 +3654,171 @@ function prepareMapFeatures(map, features, options = {}) {
3396
3654
  // dist/discontinuous/elastic/map.js
3397
3655
  var import_polygon_clipping4 = __toESM(require("polygon-clipping"), 1);
3398
3656
 
3657
+ // dist/discontinuous/polygon-constraints.js
3658
+ function tracePolygonConstraints(input, output, tolerance = 1e-10, priorities, accept, degenerate) {
3659
+ const constraints = input.flatMap((ring, r) => ring.slice(0, -1).map((a, i) => ({ a, b: ring[i + 1], r, i })));
3660
+ let work = 0;
3661
+ const tick = () => {
3662
+ if (++work > 2e7)
3663
+ throw new Error("Polygon constraint work limit exceeded");
3664
+ };
3665
+ const fraction = (p, a, b) => {
3666
+ const dx = b[0] - a[0], dy = b[1] - a[1], l = dx * dx + dy * dy;
3667
+ return l ? ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / l : NaN;
3668
+ };
3669
+ const on = (p, a, b) => {
3670
+ tick();
3671
+ const t = fraction(p, a, b), length = Math.hypot(b[0] - a[0], b[1] - a[1]);
3672
+ return length > 0 && t >= -tolerance && t <= 1 + tolerance && Math.hypot(p[0] - a[0] - t * (b[0] - a[0]), p[1] - a[1] - t * (b[1] - a[1])) <= tolerance * Math.max(1, length);
3673
+ };
3674
+ const boxes = constraints.map((c) => [
3675
+ Math.min(c.a[0], c.b[0]),
3676
+ Math.min(c.a[1], c.b[1]),
3677
+ Math.max(c.a[0], c.b[0]),
3678
+ Math.max(c.a[1], c.b[1])
3679
+ ]);
3680
+ const build = (ids) => {
3681
+ const box = [Infinity, Infinity, -Infinity, -Infinity];
3682
+ for (const id of ids) {
3683
+ tick();
3684
+ const b = boxes[id];
3685
+ box[0] = Math.min(box[0], b[0]);
3686
+ box[1] = Math.min(box[1], b[1]);
3687
+ box[2] = Math.max(box[2], b[2]);
3688
+ box[3] = Math.max(box[3], b[3]);
3689
+ }
3690
+ if (ids.length <= 16)
3691
+ return { box, ids };
3692
+ const axis = box[2] - box[0] >= box[3] - box[1] ? 0 : 1;
3693
+ ids.sort((a, b) => {
3694
+ tick();
3695
+ return boxes[a][axis] + boxes[a][axis + 2] - boxes[b][axis] - boxes[b][axis + 2];
3696
+ });
3697
+ const middle = Math.floor(ids.length / 2);
3698
+ return { box, left: build(ids.slice(0, middle)), right: build(ids.slice(middle)) };
3699
+ };
3700
+ const root = build(constraints.map((_, i) => i));
3701
+ const padding = tolerance * Math.max(1, root.box[2] - root.box[0], root.box[3] - root.box[1]);
3702
+ const query = (a, b) => {
3703
+ const box = [
3704
+ Math.min(a[0], b[0]) - padding,
3705
+ Math.min(a[1], b[1]) - padding,
3706
+ Math.max(a[0], b[0]) + padding,
3707
+ Math.max(a[1], b[1]) + padding
3708
+ ], ids = [];
3709
+ const overlaps = (b2) => b2[0] <= box[2] && b2[2] >= box[0] && b2[1] <= box[3] && b2[3] >= box[1];
3710
+ const visit = (node) => {
3711
+ tick();
3712
+ if (!overlaps(node.box))
3713
+ return;
3714
+ if (node.ids) {
3715
+ for (const id of node.ids) {
3716
+ tick();
3717
+ if (overlaps(boxes[id]))
3718
+ ids.push(id);
3719
+ }
3720
+ } else {
3721
+ visit(node.left);
3722
+ visit(node.right);
3723
+ }
3724
+ };
3725
+ visit(root);
3726
+ return ids.map((id) => constraints[id]);
3727
+ };
3728
+ const rings = [], owners = [];
3729
+ for (const ring of output) {
3730
+ const points = [];
3731
+ for (let i = 1; i < ring.length; i++) {
3732
+ const a = ring[i - 1], b = ring[i];
3733
+ if (a.every((v, k2) => v === b[k2]))
3734
+ continue;
3735
+ const breaks = [{ t: 0, p: a }];
3736
+ for (const c of query(a, b))
3737
+ if (on(c.a, a, b) && (!accept || accept(c.a))) {
3738
+ const t = fraction(c.a, a, b);
3739
+ if (t > 1e-12 && t < 1 - 1e-12)
3740
+ breaks.push({ t, p: c.a });
3741
+ }
3742
+ breaks.sort((a2, b2) => a2.t - b2.t);
3743
+ for (const item of breaks)
3744
+ if (!points.length || Math.hypot(...points[points.length - 1].map((v, k2) => v - item.p[k2])) > 1e-14)
3745
+ points.push([...item.p]);
3746
+ }
3747
+ if (points.length < 3)
3748
+ continue;
3749
+ points.push([...points[0]]);
3750
+ const tags = [];
3751
+ for (let i = 1; i < points.length; i++) {
3752
+ const a = points[i - 1], b = points[i];
3753
+ if (degenerate == null ? void 0 : degenerate(a, b)) {
3754
+ tags.push(null);
3755
+ continue;
3756
+ }
3757
+ let candidates = query(a, b).filter((c) => on(a, c.a, c.b) && on(b, c.a, c.b));
3758
+ const same = (p, q) => Math.hypot(...p.map((v, k2) => v - q[k2])) <= 64 * Number.EPSILON * Math.max(1, ...p.map(Math.abs), ...q.map(Math.abs));
3759
+ const exact = candidates.filter((c) => same(a, c.a) && same(b, c.b) || same(a, c.b) && same(b, c.a));
3760
+ if (exact.length)
3761
+ candidates = exact;
3762
+ if (candidates.some((c) => {
3763
+ var _a, _b;
3764
+ return ((_b = (_a = priorities == null ? void 0 : priorities[c.r]) == null ? void 0 : _a[c.i]) == null ? void 0 : _b.kind) === "source-boundary";
3765
+ }))
3766
+ candidates = candidates.filter((c) => {
3767
+ var _a, _b;
3768
+ return ((_b = (_a = priorities == null ? void 0 : priorities[c.r]) == null ? void 0 : _a[c.i]) == null ? void 0 : _b.kind) === "source-boundary";
3769
+ });
3770
+ if (candidates.length > 1) {
3771
+ const score = (c) => Math.max(...[a, b].map((p) => {
3772
+ const t = Math.min(1, Math.max(0, fraction(p, c.a, c.b)));
3773
+ return Math.hypot(p[0] - c.a[0] - t * (c.b[0] - c.a[0]), p[1] - c.a[1] - t * (c.b[1] - c.a[1]));
3774
+ }));
3775
+ candidates.sort((a2, b2) => score(a2) - score(b2));
3776
+ const epsilon6 = 64 * Number.EPSILON * Math.max(1, ...a.map(Math.abs), ...b.map(Math.abs));
3777
+ if (score(candidates[1]) - score(candidates[0]) > epsilon6)
3778
+ candidates = [candidates[0]];
3779
+ else if (Math.hypot(a[0] - b[0], a[1] - b[1]) <= 2 * epsilon6)
3780
+ candidates = [];
3781
+ else
3782
+ throw new Error("Ambiguous polygon clipping constraint ancestry");
3783
+ }
3784
+ tags.push(candidates.length ? [candidates[0].r, candidates[0].i] : null);
3785
+ }
3786
+ rings.push(points);
3787
+ owners.push(tags);
3788
+ }
3789
+ return { rings, owners };
3790
+ }
3791
+ function composePolygonEdge(parent, a, b, start, end) {
3792
+ if (parent.kind !== "source-boundary")
3793
+ return { ...parent };
3794
+ const v = geographic(a), length = angle(v, geographic(b));
3795
+ const parameter = (p) => length ? Math.min(1, angle(v, geographic(p)) / length) : 0;
3796
+ return {
3797
+ ...parent,
3798
+ t0: parent.t0 + parameter(start) * (parent.t1 - parent.t0),
3799
+ t1: parent.t0 + parameter(end) * (parent.t1 - parent.t0)
3800
+ };
3801
+ }
3802
+
3803
+ // dist/discontinuous/source-arc.js
3804
+ function sourceArc(a, b) {
3805
+ const va = geographic(a), vb = geographic(b), length = angle(va, vb);
3806
+ const poleA = Math.abs(a[1]) === 90, poleB = Math.abs(b[1]) === 90;
3807
+ const start = poleA && !poleB ? [b[0], a[1]] : [...a], end = poleB && !poleA ? [a[0], b[1]] : [...b];
3808
+ return (t) => {
3809
+ if (t === 0)
3810
+ return [...start];
3811
+ if (t === 1)
3812
+ return [...end];
3813
+ if (length < 1e-14)
3814
+ return [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])];
3815
+ const p = coordinates(unit(va.map((v, k2) => (Math.sin((1 - t) * length) * v + Math.sin(t * length) * vb[k2]) / Math.sin(length))));
3816
+ const reference = start[0] + t * (end[0] - start[0]);
3817
+ p[0] += 360 * Math.round((reference - p[0]) / 360);
3818
+ return p;
3819
+ };
3820
+ }
3821
+
3399
3822
  // dist/discontinuous/cap-map.js
3400
3823
  var CapMap = class extends DiscontinuousMap {
3401
3824
  constructor(regions2, local, source, cap2, radius, polarLimits = false, nonlinear = false) {
@@ -3450,10 +3873,11 @@ var CapMap = class extends DiscontinuousMap {
3450
3873
  unprojectPoint(p, id) {
3451
3874
  return super.unprojectPoint(p, id).filter((q) => this.visible(q.position));
3452
3875
  }
3453
- clip(rings, polygon, options, regionId) {
3876
+ clip(rings, polygon, options, regionId, segmentIndices) {
3454
3877
  const output = [];
3455
3878
  const originals = /* @__PURE__ */ new Map();
3456
- let ring = [];
3879
+ let ring = [], indices = [], activeSegment = 0;
3880
+ let previousInput, currentInput;
3457
3881
  const precision = Math.min((options == null ? void 0 : options.maxAngularStep) ?? 2, Math.sqrt(((options == null ? void 0 : options.tolerance) ?? 1e-4) / this.radius) / RAD / 4) * RAD;
3458
3882
  if (!Number.isFinite(precision) || 2 * Math.PI / precision > 1e6)
3459
3883
  throw new Error("Projection sampling limit exceeded; relax tolerance");
@@ -3508,14 +3932,49 @@ var CapMap = class extends DiscontinuousMap {
3508
3932
  point: (x, y) => {
3509
3933
  if (++emitted > 1e6)
3510
3934
  throw new Error("Projection sampling limit exceeded; relax tolerance");
3511
- ring.push(originals.get(`${x},${y}`) ?? this.source([x / RAD, y / RAD]));
3935
+ let source = originals.get(`${x},${y}`);
3936
+ if (!source && !polygon && this.nonlinear && previousInput && currentInput) {
3937
+ const a = geographic(previousInput), b = geographic(currentInput), length = angle(a, b);
3938
+ const at = (t) => length < 1e-14 ? [...currentInput] : coordinates(unit(a.map((v, k2) => (Math.sin((1 - t) * length) * v + Math.sin(t * length) * b[k2]) / Math.sin(length))));
3939
+ const value = (t) => geographic(local(at(t)))[0] - Math.cos(this.cap);
3940
+ const approximate = this.source([x / RAD, y / RAD]);
3941
+ const reference = length ? Math.min(1, angle(a, geographic(approximate)) / length) : 0;
3942
+ const roots = [];
3943
+ for (let j = 0; j < 32; j++) {
3944
+ let lo = j / 32, hi = (j + 1) / 32, f = value(lo), g = value(hi);
3945
+ if (Math.abs(f) < 1e-14)
3946
+ roots.push(lo);
3947
+ if (f * g >= 0)
3948
+ continue;
3949
+ for (let k2 = 0; k2 < 52; k2++) {
3950
+ const mid = (lo + hi) / 2;
3951
+ if (value(mid) >= 0 === f >= 0)
3952
+ lo = mid;
3953
+ else
3954
+ hi = mid;
3955
+ }
3956
+ roots.push((lo + hi) / 2);
3957
+ }
3958
+ if (Math.abs(value(1)) < 1e-14)
3959
+ roots.push(1);
3960
+ if (!roots.length)
3961
+ throw new Error("Unresolved nonlinear cap source intersection");
3962
+ roots.sort((a2, b2) => Math.abs(a2 - reference) - Math.abs(b2 - reference));
3963
+ source = at(roots[0]);
3964
+ }
3965
+ ring.push(source ?? this.source([x / RAD, y / RAD]));
3966
+ if (segmentIndices)
3967
+ indices.push(activeSegment);
3512
3968
  },
3513
3969
  lineStart: () => {
3514
3970
  ring = [];
3971
+ indices = [];
3515
3972
  },
3516
3973
  lineEnd: () => {
3517
- if (ring.length > 1)
3974
+ if (ring.length > 1) {
3518
3975
  output.push(ring);
3976
+ segmentIndices == null ? void 0 : segmentIndices.push(indices);
3977
+ }
3519
3978
  },
3520
3979
  polygonStart: () => {
3521
3980
  },
@@ -3532,7 +3991,9 @@ var CapMap = class extends DiscontinuousMap {
3532
3991
  }
3533
3992
  const x = q[0] * RAD, y = q[1] * RAD;
3534
3993
  originals.set(`${x},${y}`, [...p]);
3994
+ currentInput = p;
3535
3995
  stream.point(x, y);
3996
+ previousInput = p;
3536
3997
  };
3537
3998
  const subdivide = (a, b, depth = 0) => {
3538
3999
  if (this.nonlinear && depth < 20) {
@@ -3549,11 +4010,14 @@ var CapMap = class extends DiscontinuousMap {
3549
4010
  emit(b);
3550
4011
  };
3551
4012
  for (const points of rings) {
4013
+ previousInput = void 0;
3552
4014
  stream.lineStart();
3553
4015
  if (points.length)
3554
4016
  emit(points[0]);
3555
- for (let i = 1; i < points.length; i++)
4017
+ for (let i = 1; i < points.length; i++) {
4018
+ activeSegment = i - 1;
3556
4019
  subdivide(points[i - 1], points[i]);
4020
+ }
3557
4021
  if (polygon && points.length)
3558
4022
  subdivide(points[points.length - 1], points[0]);
3559
4023
  stream.lineEnd();
@@ -3565,7 +4029,41 @@ var CapMap = class extends DiscontinuousMap {
3565
4029
  projectLine(line, options) {
3566
4030
  if (this.cap === Math.PI / 2 && !this.nonlinear)
3567
4031
  return super.projectLine(line, options);
3568
- return super.projectLine(line, options).flatMap((part) => this.clip([part.sourceCoordinates], false, options, part.regionId).flatMap((line2) => super.projectLine(line2, options).filter((q) => q.regionId === part.regionId)));
4032
+ return super.projectLine(line, options).flatMap((part) => {
4033
+ const indices = part.provenance ? [] : void 0;
4034
+ const clipped = this.clip([part.sourceCoordinates], false, options, part.regionId, indices);
4035
+ return clipped.flatMap((points, piece) => super.projectLine(points, options).filter((q) => q.regionId === part.regionId).map((q) => {
4036
+ const parent = part.provenance, child = q.provenance;
4037
+ if (!parent || !child)
4038
+ return q;
4039
+ const provenance = {
4040
+ segments: [],
4041
+ parameters: [],
4042
+ vertices: [],
4043
+ angularDistances: []
4044
+ };
4045
+ for (let j = 0; j < q.sourceCoordinates.length; j++) {
4046
+ const k2 = child.segments[j];
4047
+ const edge2 = indices[piece][k2 + 1];
4048
+ const a = geographic(part.sourceCoordinates[edge2]), b = geographic(part.sourceCoordinates[edge2 + 1]), p = geographic(q.sourceCoordinates[j]);
4049
+ const length = angle(a, b), t = length ? Math.min(1, angle(a, p) / length) : 0;
4050
+ const endpoint = t < 1e-12 ? edge2 : t > 1 - 1e-12 ? edge2 + 1 : -1;
4051
+ const segment = parent.segments[edge2 + 1];
4052
+ const from = parent.segments[edge2] === segment ? parent.parameters[edge2] : 0;
4053
+ provenance.segments.push(endpoint >= 0 ? parent.segments[endpoint] : segment);
4054
+ provenance.parameters.push(endpoint >= 0 ? parent.parameters[endpoint] : from + t * (parent.parameters[edge2 + 1] - from));
4055
+ provenance.vertices.push(endpoint >= 0 ? parent.vertices[endpoint] : 4294967295);
4056
+ if (options == null ? void 0 : options.pathDistances)
4057
+ provenance.angularDistances.push(parent.angularDistances[edge2] + t * (parent.angularDistances[edge2 + 1] - parent.angularDistances[edge2]));
4058
+ }
4059
+ if (options == null ? void 0 : options.pathBoundaries) {
4060
+ const same = (a, b) => angle(geographic(a), geographic(b)) < 1e-12;
4061
+ provenance.startReason = same(q.sourceCoordinates[0], part.sourceCoordinates[0]) ? parent.startReason : "coverage-boundary";
4062
+ provenance.endReason = same(q.sourceCoordinates[q.sourceCoordinates.length - 1], part.sourceCoordinates[part.sourceCoordinates.length - 1]) ? parent.endReason : "coverage-boundary";
4063
+ }
4064
+ return { ...q, provenance };
4065
+ }));
4066
+ });
3569
4067
  }
3570
4068
  projectPolygon(rings, options) {
3571
4069
  if (this.cap === Math.PI / 2 && !this.nonlinear)
@@ -3638,15 +4136,95 @@ var CapMap = class extends DiscontinuousMap {
3638
4136
  };
3639
4137
  for (const ring of outer) {
3640
4138
  const plane = ring.map(planar), sources = [ring, ...holes.filter((h2) => contains2(plane, planar(h2[0])))];
4139
+ const parentPlane = part.sourceCoordinates.map((r) => r.map(planar));
4140
+ const fromPlane = (p) => coordinates(unit(center3.map((v, k2) => v + x[k2] * p[0] + y[k2] * p[1])));
4141
+ const traced = tracePolygonConstraints(parentPlane, sources.map((r) => r.map(planar)), this.nonlinear ? 2e-6 : 1e-10, part.edgeProvenance, (p) => this.visible(fromPlane(p)));
4142
+ const resolved = traced.rings.map((r) => r.map(fromPlane));
4143
+ traced.owners.forEach((owners, r) => owners.forEach((owner, i) => {
4144
+ if (!owner)
4145
+ return;
4146
+ const [pr, pe] = owner, a = part.sourceCoordinates[pr][pe], b = part.sourceCoordinates[pr][pe + 1], at = sourceArc(a, b);
4147
+ for (const j of [i, i + 1]) {
4148
+ const point = resolved[r][j], distance3 = angle(geographic(a), geographic(b)), fraction = distance3 ? angle(geographic(a), geographic(point)) / distance3 : 0;
4149
+ if (fraction < 1e-10)
4150
+ resolved[r][j] = [...a];
4151
+ else if (fraction > 1 - 1e-10)
4152
+ resolved[r][j] = [...b];
4153
+ else if (Math.hypot(...cross(geographic(a), geographic(b))) > 1e-14 && Math.abs(dot(unit(cross(geographic(a), geographic(b))), geographic(point))) < 1e-12) {
4154
+ resolved[r][j] = at(Math.min(1, Math.max(0, fraction)));
4155
+ } else {
4156
+ const value = (t) => geographic(this.local(at(t)))[0] - Math.cos(this.cap), roots = [];
4157
+ for (let k2 = 0; k2 < 32; k2++) {
4158
+ let lo = k2 / 32, hi = (k2 + 1) / 32, f = value(lo), g = value(hi);
4159
+ if (Math.abs(f) < 1e-13)
4160
+ roots.push(lo);
4161
+ if (f * g >= 0)
4162
+ continue;
4163
+ for (let n = 0; n < 52; n++) {
4164
+ const mid = (lo + hi) / 2;
4165
+ if (value(mid) >= 0 === f >= 0)
4166
+ lo = mid;
4167
+ else
4168
+ hi = mid;
4169
+ }
4170
+ roots.push((lo + hi) / 2);
4171
+ }
4172
+ if (Math.abs(value(1)) < 1e-13)
4173
+ roots.push(1);
4174
+ if (!roots.length)
4175
+ throw new Error("Unresolved cap polygon source intersection");
4176
+ roots.sort((a2, b2) => Math.abs(a2 - fraction) - Math.abs(b2 - fraction));
4177
+ resolved[r][j] = at(roots[0]);
4178
+ }
4179
+ }
4180
+ }));
4181
+ for (const ring2 of resolved)
4182
+ ring2[ring2.length - 1] = [...ring2[0]];
3641
4183
  result.push({
3642
4184
  regionId: part.regionId,
3643
- sourceCoordinates: sources,
3644
- coordinates: sources.map((r) => r.map((p) => {
4185
+ sourceCoordinates: resolved,
4186
+ coordinates: resolved.map((r) => r.map((p) => {
3645
4187
  const q = super.projectPoint(p, part.regionId);
3646
4188
  if (!q)
3647
- throw new Error(`Cap point outside chart ${part.regionId}: ${p}, owner ${super.findRegions(p)}`);
4189
+ throw new Error("Cap polygon source constraint left its owning chart");
3648
4190
  return q.position;
3649
- }))
4191
+ })),
4192
+ ...(options == null ? void 0 : options.polygonEdgeProvenance) ? {
4193
+ edgeProvenance: traced.owners.map((owners, r) => owners.map((owner, i) => {
4194
+ const a = resolved[r][i], b = resolved[r][i + 1];
4195
+ if (owner) {
4196
+ const [pr, pe] = owner;
4197
+ return composePolygonEdge(part.edgeProvenance[pr][pe], part.sourceCoordinates[pr][pe], part.sourceCoordinates[pr][pe + 1], a, b);
4198
+ }
4199
+ if (angle(geographic(a), geographic(b)) < 64 * Number.EPSILON)
4200
+ return {
4201
+ kind: "continuous-chart-join",
4202
+ ring: 4294967295,
4203
+ segment: 4294967295,
4204
+ t0: 0,
4205
+ t1: 0
4206
+ };
4207
+ const kinds = [];
4208
+ region.boundary.forEach((p, j) => {
4209
+ const n = cross(geographic(p), geographic(region.boundary[(j + 1) % region.boundary.length])), length = Math.hypot(...n);
4210
+ if (length && [a, b].every((p2) => Math.abs(dot(n, geographic(p2))) / length < 1e-9)) {
4211
+ const seam = this.seams.find((s) => s.regions.some((id, k2) => id === region.id && s.edges[k2] === j));
4212
+ if (seam)
4213
+ kinds.push(seam.kind);
4214
+ }
4215
+ });
4216
+ const horizon = [a, b].every((p) => Math.abs(geographic(this.local(p))[0] - Math.cos(this.cap)) < 1e-7);
4217
+ if (!horizon && !kinds.length)
4218
+ throw new Error("Unknown cap polygon clipping constraint");
4219
+ return {
4220
+ kind: horizon ? "coverage-boundary" : kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : "continuous-chart-join",
4221
+ ring: 4294967295,
4222
+ segment: 4294967295,
4223
+ t0: 0,
4224
+ t1: 0
4225
+ };
4226
+ }))
4227
+ } : {}
3650
4228
  });
3651
4229
  }
3652
4230
  }
@@ -5384,6 +5962,94 @@ function invertCell(p, target) {
5384
5962
  return output;
5385
5963
  }
5386
5964
 
5965
+ // dist/discontinuous/elastic/source-arc.js
5966
+ function clipSourceArc(a, b, polygons) {
5967
+ const at = sourceArc(a, b), va = geographic(a), vb = geographic(b), omega = angle(va, vb), ts = [0, 1];
5968
+ let work = 0;
5969
+ const tick = () => {
5970
+ if (++work > 1e6)
5971
+ throw new Error("Elastic source clipping limit exceeded");
5972
+ };
5973
+ const linear = omega < 1e-14 || Math.abs(a[1]) < 1e-12 && Math.abs(b[1]) < 1e-12 || Math.abs(at(0)[0] - at(1)[0]) < 1e-12;
5974
+ const tangent = omega > 1e-14 ? va.map((v, k2) => (vb[k2] - v * Math.cos(omega)) / Math.sin(omega)) : [0, 0, 0];
5975
+ const momentum = Math.abs(omega * (va[0] * tangent[1] - va[1] * tangent[0])) + 64 * Number.EPSILON * omega;
5976
+ const vector = (t) => va.map((v, k2) => v * Math.cos(omega * t) + tangent[k2] * Math.sin(omega * t));
5977
+ const derivative = (t) => va.map((v, k2) => omega * (-v * Math.sin(omega * t) + tangent[k2] * Math.cos(omega * t)));
5978
+ for (const rings of polygons)
5979
+ for (const ring of rings)
5980
+ for (let edge2 = 1; edge2 < ring.length; edge2++) {
5981
+ const c = ring[edge2 - 1], d = ring[edge2], dx = d[0] - c[0], dy = d[1] - c[1], length = Math.hypot(dx, dy);
5982
+ if (!length)
5983
+ continue;
5984
+ const angular = omega / RAD, latitude = Math.min(90, Math.max(Math.abs(a[1]), Math.abs(b[1])) + angular), cosine = Math.cos(latitude * RAD), longitude3 = cosine > 1e-10 ? angular / cosine : 360;
5985
+ if (Math.max(c[1], d[1]) < Math.min(a[1], b[1]) - angular - 1e-10 || Math.min(c[1], d[1]) > Math.max(a[1], b[1]) + angular + 1e-10 || Math.max(c[0], d[0]) < Math.min(a[0], b[0]) - longitude3 - 1e-10 || Math.min(c[0], d[0]) > Math.max(a[0], b[0]) + longitude3 + 1e-10)
5986
+ continue;
5987
+ const value = (t) => {
5988
+ tick();
5989
+ const p = at(t);
5990
+ return ((p[0] - c[0]) * dy - (p[1] - c[1]) * dx) / length;
5991
+ };
5992
+ const put = (t) => {
5993
+ const p = at(t), u = ((p[0] - c[0]) * dx + (p[1] - c[1]) * dy) / (length * length);
5994
+ if (u >= -1e-10 && u <= 1 + 1e-10)
5995
+ ts.push(t);
5996
+ };
5997
+ const bisect = (lo, hi, f) => {
5998
+ for (let k2 = 0; k2 < 52; k2++) {
5999
+ const mid = (lo + hi) / 2;
6000
+ if (value(mid) >= 0 === f >= 0)
6001
+ lo = mid;
6002
+ else
6003
+ hi = mid;
6004
+ }
6005
+ put((lo + hi) / 2);
6006
+ };
6007
+ const visit = (lo, hi, depth) => {
6008
+ tick();
6009
+ const f = value(lo), g = value(hi), mid = (lo + hi) / 2, h2 = (hi - lo) / 2;
6010
+ if (Math.abs(f) < 1e-12)
6011
+ put(lo);
6012
+ if (Math.abs(g) < 1e-12)
6013
+ put(hi);
6014
+ if (linear) {
6015
+ if (f * g < 0)
6016
+ bisect(lo, hi, f);
6017
+ return;
6018
+ }
6019
+ const v = vector(mid), dv = derivative(mid), r2 = v[0] * v[0] + v[1] * v[1];
6020
+ const df = ((v[0] * dv[1] - v[1] * dv[0]) / r2 * dy - dv[2] / Math.sqrt(r2) * dx) / (RAD * length);
6021
+ let z = Math.max(Math.abs(vector(lo)[2]), Math.abs(vector(hi)[2]));
6022
+ const phase = Math.atan2(tangent[2], va[2]);
6023
+ for (let k2 = -2; k2 <= 2; k2++) {
6024
+ const theta = phase + k2 * Math.PI;
6025
+ if (theta >= omega * lo && theta <= omega * hi)
6026
+ z = Math.max(z, Math.hypot(va[2], tangent[2]));
6027
+ }
6028
+ const r = Math.sqrt(Math.max(0, 1 - Math.min(1, z) ** 2));
6029
+ const second = r ? (2 * momentum * omega * Math.abs(dy) / r ** 4 + omega ** 2 * Math.abs(dx) * (1 / r + 1 / r ** 3)) / (RAD * length) : Infinity;
6030
+ if (Number.isFinite(df) && Math.abs(df) > second * h2 + 1e-12) {
6031
+ if (f * g < 0)
6032
+ bisect(lo, hi, f);
6033
+ return;
6034
+ }
6035
+ const speed = r ? (momentum * Math.abs(dy) / r ** 2 + omega * Math.abs(dx)) / (RAD * length) : Infinity;
6036
+ if (Math.abs(value(mid)) > speed * h2 + 1e-12)
6037
+ return;
6038
+ if (depth >= 24)
6039
+ throw new Error("Unresolved Elastic source clipping tangent or pole");
6040
+ visit(lo, mid, depth + 1);
6041
+ visit(mid, hi, depth + 1);
6042
+ };
6043
+ visit(0, 1, 0);
6044
+ }
6045
+ ts.sort((a2, b2) => a2 - b2);
6046
+ const result = [];
6047
+ for (let i = 1; i < ts.length; i++)
6048
+ if (ts[i] - ts[i - 1] > 1e-12 && contains(polygons, at((ts[i] + ts[i - 1]) / 2)))
6049
+ result.push({ a: at(ts[i - 1]), b: at(ts[i]) });
6050
+ return result;
6051
+ }
6052
+
5387
6053
  // dist/discontinuous/elastic/map.js
5388
6054
  var kernel = Object.assign((x, y) => [x, y], {
5389
6055
  invert: (x, y) => [x, y]
@@ -5515,7 +6181,7 @@ var ElasticMap = class {
5515
6181
  }
5516
6182
  sourceOptions(options) {
5517
6183
  const { step } = sampling2(options);
5518
- return { maxAngularStep: Math.min(step, 1), tolerance: 1e-5 };
6184
+ return { ...options, maxAngularStep: Math.min(step, 1), tolerance: 1e-5 };
5519
6185
  }
5520
6186
  sampled(c, line, options) {
5521
6187
  return sample(line, (p) => c.grid.project(p), options, Math.min(180 / (c.grid.section.rows - 1), 360 / (c.grid.section.columns - 1)));
@@ -5524,23 +6190,70 @@ var ElasticMap = class {
5524
6190
  const out = [];
5525
6191
  for (const part of splitter.projectLine(line, this.sourceOptions(options)))
5526
6192
  for (const c of this.charts.filter((c2) => c2.baseId === part.regionId)) {
5527
- let current = [];
5528
- const flush = () => {
5529
- if (current.length > 1) {
5530
- const { source, xy } = this.sampled(c, current, options);
5531
- out.push({ regionId: c.region.id, coordinates: xy, sourceCoordinates: source });
5532
- }
5533
- current = [];
6193
+ let current;
6194
+ const boundary = (point, vertex) => {
6195
+ if (vertex === 0 || vertex === line.length - 1)
6196
+ return "source-endpoint";
6197
+ let edge2 = 0;
6198
+ const kinds = [];
6199
+ for (const polygon of c.domain)
6200
+ for (const ring of polygon)
6201
+ for (let i = 1; i < ring.length; i++, edge2++)
6202
+ if (onSegment(point, ring[i - 1], ring[i])) {
6203
+ const seam = this.seams.find((s) => s.regions.some((id, j) => id === c.region.id && s.edges[j] === edge2));
6204
+ kinds.push((seam == null ? void 0 : seam.kind) ?? "boundary");
6205
+ }
6206
+ return kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : kinds.includes("join") ? "continuous-chart-join" : "coverage-boundary";
5534
6207
  };
5535
- for (let i = 1; i < part.coordinates.length; i++)
5536
- for (const [a, b] of clipSegment(part.coordinates[i - 1], part.coordinates[i], c.domain)) {
5537
- if (current.length && Math.hypot(current[current.length - 1][0] - a[0], current[current.length - 1][1] - a[1]) > 1e-8)
5538
- flush();
5539
- if (!current.length)
5540
- current.push(a);
5541
- current.push(b);
6208
+ for (let i = 1; i < part.sourceCoordinates.length; i++) {
6209
+ const a = part.coordinates[i - 1], b = part.coordinates[i];
6210
+ const intervals = clipSourceArc(a, b, c.domain);
6211
+ if (!intervals.length) {
6212
+ current = void 0;
6213
+ continue;
5542
6214
  }
5543
- flush();
6215
+ if (Math.hypot(intervals[0].a[0] - a[0], intervals[0].a[1] - a[1]) > 1e-8)
6216
+ current = void 0;
6217
+ for (const clipped of intervals) {
6218
+ const arc = sourceArc(clipped.a, clipped.b);
6219
+ const samples = sample([clipped.a, clipped.b], (p) => c.grid.project(p), options, Math.min(180 / (c.grid.section.rows - 1), 360 / (c.grid.section.columns - 1)), (_, t) => arc(t));
6220
+ const first = samples.source[0], last = samples.source[samples.source.length - 1];
6221
+ const joined = current && Math.hypot(...current.sourceCoordinates[current.sourceCoordinates.length - 1].map((v, k2) => v - first[k2])) < 1e-8;
6222
+ if (!joined) {
6223
+ current = { regionId: c.region.id, coordinates: [], sourceCoordinates: [] };
6224
+ if (part.provenance)
6225
+ current.provenance = {
6226
+ segments: [],
6227
+ parameters: [],
6228
+ vertices: [],
6229
+ angularDistances: []
6230
+ };
6231
+ out.push(current);
6232
+ }
6233
+ for (let j = joined ? 1 : 0; j < samples.source.length; j++) {
6234
+ const p = samples.source[j];
6235
+ current.sourceCoordinates.push(p);
6236
+ current.coordinates.push(samples.xy[j]);
6237
+ const parent = part.provenance, target = current.provenance;
6238
+ if (parent && target) {
6239
+ const length = angle(geographic(a), geographic(b)), t = length ? Math.min(1, angle(geographic(a), geographic(p)) / length) : 0;
6240
+ const endpoint = t < 1e-12 ? i - 1 : t > 1 - 1e-12 ? i : -1, segment = parent.segments[i], from = parent.segments[i - 1] === segment ? parent.parameters[i - 1] : 0;
6241
+ target.segments.push(endpoint >= 0 ? parent.segments[endpoint] : segment);
6242
+ target.parameters.push(endpoint >= 0 ? parent.parameters[endpoint] : from + t * (parent.parameters[i] - from));
6243
+ target.vertices.push(endpoint >= 0 ? parent.vertices[endpoint] : 4294967295);
6244
+ if (options.pathDistances)
6245
+ target.angularDistances.push(parent.angularDistances[i - 1] + t * (parent.angularDistances[i] - parent.angularDistances[i - 1]));
6246
+ if (options.pathBoundaries) {
6247
+ if (target.vertices.length === 1)
6248
+ target.startReason = boundary(p, target.vertices[0]);
6249
+ target.endReason = boundary(p, target.vertices[target.vertices.length - 1]);
6250
+ }
6251
+ }
6252
+ }
6253
+ if (Math.hypot(last[0] - b[0], last[1] - b[1]) > 1e-8)
6254
+ current = void 0;
6255
+ }
6256
+ }
5544
6257
  }
5545
6258
  return out;
5546
6259
  }
@@ -5548,13 +6261,175 @@ var ElasticMap = class {
5548
6261
  const out = [];
5549
6262
  for (const part of splitter.projectPolygon(rings, this.sourceOptions(options)))
5550
6263
  for (const c of this.charts.filter((c2) => c2.baseId === part.regionId)) {
5551
- const clipped = import_polygon_clipping4.default.intersection(snapPolygon(part.coordinates), c.domain.map(snapPolygon));
6264
+ const prepared = [], preparedEdges = [];
6265
+ part.coordinates.forEach((ring, r) => {
6266
+ const points = [], edges = [];
6267
+ for (let i = 1; i < ring.length; i++) {
6268
+ const a = ring[i - 1], b = ring[i], arc = sourceArc(a, b), length = angle(geographic(a), geographic(b)), fractions = [0, 1];
6269
+ for (const interval of clipSourceArc(a, b, c.domain))
6270
+ for (const p of [interval.a, interval.b]) {
6271
+ const t = length > 1e-14 ? angle(geographic(a), geographic(p)) / length : Math.abs(b[0] - a[0]) > 1e-14 ? (p[0] - a[0]) / (b[0] - a[0]) : 0;
6272
+ if (t > 1e-12 && t < 1 - 1e-12)
6273
+ fractions.push(t);
6274
+ }
6275
+ fractions.sort((a2, b2) => a2 - b2);
6276
+ const split = fractions.filter((t, i2) => !i2 || t - fractions[i2 - 1] > 1e-12);
6277
+ const append2 = (point) => {
6278
+ if (points.length >= 1e5)
6279
+ throw new Error("Elastic polygon constraint vertex limit exceeded");
6280
+ if (points.length) {
6281
+ const previous = points[points.length - 1];
6282
+ if (options.polygonEdgeProvenance)
6283
+ edges.push(composePolygonEdge(part.edgeProvenance[r][i - 1], a, b, previous, point));
6284
+ }
6285
+ points.push(point);
6286
+ };
6287
+ const first = arc(0);
6288
+ if (!points.length)
6289
+ points.push(first);
6290
+ else if (points[points.length - 1].some((v, k2) => v !== first[k2])) {
6291
+ if (angle(geographic(points[points.length - 1]), geographic(first)) > 1e-10)
6292
+ throw new Error("Discontinuous Elastic input source constraint");
6293
+ if (options.polygonEdgeProvenance)
6294
+ edges.push({
6295
+ kind: "continuous-chart-join",
6296
+ ring: 4294967295,
6297
+ segment: 4294967295,
6298
+ t0: 0,
6299
+ t1: 0
6300
+ });
6301
+ points.push(first);
6302
+ }
6303
+ for (let j = 1; j < split.length; j++) {
6304
+ append2(arc((split[j - 1] + split[j]) / 2));
6305
+ append2(arc(split[j]));
6306
+ }
6307
+ }
6308
+ if (points[points.length - 1].some((v, k2) => v !== points[0][k2])) {
6309
+ if (angle(geographic(points[points.length - 1]), geographic(points[0])) > 1e-10)
6310
+ throw new Error("Unclosed Elastic input source constraint");
6311
+ if (options.polygonEdgeProvenance)
6312
+ edges.push({
6313
+ kind: "continuous-chart-join",
6314
+ ring: 4294967295,
6315
+ segment: 4294967295,
6316
+ t0: 0,
6317
+ t1: 0
6318
+ });
6319
+ points.push([...points[0]]);
6320
+ }
6321
+ prepared.push(points);
6322
+ preparedEdges.push(edges);
6323
+ });
6324
+ const clipped = import_polygon_clipping4.default.intersection(snapPolygon(prepared), c.domain.map(snapPolygon));
5552
6325
  for (const polygon of clipped) {
5553
- const samples = polygon.map((r) => this.sampled(c, r, options));
6326
+ const input = snapPolygon(prepared), traced = tracePolygonConstraints(input, polygon, 1e-9, options.polygonEdgeProvenance ? preparedEdges : void 0, void 0, (a, b) => angle(geographic(a), geographic(b)) < 64 * Number.EPSILON), resolved = traced.rings.map((r) => r.map((p) => [...p]));
6327
+ traced.owners.forEach((owners, r) => owners.forEach((owner, i) => {
6328
+ if (!owner)
6329
+ return;
6330
+ const [pr, pe] = owner, a = prepared[pr][pe], b = prepared[pr][pe + 1], length = angle(geographic(a), geographic(b)), arc = sourceArc(a, b);
6331
+ for (const j of [i, i + 1]) {
6332
+ const q = traced.rings[r][j], start = input[pr][pe], end = input[pr][pe + 1];
6333
+ if (Math.hypot(q[0] - start[0], q[1] - start[1]) < 1e-8)
6334
+ resolved[r][j] = [...a];
6335
+ else if (Math.hypot(q[0] - end[0], q[1] - end[1]) < 1e-8)
6336
+ resolved[r][j] = [...b];
6337
+ else {
6338
+ const candidates = clipSourceArc(a, b, c.domain).flatMap((interval) => [
6339
+ interval.a,
6340
+ interval.b
6341
+ ]);
6342
+ if (!candidates.length)
6343
+ throw new Error("Unresolved Elastic polygon source intersection");
6344
+ const reference = length ? angle(geographic(a), geographic(q)) / length : 0;
6345
+ candidates.sort((x, y) => Math.abs((length ? angle(geographic(a), geographic(x)) / length : 0) - reference) - Math.abs((length ? angle(geographic(a), geographic(y)) / length : 0) - reference));
6346
+ const fraction = length ? angle(geographic(a), geographic(candidates[0])) / length : 0;
6347
+ resolved[r][j] = arc(Math.min(1, Math.max(0, fraction)));
6348
+ }
6349
+ }
6350
+ }));
6351
+ for (const ring of resolved)
6352
+ ring[ring.length - 1] = [...ring[0]];
6353
+ const generated = (a, b) => {
6354
+ if (angle(geographic(a), geographic(b)) < 64 * Number.EPSILON)
6355
+ return "continuous-chart-join";
6356
+ let edge2 = 0;
6357
+ const kinds = [];
6358
+ for (const polygon2 of c.domain)
6359
+ for (const ring of polygon2)
6360
+ for (let j = 1; j < ring.length; j++, edge2++)
6361
+ if (onSegment(a, ring[j - 1], ring[j]) && onSegment(b, ring[j - 1], ring[j])) {
6362
+ const seam = this.seams.find((s) => s.regions.some((id, k2) => id === c.region.id && s.edges[k2] === edge2));
6363
+ kinds.push((seam == null ? void 0 : seam.kind) ?? "boundary");
6364
+ }
6365
+ if (!kinds.length)
6366
+ throw new Error("Unknown Elastic polygon clipping constraint");
6367
+ return kinds.includes("boundary") ? "coverage-boundary" : kinds.includes("cut") ? "chart-cut" : "continuous-chart-join";
6368
+ };
6369
+ const coordinates2 = [], sourceCoordinates = [], edgeProvenance = [];
6370
+ resolved.forEach((ring, r) => {
6371
+ const source = [], xy = [], edges = [];
6372
+ for (let i = 1; i < ring.length; i++) {
6373
+ const owner = traced.owners[r][i - 1], arc = owner ? sourceArc(ring[i - 1], ring[i]) : void 0;
6374
+ const samples = sample(arc ? [arc(0), arc(1)] : [ring[i - 1], ring[i]], (p) => c.grid.project(p), options, Math.min(180 / (c.grid.section.rows - 1), 360 / (c.grid.section.columns - 1)), arc ? (_, t) => arc(t) : void 0);
6375
+ if (!source.length) {
6376
+ source.push(samples.source[0]);
6377
+ xy.push(samples.xy[0]);
6378
+ } else if (source[source.length - 1].some((v, k2) => Math.abs(v - samples.source[0][k2]) > 1e-10)) {
6379
+ if (angle(geographic(source[source.length - 1]), geographic(samples.source[0])) > 1e-10)
6380
+ throw new Error("Discontinuous Elastic polygon source constraint");
6381
+ if (options.polygonEdgeProvenance)
6382
+ edges.push({
6383
+ kind: "continuous-chart-join",
6384
+ ring: 4294967295,
6385
+ segment: 4294967295,
6386
+ t0: 0,
6387
+ t1: 0
6388
+ });
6389
+ source.push(samples.source[0]);
6390
+ xy.push(samples.xy[0]);
6391
+ }
6392
+ for (let j = 1; j < samples.source.length; j++) {
6393
+ if (options.polygonEdgeProvenance) {
6394
+ if (owner) {
6395
+ const [pr, pe] = owner;
6396
+ edges.push(composePolygonEdge(preparedEdges[pr][pe], prepared[pr][pe], prepared[pr][pe + 1], source[source.length - 1], samples.source[j]));
6397
+ } else
6398
+ edges.push({
6399
+ kind: generated(ring[i - 1], ring[i]),
6400
+ ring: 4294967295,
6401
+ segment: 4294967295,
6402
+ t0: 0,
6403
+ t1: 0
6404
+ });
6405
+ }
6406
+ source.push(samples.source[j]);
6407
+ xy.push(samples.xy[j]);
6408
+ }
6409
+ }
6410
+ if (source.length && source[source.length - 1].some((v, k2) => v !== source[0][k2])) {
6411
+ if (angle(geographic(source[source.length - 1]), geographic(source[0])) > 1e-10)
6412
+ throw new Error("Unclosed Elastic polygon source constraint");
6413
+ if (options.polygonEdgeProvenance)
6414
+ edges.push({
6415
+ kind: "continuous-chart-join",
6416
+ ring: 4294967295,
6417
+ segment: 4294967295,
6418
+ t0: 0,
6419
+ t1: 0
6420
+ });
6421
+ source.push([...source[0]]);
6422
+ xy.push([...xy[0]]);
6423
+ }
6424
+ coordinates2.push(xy);
6425
+ sourceCoordinates.push(source);
6426
+ edgeProvenance.push(edges);
6427
+ });
5554
6428
  out.push({
5555
6429
  regionId: c.region.id,
5556
- coordinates: samples.map((s) => s.xy),
5557
- sourceCoordinates: samples.map((s) => s.source)
6430
+ coordinates: coordinates2,
6431
+ sourceCoordinates,
6432
+ ...options.polygonEdgeProvenance ? { edgeProvenance } : {}
5558
6433
  });
5559
6434
  }
5560
6435
  }
@@ -5608,7 +6483,7 @@ function wrap3(lon) {
5608
6483
  return x > 180 ? x - 360 : x < -180 ? x + 360 : x;
5609
6484
  }
5610
6485
  function snapPolygon(rings) {
5611
- return rings.map((ring) => ring.map((p) => p.map((v) => Math.round(v * 1e10) / 1e10)));
6486
+ return rings.map((r) => r.map((p) => p.map((v) => Math.round(v * 1e10) / 1e10)));
5612
6487
  }
5613
6488
 
5614
6489
  // dist/discontinuous/petroff.js
@@ -6844,17 +7719,65 @@ var MercatorMap = class extends DiscontinuousMap {
6844
7719
  continue;
6845
7720
  }
6846
7721
  const at = (t) => [a[0] + (b[0] - a[0]) * t, a[1] + dy * t];
6847
- const start = at(t0), end = at(t1);
7722
+ const endpoint = (t) => {
7723
+ if (t === 0 || t === 1)
7724
+ return { xy: at(t), source: part.sourceCoordinates[i - 1 + t], t };
7725
+ const va = geographic(part.sourceCoordinates[i - 1]), vb = geographic(part.sourceCoordinates[i]), arc = angle(va, vb);
7726
+ const sourceAt = (t2) => {
7727
+ if (!arc)
7728
+ return part.sourceCoordinates[i - 1];
7729
+ const a2 = Math.sin((1 - t2) * arc) / Math.sin(arc), b2 = Math.sin(t2 * arc) / Math.sin(arc);
7730
+ return coordinates(unit(va.map((v, k2) => a2 * v + b2 * vb[k2])));
7731
+ };
7732
+ const latitude = Math.sign(at(t)[1]) * this.limit;
7733
+ let lo = 0, hi = 1;
7734
+ for (let j = 0; j < 52; j++) {
7735
+ const mid = (lo + hi) / 2;
7736
+ if (sourceAt(mid)[1] < latitude === part.sourceCoordinates[i - 1][1] < latitude)
7737
+ lo = mid;
7738
+ else
7739
+ hi = mid;
7740
+ }
7741
+ const f = (lo + hi) / 2, source = sourceAt(f);
7742
+ const xy = super.projectPoint(source, part.regionId).position;
7743
+ return { xy, source, t: f };
7744
+ };
7745
+ const start = endpoint(t0), end = endpoint(t1);
7746
+ const append2 = (point, index) => {
7747
+ current.coordinates.push(point.xy);
7748
+ current.sourceCoordinates.push(point.source);
7749
+ const source = part.provenance, target = current.provenance;
7750
+ if (!source || !target)
7751
+ return;
7752
+ const segment = source.segments[i];
7753
+ const from = source.segments[i - 1] === segment ? source.parameters[i - 1] : 0;
7754
+ target.segments.push(point.t === 0 || point.t === 1 ? source.segments[index] : segment);
7755
+ target.parameters.push(point.t === 0 || point.t === 1 ? source.parameters[index] : from + point.t * (source.parameters[i] - from));
7756
+ target.vertices.push(point.t === 0 || point.t === 1 ? source.vertices[index] : 4294967295);
7757
+ if (options == null ? void 0 : options.pathDistances)
7758
+ target.angularDistances.push(source.angularDistances[i - 1] + point.t * (source.angularDistances[i] - source.angularDistances[i - 1]));
7759
+ };
6848
7760
  if (!current || t0 > 0) {
6849
7761
  current = {
6850
7762
  regionId: part.regionId,
6851
- coordinates: [start],
6852
- sourceCoordinates: [this.source(start)]
7763
+ coordinates: [],
7764
+ sourceCoordinates: [],
7765
+ ...part.provenance ? {
7766
+ provenance: {
7767
+ segments: [],
7768
+ parameters: [],
7769
+ vertices: [],
7770
+ angularDistances: [],
7771
+ ...(options == null ? void 0 : options.pathBoundaries) ? { startReason: t0 > 0 ? "coverage-boundary" : part.provenance.startReason } : {}
7772
+ }
7773
+ } : {}
6853
7774
  };
6854
7775
  result.push(current);
7776
+ append2(start, i - 1);
6855
7777
  }
6856
- current.coordinates.push(end);
6857
- current.sourceCoordinates.push(this.source(end));
7778
+ append2(end, i);
7779
+ if (current.provenance && (options == null ? void 0 : options.pathBoundaries))
7780
+ current.provenance.endReason = t1 < 1 ? "coverage-boundary" : part.provenance.endReason;
6858
7781
  if (t1 < 1)
6859
7782
  current = void 0;
6860
7783
  }
@@ -6872,11 +7795,60 @@ var MercatorMap = class extends DiscontinuousMap {
6872
7795
  [-x, -y]
6873
7796
  ]
6874
7797
  ];
6875
- return super.projectPolygon(rings, options).flatMap((part) => import_polygon_clipping5.default.intersection(part.coordinates, box).map((coordinates2) => ({
6876
- regionId: part.regionId,
6877
- coordinates: coordinates2,
6878
- sourceCoordinates: coordinates2.map((ring) => ring.map((p) => this.source(p)))
6879
- })));
7798
+ return super.projectPolygon(rings, options).flatMap((part) => import_polygon_clipping5.default.intersection(part.coordinates, box).map((polygon) => {
7799
+ const traced = tracePolygonConstraints(part.coordinates, polygon, 1e-10, part.edgeProvenance);
7800
+ const sources = traced.rings.map((r) => r.map((p) => this.source(p)));
7801
+ traced.owners.forEach((owners, r) => owners.forEach((owner, i) => {
7802
+ if (!owner)
7803
+ return;
7804
+ const [parentRing, parentEdge] = owner, a = part.sourceCoordinates[parentRing][parentEdge], b = part.sourceCoordinates[parentRing][parentEdge + 1], at = sourceArc(a, b);
7805
+ for (const j of [i, i + 1]) {
7806
+ const q = traced.rings[r][j], first = part.coordinates[parentRing][parentEdge], last = part.coordinates[parentRing][parentEdge + 1];
7807
+ if (Math.hypot(q[0] - first[0], q[1] - first[1]) < 1e-10 * this.radius)
7808
+ sources[r][j] = [...a];
7809
+ else if (Math.hypot(q[0] - last[0], q[1] - last[1]) < 1e-10 * this.radius)
7810
+ sources[r][j] = [...b];
7811
+ else {
7812
+ const latitude = Math.sign(q[1]) * this.limit;
7813
+ let lo = 0, hi = 1;
7814
+ if (a[1] < latitude === b[1] < latitude)
7815
+ throw new Error("Unresolved Mercator source constraint intersection");
7816
+ for (let k2 = 0; k2 < 52; k2++) {
7817
+ const mid = (lo + hi) / 2;
7818
+ if (at(mid)[1] < latitude === a[1] < latitude)
7819
+ lo = mid;
7820
+ else
7821
+ hi = mid;
7822
+ }
7823
+ sources[r][j] = at((lo + hi) / 2);
7824
+ }
7825
+ }
7826
+ }));
7827
+ for (const r of sources)
7828
+ r[r.length - 1] = [...r[0]];
7829
+ return {
7830
+ regionId: part.regionId,
7831
+ sourceCoordinates: sources,
7832
+ coordinates: sources.map((r) => r.map((p) => super.projectPoint(p, part.regionId).position)),
7833
+ ...(options == null ? void 0 : options.polygonEdgeProvenance) ? {
7834
+ edgeProvenance: traced.owners.map((owners, r) => owners.map((owner, i) => {
7835
+ if (owner) {
7836
+ const [pr, pe] = owner;
7837
+ return composePolygonEdge(part.edgeProvenance[pr][pe], part.sourceCoordinates[pr][pe], part.sourceCoordinates[pr][pe + 1], sources[r][i], sources[r][i + 1]);
7838
+ }
7839
+ if (!sources[r].slice(i, i + 2).every((p) => Math.abs(Math.abs(p[1]) - this.limit) < 1e-7))
7840
+ throw new Error("Unknown Mercator clipping constraint");
7841
+ return {
7842
+ kind: "coverage-boundary",
7843
+ ring: 4294967295,
7844
+ segment: 4294967295,
7845
+ t0: 0,
7846
+ t1: 0
7847
+ };
7848
+ }))
7849
+ } : {}
7850
+ };
7851
+ }));
6880
7852
  }
6881
7853
  };
6882
7854