@diagrammo/dgmo 0.19.0 → 0.20.1

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 (48) hide show
  1. package/dist/advanced.cjs +948 -321
  2. package/dist/advanced.d.cts +148 -54
  3. package/dist/advanced.d.ts +148 -54
  4. package/dist/advanced.js +949 -321
  5. package/dist/auto.cjs +930 -317
  6. package/dist/auto.js +117 -117
  7. package/dist/auto.mjs +934 -318
  8. package/dist/cli.cjs +160 -160
  9. package/dist/index.cjs +929 -316
  10. package/dist/index.js +933 -317
  11. package/dist/internal.cjs +948 -321
  12. package/dist/internal.d.cts +148 -54
  13. package/dist/internal.d.ts +148 -54
  14. package/dist/internal.js +949 -321
  15. package/dist/map-data/PROVENANCE.json +1 -1
  16. package/dist/map-data/lakes.json +1 -0
  17. package/dist/map-data/na-lakes.json +1 -0
  18. package/dist/map-data/na-land.json +1 -0
  19. package/dist/map-data/rivers.json +1 -0
  20. package/docs/language-reference.md +12 -7
  21. package/gallery/fixtures/map-region-scope.dgmo +15 -0
  22. package/package.json +4 -4
  23. package/src/advanced.ts +7 -6
  24. package/src/c4/parser.ts +6 -6
  25. package/src/completion.ts +6 -2
  26. package/src/echarts.ts +1 -1
  27. package/src/infra/parser.ts +10 -10
  28. package/src/journey-map/parser.ts +1 -1
  29. package/src/label-layout.ts +36 -0
  30. package/src/map/data/PROVENANCE.json +1 -1
  31. package/src/map/data/README.md +2 -0
  32. package/src/map/data/lakes.json +1 -0
  33. package/src/map/data/na-lakes.json +1 -0
  34. package/src/map/data/na-land.json +1 -0
  35. package/src/map/data/rivers.json +1 -0
  36. package/src/map/layout.ts +1022 -205
  37. package/src/map/load-data.ts +73 -17
  38. package/src/map/parser.ts +22 -13
  39. package/src/map/renderer.ts +200 -219
  40. package/src/map/resolved-types.ts +18 -1
  41. package/src/map/resolver.ts +79 -7
  42. package/src/map/types.ts +4 -0
  43. package/src/mindmap/parser.ts +1 -1
  44. package/src/sitemap/parser.ts +1 -1
  45. package/src/utils/legend-d3.ts +42 -0
  46. package/src/utils/legend-layout.ts +83 -3
  47. package/src/utils/legend-svg.ts +1 -8
  48. package/src/utils/legend-types.ts +44 -1
package/dist/index.cjs CHANGED
@@ -53,6 +53,33 @@ function rectCircleOverlap(rect, circle) {
53
53
  const dy = nearestY - circle.cy;
54
54
  return dx * dx + dy * dy < circle.r * circle.r;
55
55
  }
56
+ function segmentRectOverlap(x0, y0, x1, y1, rect) {
57
+ const dx = x1 - x0;
58
+ const dy = y1 - y0;
59
+ let t0 = 0;
60
+ let t1 = 1;
61
+ const edges = [
62
+ [-dx, x0 - rect.x],
63
+ [dx, rect.x + rect.w - x0],
64
+ [-dy, y0 - rect.y],
65
+ [dy, rect.y + rect.h - y0]
66
+ ];
67
+ for (const [p, q] of edges) {
68
+ if (p === 0) {
69
+ if (q < 0) return false;
70
+ } else {
71
+ const t = q / p;
72
+ if (p < 0) {
73
+ if (t > t1) return false;
74
+ if (t > t0) t0 = t;
75
+ } else {
76
+ if (t < t0) return false;
77
+ if (t < t1) t1 = t;
78
+ }
79
+ }
80
+ }
81
+ return true;
82
+ }
56
83
  function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius, fontSize) {
57
84
  const labelHeight = fontSize + 4;
58
85
  const stepSize = labelHeight + 2;
@@ -3256,6 +3283,57 @@ var init_legend_constants = __esm({
3256
3283
  });
3257
3284
 
3258
3285
  // src/utils/legend-layout.ts
3286
+ function fmtRamp(n) {
3287
+ return Number.isInteger(n) ? String(n) : String(Math.round(n * 10) / 10);
3288
+ }
3289
+ function gradientCapsuleWidth(name, gradient) {
3290
+ const pw = pillWidth(name);
3291
+ const minW = measureLegendText(fmtRamp(gradient.min), LEGEND_ENTRY_FONT_SIZE);
3292
+ const maxW = measureLegendText(fmtRamp(gradient.max), LEGEND_ENTRY_FONT_SIZE);
3293
+ return LEGEND_CAPSULE_PAD + pw + 4 + minW + RAMP_LABEL_GAP + RAMP_LEGEND_W + RAMP_LABEL_GAP + maxW + LEGEND_CAPSULE_PAD;
3294
+ }
3295
+ function buildGradientCapsuleLayout(group, gradient) {
3296
+ const pw = pillWidth(group.name);
3297
+ const minText = fmtRamp(gradient.min);
3298
+ const maxText = fmtRamp(gradient.max);
3299
+ const minW = measureLegendText(minText, LEGEND_ENTRY_FONT_SIZE);
3300
+ const gx = LEGEND_CAPSULE_PAD + pw + 4;
3301
+ const minX = gx;
3302
+ const rampX = gx + minW + RAMP_LABEL_GAP;
3303
+ const maxX = rampX + RAMP_LEGEND_W + RAMP_LABEL_GAP;
3304
+ const width = gradientCapsuleWidth(group.name, gradient);
3305
+ return {
3306
+ groupName: group.name,
3307
+ x: 0,
3308
+ y: 0,
3309
+ width,
3310
+ height: LEGEND_HEIGHT,
3311
+ pill: {
3312
+ groupName: group.name,
3313
+ x: LEGEND_CAPSULE_PAD,
3314
+ y: LEGEND_CAPSULE_PAD,
3315
+ width: pw,
3316
+ height: LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2,
3317
+ isActive: true
3318
+ },
3319
+ entries: [],
3320
+ gradient: {
3321
+ rampX,
3322
+ rampY: (LEGEND_HEIGHT - RAMP_LEGEND_H) / 2,
3323
+ rampW: RAMP_LEGEND_W,
3324
+ rampH: RAMP_LEGEND_H,
3325
+ min: gradient.min,
3326
+ max: gradient.max,
3327
+ minText,
3328
+ minX,
3329
+ maxText,
3330
+ maxX,
3331
+ textY: LEGEND_HEIGHT / 2,
3332
+ hue: gradient.hue,
3333
+ base: gradient.base
3334
+ }
3335
+ };
3336
+ }
3259
3337
  function pillWidth(name) {
3260
3338
  return measureLegendText(name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
3261
3339
  }
@@ -3398,7 +3476,7 @@ function computeLegendLayout(config, state, containerWidth) {
3398
3476
  };
3399
3477
  }
3400
3478
  const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
3401
- const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0);
3479
+ const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
3402
3480
  if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
3403
3481
  return {
3404
3482
  height: 0,
@@ -3477,7 +3555,7 @@ function computeLegendLayout(config, state, containerWidth) {
3477
3555
  groupAvailW,
3478
3556
  config.capsulePillAddonWidth ?? 0
3479
3557
  );
3480
- } else if (!activeGroupName) {
3558
+ } else if (!activeGroupName || config.showInactivePills) {
3481
3559
  const pw = pillWidth(g.name);
3482
3560
  pills.push({
3483
3561
  groupName: g.name,
@@ -3515,6 +3593,7 @@ function computeLegendLayout(config, state, containerWidth) {
3515
3593
  };
3516
3594
  }
3517
3595
  function buildCapsuleLayout(group, containerWidth, addonWidth = 0) {
3596
+ if (group.gradient) return buildGradientCapsuleLayout(group, group.gradient);
3518
3597
  const pw = pillWidth(group.name);
3519
3598
  const info = capsuleWidth(
3520
3599
  group.name,
@@ -3685,7 +3764,7 @@ function getMaxLegendReservedHeight(config, containerWidth) {
3685
3764
  }
3686
3765
  return max;
3687
3766
  }
3688
- var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP;
3767
+ var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP, RAMP_LEGEND_W, RAMP_LEGEND_H, RAMP_LABEL_GAP;
3689
3768
  var init_legend_layout = __esm({
3690
3769
  "src/utils/legend-layout.ts"() {
3691
3770
  "use strict";
@@ -3694,6 +3773,9 @@ var init_legend_layout = __esm({
3694
3773
  CONTROL_FONT_SIZE = 11;
3695
3774
  CONTROL_ICON_GAP = 4;
3696
3775
  CONTROL_GAP = 8;
3776
+ RAMP_LEGEND_W = 80;
3777
+ RAMP_LEGEND_H = 8;
3778
+ RAMP_LABEL_GAP = 6;
3697
3779
  }
3698
3780
  });
3699
3781
 
@@ -3781,6 +3863,16 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, _isDark, c
3781
3863
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", palette.bg);
3782
3864
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", "none").attr("stroke", pillBorder).attr("stroke-width", 0.75);
3783
3865
  g.append("text").attr("x", pill.x + pill.width / 2).attr("y", LEGEND_HEIGHT / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.text).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(capsule.groupName);
3866
+ if (capsule.gradient) {
3867
+ const gr = capsule.gradient;
3868
+ const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
3869
+ const def = g.append("defs").append("linearGradient").attr("id", gradId);
3870
+ def.append("stop").attr("offset", "0%").attr("stop-color", mix(gr.hue, gr.base, 15));
3871
+ def.append("stop").attr("offset", "100%").attr("stop-color", gr.hue);
3872
+ g.append("text").attr("x", gr.minX).attr("y", gr.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.minText);
3873
+ g.append("rect").attr("class", "dgmo-legend-gradient-ramp").attr("data-ramp-min", gr.min).attr("data-ramp-max", gr.max).attr("x", gr.rampX).attr("y", gr.rampY).attr("width", gr.rampW).attr("height", gr.rampH).attr("rx", 2).attr("fill", `url(#${gradId})`);
3874
+ g.append("text").attr("x", gr.maxX).attr("y", gr.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.maxText);
3875
+ }
3784
3876
  for (const entry of capsule.entries) {
3785
3877
  const entryG = g.append("g").attr("data-legend-entry", entry.value.toLowerCase()).attr("data-series-name", entry.value).style("cursor", "pointer");
3786
3878
  entryG.append("circle").attr("cx", entry.dotCx).attr("cy", entry.dotCy).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
@@ -11254,23 +11346,22 @@ function parseC4(content, palette) {
11254
11346
  }
11255
11347
  }
11256
11348
  const parent = findParentElement(indent, stack);
11257
- if (parent) {
11258
- const descResult = tryStripDescriptionKeyword(trimmed);
11349
+ const descResult = tryStripDescriptionKeyword(trimmed);
11350
+ if (parent && descResult.isKeyword) {
11259
11351
  if (descResult.needsColon) {
11260
11352
  pushError(
11261
11353
  lineNumber,
11262
- `Use "description: ${descResult.text}" \u2014 colon is required.`,
11354
+ `Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`,
11263
11355
  "warning"
11264
11356
  );
11265
11357
  }
11266
- const descText = descResult.isKeyword ? descResult.text : trimmed;
11267
11358
  let desc = elementDescription.get(parent.element);
11268
11359
  if (!desc) {
11269
11360
  desc = [];
11270
11361
  elementDescription.set(parent.element, desc);
11271
11362
  parent.element.description = desc;
11272
11363
  }
11273
- desc.push(descText);
11364
+ desc.push(descResult.text);
11274
11365
  } else {
11275
11366
  pushError(lineNumber, `Unexpected content: "${trimmed}"`);
11276
11367
  }
@@ -11737,7 +11828,7 @@ function parseSitemap(content, palette) {
11737
11828
  if (descResult.needsColon) {
11738
11829
  pushWarning(
11739
11830
  lineNumber,
11740
- `Use "description: ${descResult.text}" \u2014 colon is required.`
11831
+ `Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
11741
11832
  );
11742
11833
  }
11743
11834
  const parent = findParentNode(indent, indentStack);
@@ -12565,23 +12656,22 @@ function parseInfra(content) {
12565
12656
  }
12566
12657
  }
12567
12658
  const descResult = tryStripDescriptionKeyword(trimmed);
12568
- if (descResult.isKeyword && currentNode.isEdge) {
12569
- continue;
12570
- }
12571
- if (!currentNode.isEdge) {
12659
+ if (descResult.isKeyword) {
12660
+ if (currentNode.isEdge) {
12661
+ continue;
12662
+ }
12572
12663
  if (descResult.needsColon) {
12573
12664
  warn(
12574
12665
  lineNumber,
12575
- `Use "description: ${descResult.text}" \u2014 colon is required.`
12666
+ `Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
12576
12667
  );
12577
12668
  }
12578
- const descText = descResult.isKeyword ? descResult.text : trimmed;
12579
- pushDescription(currentNode, descText);
12669
+ pushDescription(currentNode, descResult.text);
12580
12670
  continue;
12581
12671
  }
12582
12672
  warn(
12583
12673
  lineNumber,
12584
- `Unexpected line inside component '${currentNode.label}'. Expected a property (key: value), connection (-> Target), or description text.`
12674
+ `Unexpected line inside component '${currentNode.label}'. Expected a property (key: value), connection (-> Target), or a description (description: text).`
12585
12675
  );
12586
12676
  continue;
12587
12677
  }
@@ -15718,9 +15808,6 @@ function parseMap(content) {
15718
15808
  const pushWarning = (line12, message) => {
15719
15809
  diagnostics.push(makeDgmoError(line12, message, "warning"));
15720
15810
  };
15721
- const pushInfo = (line12, message) => {
15722
- diagnostics.push(makeDgmoError(line12, message, "warning"));
15723
- };
15724
15811
  const lines = content.split("\n");
15725
15812
  let firstIdx = 0;
15726
15813
  while (firstIdx < lines.length) {
@@ -15850,10 +15937,15 @@ function parseMap(content) {
15850
15937
  break;
15851
15938
  case "projection":
15852
15939
  dup(d.projection);
15853
- if (value && !["natural-earth", "albers-usa", "mercator"].includes(value))
15940
+ if (value && ![
15941
+ "equirectangular",
15942
+ "natural-earth",
15943
+ "albers-usa",
15944
+ "mercator"
15945
+ ].includes(value))
15854
15946
  pushWarning(
15855
15947
  line12,
15856
- `Unknown projection "${value}" (expected natural-earth | albers-usa | mercator).`
15948
+ `Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
15857
15949
  );
15858
15950
  d.projection = value;
15859
15951
  break;
@@ -15992,17 +16084,21 @@ function parseMap(content) {
15992
16084
  scoreNum = void 0;
15993
16085
  }
15994
16086
  }
15995
- if (scoreNum !== void 0 && Object.keys(tags).length)
15996
- pushInfo(
15997
- line12,
15998
- "A region has both `score:` and a tag value \u2014 v1 renders only the score (bivariate is a future seam)."
15999
- );
16087
+ let regionName = split.name;
16088
+ let regionScope;
16089
+ const rToks = regionName.split(/\s+/);
16090
+ const rLast = rToks[rToks.length - 1];
16091
+ if (rToks.length > 1 && SCOPE_RE.test(rLast)) {
16092
+ regionName = rToks.slice(0, -1).join(" ");
16093
+ regionScope = rLast;
16094
+ }
16000
16095
  const region = {
16001
- name: split.name,
16096
+ name: regionName,
16002
16097
  tags,
16003
16098
  meta,
16004
16099
  lineNumber: line12
16005
16100
  };
16101
+ if (regionScope !== void 0) region.scope = regionScope;
16006
16102
  if (scoreNum !== void 0) region.score = scoreNum;
16007
16103
  regions.push(region);
16008
16104
  }
@@ -17126,7 +17222,7 @@ function parseMindmap(content, palette) {
17126
17222
  if (descResult.needsColon) {
17127
17223
  pushWarning(
17128
17224
  lineNumber,
17129
- `Use "description: ${descResult.text}" \u2014 colon is required.`
17225
+ `Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
17130
17226
  );
17131
17227
  }
17132
17228
  const parent = findMetadataParent2(indent, indentStack);
@@ -18914,7 +19010,7 @@ function parseJourneyMap(content, palette) {
18914
19010
  if (descResult.needsColon) {
18915
19011
  warn(
18916
19012
  lineNumber,
18917
- `Use "description: ${descResult.text}" \u2014 colon is required.`
19013
+ `Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
18918
19014
  );
18919
19015
  }
18920
19016
  currentStep.description = descResult.text;
@@ -45503,7 +45599,9 @@ function resolveMap(parsed, data) {
45503
45599
  const usScoped = parsed.directives.region === "us-states" || parsed.directives.defaultCountry?.toUpperCase() === "US" || parsed.regions.some((r) => {
45504
45600
  const f = fold(r.name);
45505
45601
  return usStateIndex.has(f) && !countryIndex.has(f);
45506
- }) || parsed.pois.some(
45602
+ }) || parsed.regions.some(
45603
+ (r) => r.scope === "US" || r.scope?.startsWith("US-")
45604
+ ) || parsed.pois.some(
45507
45605
  (p) => p.pos.kind === "name" && p.pos.scope?.startsWith("US-")
45508
45606
  );
45509
45607
  const regions = [];
@@ -45515,7 +45613,30 @@ function resolveMap(parsed, data) {
45515
45613
  const inCountry = countryIndex.get(f) ?? (REGION_ALIASES[f] ? countryIndex.get(REGION_ALIASES[f]) : void 0);
45516
45614
  const inState = usStateIndex.get(f);
45517
45615
  let chosen = null;
45518
- if (inCountry && inState) {
45616
+ const scope = r.scope;
45617
+ if (scope) {
45618
+ const wantsState = scope === "US" || scope.startsWith("US-");
45619
+ if (wantsState && inState) {
45620
+ if (scope.startsWith("US-") && inState.id !== scope) {
45621
+ err(
45622
+ r.lineNumber,
45623
+ `No subdivision "${r.name}" in scope ${scope} (it is ${inState.id}).`,
45624
+ "E_MAP_SCOPE_MISS"
45625
+ );
45626
+ continue;
45627
+ }
45628
+ chosen = { ...inState, layer: "us-state" };
45629
+ } else if (!wantsState && inCountry) {
45630
+ chosen = { ...inCountry, layer: "country" };
45631
+ } else {
45632
+ err(
45633
+ r.lineNumber,
45634
+ `No region "${r.name}" found in scope ${scope}.`,
45635
+ "E_MAP_SCOPE_MISS"
45636
+ );
45637
+ continue;
45638
+ }
45639
+ } else if (inCountry && inState) {
45519
45640
  if (usScoped) {
45520
45641
  chosen = { ...inState, layer: "us-state" };
45521
45642
  } else {
@@ -45523,7 +45644,7 @@ function resolveMap(parsed, data) {
45523
45644
  }
45524
45645
  warn(
45525
45646
  r.lineNumber,
45526
- `"${r.name}" is both a country and a US state \u2014 resolved as ${chosen.layer} (${chosen.id}).`,
45647
+ `"${r.name}" is both a country and a US state \u2014 resolved as ${chosen.layer} (${chosen.id}). Pin it with an ISO code (${inState.id} / ${inCountry.id}) or name + scope ("${r.name} US" / "${r.name} ${inCountry.id}").`,
45527
45648
  "W_MAP_REGION_AMBIGUOUS"
45528
45649
  );
45529
45650
  } else if (inState) {
@@ -45695,9 +45816,17 @@ function resolveMap(parsed, data) {
45695
45816
  };
45696
45817
  registerPoi(id, poi, p.lineNumber);
45697
45818
  }
45819
+ const declaredByName = /* @__PURE__ */ new Map();
45820
+ for (const p of pois) {
45821
+ const fn = p.name ? fold(p.name) : void 0;
45822
+ if (fn && fn !== p.id && !declaredByName.has(fn))
45823
+ declaredByName.set(fn, p.id);
45824
+ }
45698
45825
  const resolveEndpoint2 = (ref, line12) => {
45699
45826
  const f = fold(ref);
45700
45827
  if (registry.has(f)) return f;
45828
+ const aliased = declaredByName.get(f);
45829
+ if (aliased) return aliased;
45701
45830
  const got = lookupName(ref, void 0, line12, inferredCountry, true);
45702
45831
  if (got.kind !== "ok") return null;
45703
45832
  noteCountry(got.iso);
@@ -45777,23 +45906,29 @@ function resolveMap(parsed, data) {
45777
45906
  [-180, -85],
45778
45907
  [180, 85]
45779
45908
  ];
45780
- const extent2 = unioned ? pad(unioned, PAD_FRACTION) : DEFAULT_EXTENT;
45909
+ let extent2 = unioned ? pad(unioned, PAD_FRACTION) : DEFAULT_EXTENT;
45781
45910
  const lonSpan = extent2[1][0] - extent2[0][0];
45782
45911
  const latSpan = extent2[1][1] - extent2[0][1];
45783
45912
  const span = Math.max(lonSpan, latSpan);
45784
45913
  const usDominant = (inferredCountry === "US" || subdivisions.includes("us-states")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
45785
45914
  let projection;
45786
45915
  const override = parsed.directives.projection;
45787
- if (override === "natural-earth" || override === "albers-usa" || override === "mercator") {
45916
+ if (override === "equirectangular" || override === "natural-earth" || override === "albers-usa" || override === "mercator") {
45788
45917
  projection = override;
45789
45918
  } else if (usDominant) {
45790
45919
  projection = "albers-usa";
45791
45920
  } else if (span > WORLD_SPAN) {
45792
- projection = "natural-earth";
45921
+ projection = "equirectangular";
45793
45922
  } else if (span < MERCATOR_MAX_SPAN) {
45794
45923
  projection = "mercator";
45795
45924
  } else {
45796
- projection = "natural-earth";
45925
+ projection = "equirectangular";
45926
+ }
45927
+ if (lonSpan >= 180) {
45928
+ extent2 = [
45929
+ [-180, Math.min(extent2[0][1], WORLD_LAT_SOUTH)],
45930
+ [180, Math.max(extent2[1][1], WORLD_LAT_NORTH)]
45931
+ ];
45797
45932
  }
45798
45933
  result.regions = regions;
45799
45934
  result.pois = pois;
@@ -45841,7 +45976,7 @@ function firstError(diags) {
45841
45976
  const e = diags.find((d) => d.severity === "error");
45842
45977
  return e ? formatDgmoError(e) : null;
45843
45978
  }
45844
- var WORLD_SPAN, MERCATOR_MAX_SPAN, PAD_FRACTION, REGION_ALIASES;
45979
+ var WORLD_SPAN, MERCATOR_MAX_SPAN, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES;
45845
45980
  var init_resolver2 = __esm({
45846
45981
  "src/map/resolver.ts"() {
45847
45982
  "use strict";
@@ -45850,6 +45985,8 @@ var init_resolver2 = __esm({
45850
45985
  WORLD_SPAN = 90;
45851
45986
  MERCATOR_MAX_SPAN = 25;
45852
45987
  PAD_FRACTION = 0.05;
45988
+ WORLD_LAT_SOUTH = -58;
45989
+ WORLD_LAT_NORTH = 78;
45853
45990
  REGION_ALIASES = {
45854
45991
  // Common everyday names → the Natural-Earth display name actually shipped.
45855
45992
  "united states": "united states of america",
@@ -45879,14 +46016,22 @@ var load_data_exports = {};
45879
46016
  __export(load_data_exports, {
45880
46017
  loadMapData: () => loadMapData
45881
46018
  });
45882
- async function readJson(dir, name) {
45883
- return JSON.parse(await (0, import_promises.readFile)((0, import_node_path.resolve)(dir, name), "utf8"));
46019
+ async function loadNodeBuiltins() {
46020
+ const [{ readFile }, { fileURLToPath }, { dirname, resolve }] = await Promise.all([
46021
+ import("fs/promises"),
46022
+ import("url"),
46023
+ import("path")
46024
+ ]);
46025
+ return { readFile, fileURLToPath, dirname, resolve };
46026
+ }
46027
+ async function readJson(nb, dir, name) {
46028
+ return JSON.parse(await nb.readFile(nb.resolve(dir, name), "utf8"));
45884
46029
  }
45885
- async function firstExistingDir(baseDir) {
46030
+ async function firstExistingDir(nb, baseDir) {
45886
46031
  for (const rel of CANDIDATE_DIRS) {
45887
- const dir = (0, import_node_path.resolve)(baseDir, rel);
46032
+ const dir = nb.resolve(baseDir, rel);
45888
46033
  try {
45889
- await (0, import_promises.readFile)((0, import_node_path.resolve)(dir, FILES.gazetteer), "utf8");
46034
+ await nb.readFile(nb.resolve(dir, FILES.gazetteer), "utf8");
45890
46035
  return dir;
45891
46036
  } catch {
45892
46037
  }
@@ -45902,10 +46047,10 @@ function validate(data) {
45902
46047
  }
45903
46048
  return data;
45904
46049
  }
45905
- function moduleBaseDir() {
46050
+ function moduleBaseDir(nb) {
45906
46051
  try {
45907
46052
  const url = import_meta.url;
45908
- if (url) return (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(url));
46053
+ if (url) return nb.dirname(nb.fileURLToPath(url));
45909
46054
  } catch {
45910
46055
  }
45911
46056
  if (typeof __dirname !== "undefined") return __dirname;
@@ -45913,32 +46058,57 @@ function moduleBaseDir() {
45913
46058
  }
45914
46059
  function loadMapData() {
45915
46060
  cache ??= (async () => {
45916
- const dir = await firstExistingDir(moduleBaseDir());
45917
- const [worldCoarse, worldDetail, usStates, gazetteer] = await Promise.all([
45918
- readJson(dir, FILES.worldCoarse),
45919
- readJson(dir, FILES.worldDetail),
45920
- readJson(dir, FILES.usStates),
45921
- readJson(dir, FILES.gazetteer)
46061
+ const nb = await loadNodeBuiltins();
46062
+ const dir = await firstExistingDir(nb, moduleBaseDir(nb));
46063
+ const [
46064
+ worldCoarse,
46065
+ worldDetail,
46066
+ usStates,
46067
+ lakes,
46068
+ rivers,
46069
+ naLand,
46070
+ naLakes,
46071
+ gazetteer
46072
+ ] = await Promise.all([
46073
+ readJson(nb, dir, FILES.worldCoarse),
46074
+ readJson(nb, dir, FILES.worldDetail),
46075
+ readJson(nb, dir, FILES.usStates),
46076
+ // Lakes/rivers/NA assets are optional — older bundles may predate them.
46077
+ readJson(nb, dir, FILES.lakes).catch(() => void 0),
46078
+ readJson(nb, dir, FILES.rivers).catch(() => void 0),
46079
+ readJson(nb, dir, FILES.naLand).catch(() => void 0),
46080
+ readJson(nb, dir, FILES.naLakes).catch(() => void 0),
46081
+ readJson(nb, dir, FILES.gazetteer)
45922
46082
  ]);
45923
- return validate({ worldCoarse, worldDetail, usStates, gazetteer });
46083
+ return validate({
46084
+ worldCoarse,
46085
+ worldDetail,
46086
+ usStates,
46087
+ gazetteer,
46088
+ ...lakes && { lakes },
46089
+ ...rivers && { rivers },
46090
+ ...naLand && { naLand },
46091
+ ...naLakes && { naLakes }
46092
+ });
45924
46093
  })().catch((e) => {
45925
46094
  cache = void 0;
45926
46095
  throw e;
45927
46096
  });
45928
46097
  return cache;
45929
46098
  }
45930
- var import_promises, import_node_url, import_node_path, import_meta, FILES, CANDIDATE_DIRS, cache;
46099
+ var import_meta, FILES, CANDIDATE_DIRS, cache;
45931
46100
  var init_load_data = __esm({
45932
46101
  "src/map/load-data.ts"() {
45933
46102
  "use strict";
45934
- import_promises = require("fs/promises");
45935
- import_node_url = require("url");
45936
- import_node_path = require("path");
45937
46103
  import_meta = {};
45938
46104
  FILES = {
45939
46105
  worldCoarse: "world-coarse.json",
45940
46106
  worldDetail: "world-detail.json",
45941
46107
  usStates: "us-states.json",
46108
+ lakes: "lakes.json",
46109
+ rivers: "rivers.json",
46110
+ naLand: "na-land.json",
46111
+ naLakes: "na-lakes.json",
45942
46112
  gazetteer: "gazetteer.json"
45943
46113
  };
45944
46114
  CANDIDATE_DIRS = [
@@ -45966,36 +46136,67 @@ function decodeLayer(topo) {
45966
46136
  function projectionFor(family) {
45967
46137
  switch (family) {
45968
46138
  case "albers-usa":
45969
- return (0, import_d3_geo2.geoAlbersUsa)();
46139
+ return usConusProjection();
45970
46140
  case "mercator":
45971
46141
  return (0, import_d3_geo2.geoMercator)();
45972
46142
  case "natural-earth":
45973
- default:
45974
46143
  return (0, import_d3_geo2.geoNaturalEarth1)();
46144
+ case "equirectangular":
46145
+ default:
46146
+ return (0, import_d3_geo2.geoEquirectangular)();
45975
46147
  }
45976
46148
  }
46149
+ function mapBackgroundColor(palette) {
46150
+ return mix(palette.colors.blue, palette.bg, WATER_TINT);
46151
+ }
45977
46152
  function layoutMap(resolved, data, size, opts) {
45978
46153
  const { palette, isDark } = opts;
45979
46154
  const { width, height } = size;
45980
- const worldTopo = resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
46155
+ const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
46156
+ const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
46157
+ const worldTopo = usCrisp ? data.naLand : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
45981
46158
  const worldLayer = decodeLayer(worldTopo);
45982
- const usLayer = resolved.basemaps.subdivisions.includes("us-states") ? decodeLayer(data.usStates) : null;
45983
- const neutralFill = mix(palette.border, palette.bg, 32);
45984
- const regionStroke = mix(palette.border, palette.bg, 70);
46159
+ const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
46160
+ const landTint = isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT;
46161
+ const neutralFill = mix(palette.colors.green, palette.bg, landTint);
46162
+ const water = mapBackgroundColor(palette);
46163
+ const usContext = usLayer !== null;
46164
+ const foreignFill = mix(
46165
+ palette.colors.gray,
46166
+ palette.bg,
46167
+ isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
46168
+ );
46169
+ const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
45985
46170
  const scores = resolved.regions.filter((r) => r.score !== void 0).map((r) => r.score);
45986
46171
  const scaleOverride = resolved.directives.scale;
45987
46172
  const rampMin = scaleOverride ? scaleOverride.min : Math.min(...scores);
45988
46173
  const rampMax = scaleOverride ? scaleOverride.max : Math.max(...scores);
45989
- const rampHue = palette.primary;
46174
+ const rampHue = palette.colors.red;
45990
46175
  const hasRamp = scores.length > 0;
45991
- const activeGroup = resolveActiveTagGroup(
45992
- resolved.tagGroups,
45993
- resolved.directives.activeTag
45994
- );
46176
+ const SCORE_NAME = hasRamp ? resolved.directives.metric?.trim() || "Score" : null;
46177
+ const matchColorGroup = (v) => {
46178
+ const lv = v.trim().toLowerCase();
46179
+ if (lv === "none") return null;
46180
+ if (SCORE_NAME && (lv === "score" || lv === SCORE_NAME.toLowerCase()))
46181
+ return SCORE_NAME;
46182
+ const tg = resolved.tagGroups.find((g) => g.name.toLowerCase() === lv);
46183
+ return tg ? tg.name : v;
46184
+ };
46185
+ const override = opts.activeGroup;
46186
+ let activeGroup;
46187
+ if (override !== void 0) {
46188
+ activeGroup = override === null ? null : matchColorGroup(override);
46189
+ } else if (resolved.directives.activeTag !== void 0) {
46190
+ activeGroup = matchColorGroup(resolved.directives.activeTag);
46191
+ } else {
46192
+ activeGroup = SCORE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
46193
+ }
46194
+ const activeIsScore = SCORE_NAME !== null && activeGroup === SCORE_NAME;
46195
+ const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
45995
46196
  const fillForScore = (s) => {
45996
46197
  const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
45997
46198
  const pct = RAMP_FLOOR + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR);
45998
- return mix(rampHue, isDark ? palette.surface : palette.bg, pct);
46199
+ return mix(rampHue, rampBase, pct);
45999
46200
  };
46000
46201
  const tagFill = (tags, groupName) => {
46001
46202
  if (!groupName) return null;
@@ -46009,40 +46210,40 @@ function layoutMap(resolved, data, size, opts) {
46009
46210
  (e) => e.value.toLowerCase() === val.toLowerCase()
46010
46211
  );
46011
46212
  if (!entry?.color) return null;
46012
- return shapeFill(palette, entry.color, isDark);
46213
+ return mix(
46214
+ entry.color,
46215
+ palette.bg,
46216
+ isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT
46217
+ );
46218
+ };
46219
+ const regionFill = (r) => {
46220
+ if (activeIsScore) {
46221
+ return r.score !== void 0 ? fillForScore(r.score) : neutralFill;
46222
+ }
46223
+ return tagFill(r.tags, activeGroup) ?? neutralFill;
46013
46224
  };
46014
46225
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
46015
- const regionFeatures = [];
46016
- for (const r of resolved.regions) {
46017
- const f = r.layer === "us-state" ? usLayer?.get(r.iso) : worldLayer.get(r.iso);
46018
- if (f) regionFeatures.push(f);
46019
- }
46020
- const extentCorners = () => {
46226
+ const extentOutline = () => {
46021
46227
  const [[w, s], [e, n]] = resolved.extent;
46228
+ const N = 16;
46229
+ const coords = [];
46230
+ for (let i = 0; i <= N; i++) {
46231
+ const t = i / N;
46232
+ const lon = w + (e - w) * t;
46233
+ const lat = s + (n - s) * t;
46234
+ coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
46235
+ }
46022
46236
  return {
46023
46237
  type: "Feature",
46024
46238
  properties: {},
46025
- geometry: {
46026
- type: "MultiPoint",
46027
- coordinates: [
46028
- [w, s],
46029
- [e, s],
46030
- [e, n],
46031
- [w, n]
46032
- ]
46033
- }
46239
+ geometry: { type: "MultiPoint", coordinates: coords }
46034
46240
  };
46035
46241
  };
46036
46242
  let fitFeatures;
46037
- if (resolved.projection === "albers-usa") {
46038
- if (regionFeatures.length > 0) fitFeatures = regionFeatures;
46039
- else if (usLayer) fitFeatures = [...usLayer.values()];
46040
- else {
46041
- const us = worldLayer.get("US");
46042
- fitFeatures = us ? [us] : [...worldLayer.values()];
46043
- }
46243
+ if (resolved.projection === "albers-usa" && usLayer) {
46244
+ fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
46044
46245
  } else {
46045
- fitFeatures = [extentCorners()];
46246
+ fitFeatures = [extentOutline()];
46046
46247
  }
46047
46248
  const fitTarget = { type: "FeatureCollection", features: fitFeatures };
46048
46249
  const projection = projectionFor(resolved.projection);
@@ -46051,32 +46252,311 @@ function layoutMap(resolved, data, size, opts) {
46051
46252
  if (centerLon > 180) centerLon -= 360;
46052
46253
  projection.rotate([-centerLon, 0]);
46053
46254
  }
46054
- projection.fitExtent(
46255
+ const TITLE_GAP = 16;
46256
+ let topPad = FIT_PAD;
46257
+ if (resolved.title && resolved.pois.length > 0) {
46258
+ const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
46259
+ topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP);
46260
+ }
46261
+ const fitBox = [
46262
+ [FIT_PAD, topPad],
46055
46263
  [
46056
- [FIT_PAD, FIT_PAD],
46057
- [
46058
- Math.max(FIT_PAD + 1, width - FIT_PAD),
46059
- Math.max(FIT_PAD + 1, height - FIT_PAD)
46060
- ]
46061
- ],
46062
- fitTarget
46063
- );
46064
- const path = (0, import_d3_geo2.geoPath)(projection);
46065
- const project = (lon, lat) => projection([lon, lat]) ?? null;
46264
+ Math.max(FIT_PAD + 1, width - FIT_PAD),
46265
+ Math.max(topPad + 1, height - FIT_PAD)
46266
+ ]
46267
+ ];
46268
+ projection.fitExtent(fitBox, fitTarget);
46269
+ const fitGB = (0, import_d3_geo2.geoBounds)(fitTarget);
46270
+ const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
46271
+ let path;
46272
+ let project;
46273
+ if (fitIsGlobal) {
46274
+ const cb = (0, import_d3_geo2.geoPath)(projection).bounds(fitTarget);
46275
+ const bx0 = cb[0][0];
46276
+ const by0 = cb[0][1];
46277
+ const cw = cb[1][0] - bx0;
46278
+ const ch = cb[1][1] - by0;
46279
+ const ox = fitBox[0][0];
46280
+ const oy = fitBox[0][1];
46281
+ const sx = cw > 0 ? (fitBox[1][0] - ox) / cw : 1;
46282
+ const sy = ch > 0 ? (fitBox[1][1] - oy) / ch : 1;
46283
+ const stretch = (x, y) => [
46284
+ ox + (x - bx0) * sx,
46285
+ oy + (y - by0) * sy
46286
+ ];
46287
+ const baseProjection = projection;
46288
+ const tx = (0, import_d3_geo2.geoTransform)({
46289
+ point(x, y) {
46290
+ const [px, py] = stretch(x, y);
46291
+ this.stream.point(px, py);
46292
+ }
46293
+ });
46294
+ path = (0, import_d3_geo2.geoPath)({
46295
+ stream: (s) => baseProjection.stream(
46296
+ tx.stream(s)
46297
+ )
46298
+ });
46299
+ project = (lon, lat) => {
46300
+ const p = baseProjection([lon, lat]);
46301
+ return p ? stretch(p[0], p[1]) : null;
46302
+ };
46303
+ } else {
46304
+ path = (0, import_d3_geo2.geoPath)(projection);
46305
+ project = (lon, lat) => projection([lon, lat]) ?? null;
46306
+ }
46307
+ const insets = [];
46308
+ const insetRegions = [];
46309
+ const insetLabelSeeds = [];
46310
+ if (resolved.projection === "albers-usa" && usLayer) {
46311
+ const PAD = 8;
46312
+ const GAP = 12;
46313
+ const yB = height - FIT_PAD;
46314
+ const BW = 8;
46315
+ const coast = /* @__PURE__ */ new Map();
46316
+ const addPt = (lon, lat) => {
46317
+ const p = projection([lon, lat]);
46318
+ if (!p) return;
46319
+ const bi = Math.floor(p[0] / BW);
46320
+ const cur = coast.get(bi);
46321
+ if (cur === void 0 || p[1] > cur) coast.set(bi, p[1]);
46322
+ };
46323
+ const walk = (co) => {
46324
+ if (Array.isArray(co) && typeof co[0] === "number")
46325
+ addPt(co[0], co[1]);
46326
+ else if (Array.isArray(co)) for (const c of co) walk(c);
46327
+ };
46328
+ for (const [iso, f] of usLayer) {
46329
+ if (US_NON_CONUS.has(iso)) continue;
46330
+ walk(f.geometry.coordinates);
46331
+ }
46332
+ const at = (x) => {
46333
+ const bi = Math.floor(x / BW);
46334
+ let y = -Infinity;
46335
+ for (let k = bi - 1; k <= bi + 1; k++) {
46336
+ const v = coast.get(k);
46337
+ if (v !== void 0 && v > y) y = v;
46338
+ }
46339
+ return y;
46340
+ };
46341
+ const coastTop = (x0, xr) => {
46342
+ const n = 24;
46343
+ const pts = [];
46344
+ let maxY = -Infinity;
46345
+ for (let i = 0; i <= n; i++) {
46346
+ const x = x0 + (xr - x0) * i / n;
46347
+ const y = at(x);
46348
+ if (y > -Infinity) {
46349
+ pts.push([x, y]);
46350
+ if (y > maxY) maxY = y;
46351
+ }
46352
+ }
46353
+ if (pts.length === 0) return () => yB - height * 0.42;
46354
+ let m = 0;
46355
+ if (pts.length >= 2) {
46356
+ let sx = 0, sy = 0, sxx = 0, sxy = 0;
46357
+ for (const [x, y] of pts) {
46358
+ sx += x;
46359
+ sy += y;
46360
+ sxx += x * x;
46361
+ sxy += x * y;
46362
+ }
46363
+ const den = pts.length * sxx - sx * sx;
46364
+ if (den !== 0) m = (pts.length * sxy - sx * sy) / den;
46365
+ }
46366
+ m = Math.max(-0.35, Math.min(0.35, m));
46367
+ let c = -Infinity;
46368
+ for (const [x, y] of pts) {
46369
+ const need = y - m * x + GAP;
46370
+ if (need > c) c = need;
46371
+ }
46372
+ return (x) => m * x + c;
46373
+ };
46374
+ const placeInset = (iso, proj, boxX, iwReq) => {
46375
+ const f = usLayer.get(iso);
46376
+ if (!f) return boxX;
46377
+ const x0 = boxX;
46378
+ const iw = Math.min(iwReq, width - FIT_PAD - x0 - 2 * PAD);
46379
+ if (iw < 24) return boxX;
46380
+ const xr = x0 + iw + 2 * PAD;
46381
+ const top = coastTop(x0, xr);
46382
+ const yL = top(x0);
46383
+ const yR = top(xr);
46384
+ proj.fitWidth(iw, f);
46385
+ const bb = (0, import_d3_geo2.geoPath)(proj).bounds(f);
46386
+ const sh = Number.isFinite(bb[0][0]) ? bb[1][1] - bb[0][1] : iw;
46387
+ const needH = sh + 2 * PAD;
46388
+ let topFit = Math.max(yL, yR);
46389
+ const bottom = Math.min(topFit + needH, yB);
46390
+ if (bottom - topFit < needH) topFit = bottom - needH;
46391
+ const lift = topFit - Math.max(yL, yR);
46392
+ const topL = yL + lift;
46393
+ const topR = yR + lift;
46394
+ proj.fitExtent(
46395
+ [
46396
+ [x0 + PAD, topFit + PAD],
46397
+ [xr - PAD, bottom - PAD]
46398
+ ],
46399
+ f
46400
+ );
46401
+ const d = (0, import_d3_geo2.geoPath)(proj)(f) ?? "";
46402
+ if (!d) return xr;
46403
+ const r = regionById.get(iso);
46404
+ let fill2 = neutralFill;
46405
+ let lineNumber = -1;
46406
+ if (r?.layer === "us-state") {
46407
+ fill2 = regionFill(r);
46408
+ lineNumber = r.lineNumber;
46409
+ }
46410
+ insets.push({
46411
+ x: x0,
46412
+ y: Math.min(topL, topR),
46413
+ w: xr - x0,
46414
+ h: bottom - Math.min(topL, topR),
46415
+ points: [
46416
+ [x0, topL],
46417
+ [xr, topR],
46418
+ [xr, bottom],
46419
+ [x0, bottom]
46420
+ ]
46421
+ });
46422
+ insetRegions.push({
46423
+ id: iso,
46424
+ d,
46425
+ fill: fill2,
46426
+ stroke: regionStroke,
46427
+ lineNumber,
46428
+ layer: "us-state",
46429
+ ...r?.score !== void 0 && { score: r.score },
46430
+ ...r && Object.keys(r.tags).length > 0 && { tags: r.tags }
46431
+ });
46432
+ const ctr = (0, import_d3_geo2.geoPath)(proj).centroid(f);
46433
+ if (Number.isFinite(ctr[0])) {
46434
+ const name = f.properties?.name ?? iso;
46435
+ insetLabelSeeds.push({ x: ctr[0], y: ctr[1], iso, name, lineNumber });
46436
+ }
46437
+ return xr;
46438
+ };
46439
+ const akRight = placeInset(
46440
+ "US-AK",
46441
+ alaskaProjection(),
46442
+ FIT_PAD,
46443
+ width * 0.15
46444
+ );
46445
+ placeInset("US-HI", hawaiiProjection(), akRight + 24, width * 0.1);
46446
+ }
46447
+ const conusFit = resolved.projection === "albers-usa" && !!usLayer;
46448
+ const cullExtent = conusFit ? (0, import_d3_geo2.geoBounds)(fitTarget) : resolved.extent;
46449
+ const [[exW, exS], [exE, exN]] = cullExtent;
46450
+ const lonSpan = exE - exW;
46451
+ const latSpan = exN - exS;
46452
+ const isGlobalView = lonSpan >= 270 || latSpan >= 130;
46453
+ const padLon = Math.max(8, lonSpan * 0.35);
46454
+ const padLat = Math.max(8, latSpan * 0.35);
46455
+ const vW = exW - padLon;
46456
+ const vE = exE + padLon;
46457
+ const vS = exS - padLat;
46458
+ const vN = exN + padLat;
46459
+ const vLonCenter = (exW + exE) / 2;
46460
+ const normLon = (lon) => {
46461
+ let L = lon;
46462
+ while (L < vLonCenter - 180) L += 360;
46463
+ while (L > vLonCenter + 180) L -= 360;
46464
+ return L;
46465
+ };
46466
+ const ringOverlapsView = (ring) => {
46467
+ let anyIn = false;
46468
+ let loMin = Infinity, loMax = -Infinity, laMin = Infinity, laMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
46469
+ for (const [rawLon, lat] of ring) {
46470
+ const lon = normLon(rawLon);
46471
+ if (lon >= vW && lon <= vE && lat >= vS && lat <= vN) anyIn = true;
46472
+ if (lon < loMin) loMin = lon;
46473
+ if (lon > loMax) loMax = lon;
46474
+ if (rawLon < rawMin) rawMin = rawLon;
46475
+ if (rawLon > rawMax) rawMax = rawLon;
46476
+ if (lat < laMin) laMin = lat;
46477
+ if (lat > laMax) laMax = lat;
46478
+ }
46479
+ if (loMax - loMin > 270) return false;
46480
+ if (rawMax - rawMin > 180 && loMax - loMin < 90) return false;
46481
+ if (anyIn) return true;
46482
+ if (loMax - loMin > 180) return false;
46483
+ return !(loMax < vW || loMin > vE || laMax < vS || laMin > vN);
46484
+ };
46485
+ const cullFeatureToView = (f) => {
46486
+ if (isGlobalView) return f;
46487
+ const g = f.geometry;
46488
+ if (!g) return f;
46489
+ if (g.type === "Polygon") {
46490
+ const ring = g.coordinates[0];
46491
+ return ringOverlapsView(ring) ? f : null;
46492
+ }
46493
+ if (g.type === "MultiPolygon") {
46494
+ const polys = g.coordinates;
46495
+ const keep = polys.filter(
46496
+ (p) => ringOverlapsView(p[0])
46497
+ );
46498
+ if (!keep.length) return null;
46499
+ if (keep.length === polys.length) return f;
46500
+ return { ...f, geometry: { ...g, coordinates: keep } };
46501
+ }
46502
+ return f;
46503
+ };
46504
+ const SEAM_SLIVER_MAX_SPAN = 100;
46505
+ const ringIsFrameFiller = (ring) => {
46506
+ const lons = ring.map(([lon]) => lon).sort((a, b) => a - b);
46507
+ if (lons.length < 2) return false;
46508
+ let maxGap = -1;
46509
+ let gapIdx = 0;
46510
+ for (let i = 1; i < lons.length; i++) {
46511
+ const g = lons[i] - lons[i - 1];
46512
+ if (g > maxGap) {
46513
+ maxGap = g;
46514
+ gapIdx = i;
46515
+ }
46516
+ }
46517
+ const wrapGap = lons[0] + 360 - lons[lons.length - 1];
46518
+ if (wrapGap >= maxGap) return false;
46519
+ const span = 360 - maxGap;
46520
+ const east = lons[gapIdx - 1] + 360;
46521
+ return east > 180 && span < SEAM_SLIVER_MAX_SPAN;
46522
+ };
46523
+ const dropFrameFillers = (f) => {
46524
+ const g = f.geometry;
46525
+ if (!g) return f;
46526
+ if (g.type === "Polygon") {
46527
+ const ring = g.coordinates[0];
46528
+ return ringIsFrameFiller(ring) ? null : f;
46529
+ }
46530
+ if (g.type === "MultiPolygon") {
46531
+ const polys = g.coordinates;
46532
+ const keep = polys.filter(
46533
+ (p) => !ringIsFrameFiller(p[0])
46534
+ );
46535
+ if (!keep.length) return null;
46536
+ if (keep.length === polys.length) return f;
46537
+ return { ...f, geometry: { ...g, coordinates: keep } };
46538
+ }
46539
+ return f;
46540
+ };
46066
46541
  const regions = [];
46067
- const pushRegionLayer = (layerFeatures, layerKind) => {
46542
+ const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
46068
46543
  for (const [iso, f] of layerFeatures) {
46069
- const d = path(f) ?? "";
46070
- if (!d) continue;
46544
+ if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
46545
+ continue;
46546
+ if (layerKind === "country" && usContext && iso === "US") continue;
46071
46547
  const r = regionById.get(iso);
46548
+ const viewF = shouldCull ? cullFeatureToView(f) : dropFrameFillers(f);
46549
+ if (!viewF) continue;
46550
+ const d = path(viewF) ?? "";
46551
+ if (!d) continue;
46072
46552
  const isThisLayer = r?.layer === layerKind;
46073
- let fill2 = neutralFill;
46553
+ const isForeign = layerKind === "country" && usContext && iso !== "US";
46554
+ let fill2 = isForeign ? foreignFill : neutralFill;
46074
46555
  let label;
46075
46556
  let lineNumber = -1;
46076
46557
  let layer = "base";
46077
46558
  if (isThisLayer) {
46078
- if (r.score !== void 0) fill2 = fillForScore(r.score);
46079
- else fill2 = tagFill(r.tags, activeGroup) ?? neutralFill;
46559
+ fill2 = regionFill(r);
46080
46560
  lineNumber = r.lineNumber;
46081
46561
  layer = layerKind;
46082
46562
  label = r.name;
@@ -46088,12 +46568,42 @@ function layoutMap(resolved, data, size, opts) {
46088
46568
  stroke: regionStroke,
46089
46569
  lineNumber,
46090
46570
  layer,
46091
- ...label !== void 0 && { label }
46571
+ ...label !== void 0 && { label },
46572
+ ...isThisLayer && r.score !== void 0 && { score: r.score },
46573
+ ...isThisLayer && Object.keys(r.tags).length > 0 && { tags: r.tags }
46092
46574
  });
46093
46575
  }
46094
46576
  };
46095
- pushRegionLayer(worldLayer, "country");
46096
- if (usLayer) pushRegionLayer(usLayer, "us-state");
46577
+ pushRegionLayer(worldLayer, "country", !isGlobalView);
46578
+ if (usLayer) pushRegionLayer(usLayer, "us-state", !conusFit && !isGlobalView);
46579
+ const lakesTopo = usCrisp && data.naLakes ? data.naLakes : data.lakes;
46580
+ if (lakesTopo) {
46581
+ for (const [, f] of decodeLayer(lakesTopo)) {
46582
+ const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
46583
+ if (!viewF) continue;
46584
+ const d = path(viewF) ?? "";
46585
+ if (!d) continue;
46586
+ regions.push({
46587
+ id: "lake",
46588
+ d,
46589
+ fill: water,
46590
+ stroke: "none",
46591
+ lineNumber: -1,
46592
+ layer: "base"
46593
+ });
46594
+ }
46595
+ }
46596
+ const riverColor = water;
46597
+ const rivers = [];
46598
+ if (data.rivers) {
46599
+ for (const [, f] of decodeLayer(data.rivers)) {
46600
+ const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
46601
+ if (!viewF) continue;
46602
+ const d = path(viewF) ?? "";
46603
+ if (!d) continue;
46604
+ rivers.push({ d, color: riverColor, width: RIVER_WIDTH });
46605
+ }
46606
+ }
46097
46607
  const sizeVals = resolved.pois.map((p) => Number(p.meta["size"])).filter((n) => Number.isFinite(n) && n > 0);
46098
46608
  const sizeMin = sizeVals.length ? Math.min(...sizeVals) : 0;
46099
46609
  const sizeMax = sizeVals.length ? Math.max(...sizeVals) : 0;
@@ -46114,8 +46624,8 @@ function layoutMap(resolved, data, size, opts) {
46114
46624
  if (hex) return { fill: hex, stroke: mix(hex, palette.text, 18) };
46115
46625
  }
46116
46626
  return {
46117
- fill: palette.accent,
46118
- stroke: mix(palette.accent, palette.text, 18)
46627
+ fill: palette.colors.orange,
46628
+ stroke: mix(palette.colors.orange, palette.text, 18)
46119
46629
  };
46120
46630
  };
46121
46631
  const routeNumberById = /* @__PURE__ */ new Map();
@@ -46153,7 +46663,7 @@ function layoutMap(resolved, data, size, opts) {
46153
46663
  cy += Math.sin(ang) * COLO_R;
46154
46664
  }
46155
46665
  const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
46156
- poiScreen.set(e.p.id, { cx, cy });
46666
+ poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
46157
46667
  const num = routeNumberById.get(e.p.id);
46158
46668
  pois.push({
46159
46669
  id: e.p.id,
@@ -46170,17 +46680,36 @@ function layoutMap(resolved, data, size, opts) {
46170
46680
  });
46171
46681
  }
46172
46682
  const legs = [];
46683
+ const RIM_GAP = 1.5;
46173
46684
  const legPath = (a, b, curved, offset) => {
46174
- if (!curved && offset === 0) return `M${a.cx},${a.cy}L${b.cx},${b.cy}`;
46175
46685
  const mx = (a.cx + b.cx) / 2;
46176
46686
  const my = (a.cy + b.cy) / 2;
46177
46687
  const dx = b.cx - a.cx;
46178
46688
  const dy = b.cy - a.cy;
46179
46689
  const len = Math.hypot(dx, dy) || 1;
46690
+ const trimA = Math.min(a.r + RIM_GAP, len * 0.45);
46691
+ const trimB = Math.min(b.r + RIM_GAP, len * 0.45);
46692
+ if (!curved && offset === 0) {
46693
+ const ux = dx / len;
46694
+ const uy = dy / len;
46695
+ const ax2 = a.cx + ux * trimA;
46696
+ const ay2 = a.cy + uy * trimA;
46697
+ const bx2 = b.cx - ux * trimB;
46698
+ const by2 = b.cy - uy * trimB;
46699
+ return `M${ax2},${ay2}L${bx2},${by2}`;
46700
+ }
46180
46701
  const nx = -dy / len;
46181
46702
  const ny = dx / len;
46182
46703
  const bow = offset !== 0 ? offset : len * ARC_CURVE_FRAC;
46183
- return `M${a.cx},${a.cy}Q${mx + nx * bow},${my + ny * bow} ${b.cx},${b.cy}`;
46704
+ const px = mx + nx * bow;
46705
+ const py = my + ny * bow;
46706
+ const ta = Math.hypot(px - a.cx, py - a.cy) || 1;
46707
+ const tb = Math.hypot(b.cx - px, b.cy - py) || 1;
46708
+ const ax = a.cx + (px - a.cx) / ta * trimA;
46709
+ const ay = a.cy + (py - a.cy) / ta * trimA;
46710
+ const bx = b.cx - (b.cx - px) / tb * trimB;
46711
+ const by = b.cy - (b.cy - py) / tb * trimB;
46712
+ return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
46184
46713
  };
46185
46714
  for (const rt of resolved.routes) {
46186
46715
  const curved = rt.meta["style"] === "arc";
@@ -46191,7 +46720,7 @@ function layoutMap(resolved, data, size, opts) {
46191
46720
  legs.push({
46192
46721
  d: legPath(a, b, curved, 0),
46193
46722
  width: W_MIN,
46194
- color: mix(palette.text, palette.bg, 55),
46723
+ color: mix(palette.text, palette.bg, 72),
46195
46724
  arrow: true,
46196
46725
  lineNumber: rt.lineNumber
46197
46726
  });
@@ -46227,7 +46756,7 @@ function layoutMap(resolved, data, size, opts) {
46227
46756
  legs.push({
46228
46757
  d: legPath(a, b, curved, offset),
46229
46758
  width: widthFor(e),
46230
- color: mix(palette.text, palette.bg, 45),
46759
+ color: mix(palette.text, palette.bg, 66),
46231
46760
  arrow: e.directed,
46232
46761
  lineNumber: e.lineNumber,
46233
46762
  ...e.label !== void 0 && {
@@ -46239,38 +46768,92 @@ function layoutMap(resolved, data, size, opts) {
46239
46768
  });
46240
46769
  }
46241
46770
  const labels = [];
46242
- const pinList = [];
46243
46771
  const obstacles = [];
46244
46772
  const markers = pois.map((p) => ({
46245
46773
  cx: p.cx,
46246
46774
  cy: p.cy,
46247
46775
  r: p.r
46248
46776
  }));
46249
- const collides = (rect) => markers.some((m) => rectCircleOverlap(rect, m)) || obstacles.some((o) => rectsOverlap(rect, o));
46777
+ const legSegments = [];
46778
+ for (const leg of legs) {
46779
+ const m = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
46780
+ leg.d
46781
+ );
46782
+ if (!m) continue;
46783
+ const x0 = +m[1];
46784
+ const y0 = +m[2];
46785
+ if (m[3] !== void 0) {
46786
+ legSegments.push([x0, y0, +m[3], +m[4]]);
46787
+ } else {
46788
+ const cx = +m[5];
46789
+ const cy = +m[6];
46790
+ const ex = +m[7];
46791
+ const ey = +m[8];
46792
+ const N = 8;
46793
+ let px = x0;
46794
+ let py = y0;
46795
+ for (let i = 1; i <= N; i++) {
46796
+ const t = i / N;
46797
+ const u = 1 - t;
46798
+ const qx = u * u * x0 + 2 * u * t * cx + t * t * ex;
46799
+ const qy = u * u * y0 + 2 * u * t * cy + t * t * ey;
46800
+ legSegments.push([px, py, qx, qy]);
46801
+ px = qx;
46802
+ py = qy;
46803
+ }
46804
+ }
46805
+ }
46806
+ const collides = (rect) => markers.some((m) => rectCircleOverlap(rect, m)) || obstacles.some((o) => rectsOverlap(rect, o)) || legSegments.some((s) => segmentRectOverlap(s[0], s[1], s[2], s[3], rect));
46250
46807
  const regionLabelMode = resolved.directives.regionLabels ?? "off";
46808
+ const LABEL_PADX = 6;
46809
+ const LABEL_PADY = 3;
46810
+ const labelW = (text) => measureLegendText(text, FONT) + 2 * LABEL_PADX;
46811
+ const labelH = FONT + 2 * LABEL_PADY;
46812
+ const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
46813
+ const color = contrastText(
46814
+ fill2,
46815
+ palette.textOnFillLight,
46816
+ palette.textOnFillDark
46817
+ );
46818
+ const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
46819
+ labels.push({
46820
+ x,
46821
+ y,
46822
+ text,
46823
+ anchor: "middle",
46824
+ color,
46825
+ halo: true,
46826
+ haloColor,
46827
+ lineNumber
46828
+ });
46829
+ };
46830
+ const WORLD_LABEL_ANCHORS = {
46831
+ US: [-98.5, 39.5]
46832
+ // CONUS geographic centre (near Lebanon, Kansas)
46833
+ };
46251
46834
  if (regionLabelMode === "full" || regionLabelMode === "abbrev") {
46252
46835
  for (const r of regions) {
46253
46836
  if (r.layer === "base" || r.label === void 0) continue;
46254
46837
  const f = r.layer === "us-state" ? usLayer?.get(r.id) : worldLayer.get(r.id);
46255
46838
  if (!f) continue;
46256
46839
  const [[x0, y0], [x1, y1]] = path.bounds(f);
46257
- if ((x1 - x0) * (y1 - y0) < TINY_REGION_AREA) continue;
46258
- const c = path.centroid(f);
46259
- if (!Number.isFinite(c[0])) continue;
46260
46840
  const text = regionLabelMode === "abbrev" ? r.id.replace(/^US-/, "") : r.label;
46261
- labels.push({
46262
- x: c[0],
46263
- y: c[1],
46841
+ if (labelW(text) > x1 - x0 || labelH > y1 - y0) continue;
46842
+ const anchor = r.layer !== "us-state" ? WORLD_LABEL_ANCHORS[r.id] : void 0;
46843
+ const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
46844
+ if (!c || !Number.isFinite(c[0])) continue;
46845
+ pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
46846
+ }
46847
+ for (const seed of insetLabelSeeds) {
46848
+ const text = regionLabelMode === "abbrev" ? seed.iso.replace(/^US-/, "") : seed.name;
46849
+ const src = regionById.get(seed.iso);
46850
+ pushRegionLabel(
46851
+ seed.x,
46852
+ seed.y,
46264
46853
  text,
46265
- anchor: "middle",
46266
- color: contrastText(
46267
- r.fill,
46268
- palette.textOnFillLight,
46269
- palette.textOnFillDark
46270
- ),
46271
- halo: true,
46272
- lineNumber: r.lineNumber
46273
- });
46854
+ src ? regionFill(src) : neutralFill,
46855
+ seed.lineNumber
46856
+ );
46274
46857
  }
46275
46858
  }
46276
46859
  const poiLabelMode = resolved.directives.poiLabels ?? "auto";
@@ -46283,68 +46866,106 @@ function layoutMap(resolved, data, size, opts) {
46283
46866
  const src = poiById.get(p.id);
46284
46867
  return src?.label ?? src?.name ?? p.id;
46285
46868
  };
46286
- let pinCounter = 0;
46287
- for (const p of ordered) {
46869
+ const poiLabH = FONT * 1.25;
46870
+ const labelInfo = (p) => {
46288
46871
  const text = labelText(p);
46289
- const w = measureLegendText(text, FONT);
46290
- const h = FONT * 1.25;
46291
- const inline = { x: p.cx + p.r + 3, y: p.cy - h / 2, w, h };
46292
- if (!collides(inline)) {
46293
- obstacles.push(inline);
46872
+ return { text, w: measureLegendText(text, FONT) };
46873
+ };
46874
+ const pushInline = (p, text, w, side) => {
46875
+ const tx = side === "right" ? p.cx + p.r + 3 : p.cx - p.r - 3;
46876
+ obstacles.push({
46877
+ x: side === "right" ? tx : tx - w,
46878
+ y: p.cy - poiLabH / 2,
46879
+ w,
46880
+ h: poiLabH
46881
+ });
46882
+ labels.push({
46883
+ x: tx,
46884
+ y: p.cy + FONT / 3,
46885
+ text,
46886
+ anchor: side === "right" ? "start" : "end",
46887
+ color: palette.text,
46888
+ halo: true,
46889
+ haloColor: palette.bg,
46890
+ poiId: p.id,
46891
+ lineNumber: p.lineNumber
46892
+ });
46893
+ };
46894
+ const inlineFits = (p, w, side) => {
46895
+ const tx = side === "right" ? p.cx + p.r + 3 : p.cx - p.r - 3;
46896
+ const rect = {
46897
+ x: side === "right" ? tx : tx - w,
46898
+ y: p.cy - poiLabH / 2,
46899
+ w,
46900
+ h: poiLabH
46901
+ };
46902
+ return rect.x >= 0 && rect.x + rect.w <= width && !collides(rect);
46903
+ };
46904
+ const GROUP_R = 30;
46905
+ const groups = [];
46906
+ for (const p of ordered) {
46907
+ const near = groups.find(
46908
+ (g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
46909
+ );
46910
+ if (near) near.push(p);
46911
+ else groups.push([p]);
46912
+ }
46913
+ const ROW_GAP2 = 3;
46914
+ const step = poiLabH + ROW_GAP2;
46915
+ const COL_GAP = 16;
46916
+ const placeColumn = (group) => {
46917
+ const items = group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
46918
+ const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
46919
+ const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
46920
+ const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
46921
+ const maxW = Math.max(...items.map((o) => o.w));
46922
+ const side = right + COL_GAP + maxW <= width - 2 ? "right" : "left";
46923
+ const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
46924
+ const totalH = items.length * step;
46925
+ let startY = cyMid - totalH / 2;
46926
+ startY = Math.max(2, Math.min(startY, height - totalH - 2));
46927
+ items.forEach((o, i) => {
46928
+ const rowCy = startY + i * step + step / 2;
46929
+ obstacles.push({
46930
+ x: side === "right" ? colX : colX - o.w,
46931
+ y: rowCy - poiLabH / 2,
46932
+ w: o.w,
46933
+ h: poiLabH
46934
+ });
46294
46935
  labels.push({
46295
- x: inline.x,
46296
- y: p.cy + FONT / 3,
46297
- text,
46298
- anchor: "start",
46936
+ x: colX,
46937
+ y: rowCy + FONT / 3,
46938
+ text: o.text,
46939
+ anchor: side === "right" ? "start" : "end",
46299
46940
  color: palette.text,
46300
46941
  halo: true,
46301
- lineNumber: p.lineNumber
46942
+ haloColor: palette.bg,
46943
+ leader: {
46944
+ x1: o.p.cx,
46945
+ y1: o.p.cy,
46946
+ x2: side === "right" ? colX - 2 : colX + 2,
46947
+ y2: rowCy
46948
+ },
46949
+ leaderColor: o.p.fill,
46950
+ poiId: o.p.id,
46951
+ lineNumber: o.p.lineNumber
46302
46952
  });
46303
- continue;
46304
- }
46305
- let placed = false;
46306
- for (let k = 1; k <= 2 && !placed; k++) {
46307
- for (const [dx, dy] of RING_DIRS) {
46308
- const cx = p.cx + dx * LEADER_STEP * k;
46309
- const cy = p.cy + dy * LEADER_STEP * k;
46310
- const rect = {
46311
- x: dx >= 0 ? cx : cx - w,
46312
- y: cy - h / 2,
46313
- w,
46314
- h
46315
- };
46316
- if (rect.x < 0 || rect.x + rect.w > width || rect.y < 0 || rect.y + rect.h > height) {
46317
- continue;
46318
- }
46319
- if (collides(rect)) continue;
46320
- obstacles.push(rect);
46321
- labels.push({
46322
- x: cx,
46323
- y: cy + FONT / 3,
46324
- text,
46325
- anchor: dx >= 0 ? "start" : "end",
46326
- color: palette.text,
46327
- halo: true,
46328
- leader: { x1: p.cx, y1: p.cy, x2: cx, y2: cy },
46329
- lineNumber: p.lineNumber
46330
- });
46331
- placed = true;
46332
- break;
46953
+ });
46954
+ };
46955
+ for (const g of groups) {
46956
+ if (g.length === 1) {
46957
+ const p = g[0];
46958
+ const { text, w } = labelInfo(p);
46959
+ if (inlineFits(p, w, "right")) {
46960
+ pushInline(p, text, w, "right");
46961
+ continue;
46962
+ }
46963
+ if (inlineFits(p, w, "left")) {
46964
+ pushInline(p, text, w, "left");
46965
+ continue;
46333
46966
  }
46334
46967
  }
46335
- if (placed) continue;
46336
- pinCounter += 1;
46337
- pinList.push({ pin: pinCounter, label: text });
46338
- labels.push({
46339
- x: p.cx + p.r + 2,
46340
- y: p.cy - p.r,
46341
- text: String(pinCounter),
46342
- anchor: "start",
46343
- color: palette.text,
46344
- halo: true,
46345
- pin: pinCounter,
46346
- lineNumber: p.lineNumber
46347
- });
46968
+ placeColumn(g);
46348
46969
  }
46349
46970
  }
46350
46971
  let legend = null;
@@ -46353,8 +46974,7 @@ function layoutMap(resolved, data, size, opts) {
46353
46974
  name: g.name,
46354
46975
  entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
46355
46976
  }));
46356
- const hasAnything = tagGroups.length > 0 || hasRamp || sizeVals.length > 0 || weightVals.length > 0;
46357
- if (hasAnything) {
46977
+ if (tagGroups.length > 0 || hasRamp) {
46358
46978
  legend = {
46359
46979
  tagGroups,
46360
46980
  activeGroup,
@@ -46365,20 +46985,9 @@ function layoutMap(resolved, data, size, opts) {
46365
46985
  },
46366
46986
  min: rampMin,
46367
46987
  max: rampMax,
46368
- hue: rampHue
46369
- }
46370
- },
46371
- ...sizeVals.length > 0 && {
46372
- size: {
46373
- ...resolved.directives.sizeMetric !== void 0 && {
46374
- metric: resolved.directives.sizeMetric
46375
- },
46376
- min: sizeMin,
46377
- max: sizeMax
46988
+ hue: rampHue,
46989
+ base: rampBase
46378
46990
  }
46379
- },
46380
- ...weightVals.length > 0 && {
46381
- weight: { min: wMin, max: wMax }
46382
46991
  }
46383
46992
  };
46384
46993
  }
@@ -46386,28 +46995,30 @@ function layoutMap(resolved, data, size, opts) {
46386
46995
  return {
46387
46996
  width,
46388
46997
  height,
46389
- background: palette.bg,
46998
+ background: water,
46390
46999
  title: resolved.title,
46391
47000
  ...resolved.subtitle !== void 0 && { subtitle: resolved.subtitle },
46392
47001
  ...resolved.caption !== void 0 && { caption: resolved.caption },
46393
47002
  regions,
47003
+ rivers,
46394
47004
  legs,
46395
47005
  pois,
46396
47006
  labels,
46397
- pinList,
46398
- legend
47007
+ legend,
47008
+ insets,
47009
+ insetRegions
46399
47010
  };
46400
47011
  }
46401
- var import_d3_geo2, import_topojson_client2, FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, LEADER_STEP, COLO_EPS, COLO_R, GOLDEN_ANGLE, FAN_STEP, TINY_REGION_AREA, ARC_CURVE_FRAC, RING_DIRS;
47012
+ var import_d3_geo2, import_topojson_client2, FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT, RIVER_WIDTH, FOREIGN_TINT_LIGHT, FOREIGN_TINT_DARK, COLO_R, GOLDEN_ANGLE, FAN_STEP, ARC_CURVE_FRAC, usConusProjection, alaskaProjection, hawaiiProjection, INSET_STATES, US_NON_CONUS;
46402
47013
  var init_layout15 = __esm({
46403
47014
  "src/map/layout.ts"() {
46404
47015
  "use strict";
46405
47016
  import_d3_geo2 = require("d3-geo");
46406
47017
  import_topojson_client2 = require("topojson-client");
46407
47018
  init_color_utils();
46408
- init_tag_groups();
46409
47019
  init_label_layout();
46410
47020
  init_legend_constants();
47021
+ init_title_constants();
46411
47022
  FIT_PAD = 24;
46412
47023
  RAMP_FLOOR = 15;
46413
47024
  R_DEFAULT = 6;
@@ -46416,23 +47027,32 @@ var init_layout15 = __esm({
46416
47027
  W_MIN = 1.25;
46417
47028
  W_MAX = 8;
46418
47029
  FONT = 11;
46419
- LEADER_STEP = 14;
46420
47030
  COLO_EPS = 1.5;
47031
+ LAND_TINT_LIGHT = 58;
47032
+ LAND_TINT_DARK = 75;
47033
+ TAG_TINT_LIGHT = 60;
47034
+ TAG_TINT_DARK = 68;
47035
+ WATER_TINT = 55;
47036
+ RIVER_WIDTH = 1.3;
47037
+ FOREIGN_TINT_LIGHT = 30;
47038
+ FOREIGN_TINT_DARK = 62;
46421
47039
  COLO_R = 9;
46422
47040
  GOLDEN_ANGLE = 2.399963229728653;
46423
47041
  FAN_STEP = 16;
46424
- TINY_REGION_AREA = 600;
46425
47042
  ARC_CURVE_FRAC = 0.18;
46426
- RING_DIRS = [
46427
- [1, 0],
46428
- [0, 1],
46429
- [-1, 0],
46430
- [0, -1],
46431
- [1, 1],
46432
- [-1, 1],
46433
- [-1, -1],
46434
- [1, -1]
46435
- ];
47043
+ usConusProjection = () => (0, import_d3_geo2.geoConicEqualArea)().parallels([29.5, 45.5]).rotate([96, 0]);
47044
+ alaskaProjection = () => (0, import_d3_geo2.geoConicEqualArea)().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
47045
+ hawaiiProjection = () => (0, import_d3_geo2.geoMercator)();
47046
+ INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
47047
+ US_NON_CONUS = /* @__PURE__ */ new Set([
47048
+ "US-AK",
47049
+ "US-HI",
47050
+ "US-AS",
47051
+ "US-GU",
47052
+ "US-MP",
47053
+ "US-PR",
47054
+ "US-VI"
47055
+ ]);
46436
47056
  }
46437
47057
  });
46438
47058
 
@@ -46442,7 +47062,7 @@ __export(renderer_exports16, {
46442
47062
  renderMap: () => renderMap,
46443
47063
  renderMapForExport: () => renderMapForExport
46444
47064
  });
46445
- function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims) {
47065
+ function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
46446
47066
  d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
46447
47067
  const width = exportDims?.width ?? container.clientWidth;
46448
47068
  const height = exportDims?.height ?? container.clientHeight;
@@ -46453,27 +47073,29 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
46453
47073
  { width, height },
46454
47074
  {
46455
47075
  palette,
46456
- isDark
47076
+ isDark,
47077
+ ...activeGroupOverride !== void 0 && {
47078
+ activeGroup: activeGroupOverride
47079
+ }
46457
47080
  }
46458
47081
  );
46459
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
47082
+ const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY).style("background", layout.background);
46460
47083
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
46461
- const arrowColor = mix(palette.text, palette.bg, 50);
46462
47084
  const defs = svg.append("defs");
46463
- defs.append("marker").attr("id", "dgmo-map-arrow").attr("viewBox", "0 0 10 10").attr("refX", 9).attr("refY", 5).attr("markerWidth", 7).attr("markerHeight", 7).attr("orient", "auto-start-reverse").append("path").attr("d", "M0,0L10,5L0,10z").attr("fill", arrowColor);
46464
- const haloColor = layout.background;
46465
- if (layout.title) {
46466
- svg.append("text").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).text(layout.title);
46467
- }
46468
- if (layout.subtitle) {
46469
- svg.append("text").attr("x", width / 2).attr("y", TITLE_Y + TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("font-size", LABEL_FONT + 1).attr("fill", palette.textMuted).text(layout.subtitle);
46470
- }
46471
- if (layout.caption) {
46472
- svg.append("text").attr("x", width / 2).attr("y", height - 8).attr("text-anchor", "middle").attr("font-size", LABEL_FONT).attr("fill", palette.textMuted).text(layout.caption);
46473
- }
47085
+ const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
47086
+ const haloColor = palette.bg;
46474
47087
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
46475
- for (const r of layout.regions) {
46476
- const p = gRegions.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", 0.5);
47088
+ const drawRegion = (g, r, strokeWidth) => {
47089
+ const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
47090
+ if (r.layer !== "base") {
47091
+ p.classed("dgmo-map-region", true).attr("data-region", r.id);
47092
+ if (r.score !== void 0) p.attr("data-score", r.score);
47093
+ if (r.tags) {
47094
+ for (const [group, value] of Object.entries(r.tags)) {
47095
+ p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
47096
+ }
47097
+ }
47098
+ }
46477
47099
  if (r.lineNumber >= 0) {
46478
47100
  p.attr("data-line-number", r.lineNumber);
46479
47101
  if (onClickItem) {
@@ -46483,11 +47105,31 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
46483
47105
  );
46484
47106
  }
46485
47107
  }
47108
+ };
47109
+ for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
47110
+ if (layout.rivers.length) {
47111
+ const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
47112
+ for (const r of layout.rivers) {
47113
+ gRivers.append("path").attr("d", r.d).attr("stroke", r.color).attr("stroke-width", r.width).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
47114
+ }
47115
+ }
47116
+ if (layout.insets.length) {
47117
+ const insetG = svg.append("g").attr("class", "dgmo-map-insets");
47118
+ for (const box of layout.insets) {
47119
+ const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
47120
+ insetG.append("path").attr("d", d).attr("fill", layout.background).attr("stroke", mix(palette.text, palette.bg, 55)).attr("stroke-width", 1).attr("stroke-linejoin", "round");
47121
+ }
47122
+ for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
46486
47123
  }
46487
47124
  const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
46488
- for (const leg of layout.legs) {
47125
+ layout.legs.forEach((leg, i) => {
46489
47126
  const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
46490
- if (leg.arrow) p.attr("marker-end", "url(#dgmo-map-arrow)");
47127
+ if (leg.arrow) {
47128
+ const id = `dgmo-map-arrow-${i}`;
47129
+ const s = arrowSize(leg.width);
47130
+ defs.append("marker").attr("id", id).attr("viewBox", "0 0 10 10").attr("refX", 10).attr("refY", 5).attr("markerUnits", "userSpaceOnUse").attr("markerWidth", s).attr("markerHeight", s).attr("orient", "auto-start-reverse").append("path").attr("d", "M0,0L10,5L0,10z").attr("fill", leg.color);
47131
+ p.attr("marker-end", `url(#${id})`);
47132
+ }
46491
47133
  if (leg.label !== void 0 && leg.labelX !== void 0) {
46492
47134
  emitText(
46493
47135
  gLegs,
@@ -46501,13 +47143,13 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
46501
47143
  LABEL_FONT - 1
46502
47144
  );
46503
47145
  }
46504
- }
47146
+ });
46505
47147
  const gPois = svg.append("g").attr("class", "dgmo-map-pois");
46506
47148
  for (const poi of layout.pois) {
46507
47149
  if (poi.isOrigin) {
46508
47150
  gPois.append("circle").attr("cx", poi.cx).attr("cy", poi.cy).attr("r", poi.r + 3).attr("fill", "none").attr("stroke", poi.stroke).attr("stroke-width", 1.5);
46509
47151
  }
46510
- const c = gPois.append("circle").attr("cx", poi.cx).attr("cy", poi.cy).attr("r", poi.r).attr("fill", poi.fill).attr("stroke", poi.stroke).attr("stroke-width", 1).attr("data-line-number", poi.lineNumber);
47152
+ const c = gPois.append("circle").attr("cx", poi.cx).attr("cy", poi.cy).attr("r", poi.r).attr("fill", poi.fill).attr("stroke", poi.stroke).attr("stroke-width", 1).attr("data-line-number", poi.lineNumber).attr("data-poi", poi.id);
46511
47153
  if (onClickItem) {
46512
47154
  c.style("cursor", "pointer").on(
46513
47155
  "click",
@@ -46531,48 +47173,66 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
46531
47173
  const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
46532
47174
  for (const lab of layout.labels) {
46533
47175
  if (lab.leader) {
46534
- gLabels.append("line").attr("x1", lab.leader.x1).attr("y1", lab.leader.y1).attr("x2", lab.leader.x2).attr("y2", lab.leader.y2).attr("stroke", mix(palette.textMuted, palette.bg, 60)).attr("stroke-width", 0.75);
47176
+ const line12 = gLabels.append("line").attr("x1", lab.leader.x1).attr("y1", lab.leader.y1).attr("x2", lab.leader.x2).attr("y2", lab.leader.y2).attr(
47177
+ "stroke",
47178
+ lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
47179
+ ).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
47180
+ if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
46535
47181
  }
46536
- if (lab.pin !== void 0) {
46537
- gLabels.append("rect").attr("x", lab.x - 1).attr("y", lab.y - LABEL_FONT).attr("width", LABEL_FONT * 1.3).attr("height", LABEL_FONT * 1.3).attr("rx", 2).attr("fill", palette.surface).attr("stroke", palette.border);
46538
- }
46539
- emitText(
47182
+ const t = emitText(
46540
47183
  gLabels,
46541
47184
  lab.x,
46542
47185
  lab.y,
46543
47186
  lab.text,
46544
47187
  lab.anchor,
46545
47188
  lab.color,
46546
- haloColor,
47189
+ lab.haloColor,
46547
47190
  lab.halo,
46548
47191
  LABEL_FONT
46549
47192
  );
46550
- }
46551
- if (layout.pinList.length > 0) {
46552
- const gPins = svg.append("g").attr("class", "dgmo-map-pin-list").attr(
46553
- "transform",
46554
- `translate(12, ${height - layout.pinList.length * 14 - 8})`
46555
- );
46556
- layout.pinList.forEach((entry, i) => {
46557
- gPins.append("text").attr("x", 0).attr("y", i * 14).attr("font-size", LABEL_FONT - 1).attr("fill", palette.textMuted).text(`${entry.pin} \u2014 ${entry.label}`);
46558
- });
47193
+ if (lab.poiId !== void 0) {
47194
+ t.attr("data-poi", lab.poiId).style("cursor", "default");
47195
+ }
46559
47196
  }
46560
47197
  if (layout.legend) {
46561
47198
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
46562
47199
  const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
46563
- const groups = layout.legend.tagGroups.filter((g) => g.entries.length > 0);
47200
+ const ramp = layout.legend.ramp;
47201
+ const scoreGroup = ramp ? {
47202
+ name: ramp.metric?.trim() || "Score",
47203
+ entries: [],
47204
+ gradient: {
47205
+ min: ramp.min,
47206
+ max: ramp.max,
47207
+ hue: ramp.hue,
47208
+ base: ramp.base
47209
+ }
47210
+ } : null;
47211
+ const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
47212
+ const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
46564
47213
  if (groups.length > 0) {
46565
47214
  const config = {
46566
- groups: groups.map((g) => ({ name: g.name, entries: [...g.entries] })),
47215
+ groups,
46567
47216
  position: { placement: "top-center", titleRelation: "below-title" },
46568
47217
  mode: exportDims ? "export" : "preview",
46569
- showEmptyGroups: false
47218
+ showEmptyGroups: false,
47219
+ // Keep inactive siblings visible as pills so the user can click to flip
47220
+ // the active colouring dimension (preview only — export shows just the
47221
+ // active group).
47222
+ showInactivePills: true
46570
47223
  };
46571
47224
  const state = { activeGroup: layout.legend.activeGroup };
46572
47225
  renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
46573
47226
  }
46574
- const pinGap = layout.pinList.length ? layout.pinList.length * 14 + 14 : 0;
46575
- emitExtraLegend(svg, layout, palette, height, pinGap);
47227
+ }
47228
+ if (layout.title) {
47229
+ svg.append("text").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.title);
47230
+ }
47231
+ if (layout.subtitle) {
47232
+ svg.append("text").attr("x", width / 2).attr("y", TITLE_Y + TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("font-size", LABEL_FONT + 1).attr("fill", palette.textMuted).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.subtitle);
47233
+ }
47234
+ if (layout.caption) {
47235
+ svg.append("text").attr("x", width / 2).attr("y", height - 8).attr("text-anchor", "middle").attr("font-size", LABEL_FONT).attr("fill", palette.textMuted).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.caption);
46576
47236
  }
46577
47237
  }
46578
47238
  function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
@@ -46583,54 +47243,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
46583
47243
  if (withHalo) {
46584
47244
  t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7);
46585
47245
  }
46586
- }
46587
- function emitExtraLegend(svg, layout, palette, height, bottomGap) {
46588
- const { legend } = layout;
46589
- if (!legend) return;
46590
- if (!legend.ramp && !legend.size && !legend.weight) return;
46591
- const blocks = [];
46592
- const g = svg.append("g").attr("class", "dgmo-map-legend-keys").attr("transform", `translate(12, ${height - 56 - bottomGap})`);
46593
- let xCursor = 0;
46594
- if (legend.ramp) {
46595
- const ramp = legend.ramp;
46596
- blocks.push(() => {
46597
- const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
46598
- const gradId = "dgmo-map-ramp";
46599
- const grad = block.append("defs").append("linearGradient").attr("id", gradId).attr("x1", "0%").attr("x2", "100%");
46600
- grad.append("stop").attr("offset", "0%").attr("stop-color", mix(ramp.hue, palette.bg, 15));
46601
- grad.append("stop").attr("offset", "100%").attr("stop-color", ramp.hue);
46602
- block.append("rect").attr("width", 80).attr("height", 8).attr("fill", `url(#${gradId})`);
46603
- block.append("text").attr("x", 0).attr("y", 22).attr("font-size", 9).attr("fill", palette.textMuted).text(String(ramp.min));
46604
- block.append("text").attr("x", 80).attr("y", 22).attr("text-anchor", "end").attr("font-size", 9).attr("fill", palette.textMuted).text(String(ramp.max));
46605
- if (ramp.metric) {
46606
- block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(ramp.metric);
46607
- }
46608
- xCursor += 110;
46609
- });
46610
- }
46611
- if (legend.size) {
46612
- const sz = legend.size;
46613
- blocks.push(() => {
46614
- const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
46615
- [3, 6, 10].forEach((r, i) => {
46616
- block.append("circle").attr("cx", i * 26 + r).attr("cy", 8).attr("r", r).attr("fill", "none").attr("stroke", palette.textMuted);
46617
- });
46618
- block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(sz.metric ?? "size");
46619
- xCursor += 110;
46620
- });
46621
- }
46622
- if (legend.weight) {
46623
- const wt = legend.weight;
46624
- blocks.push(() => {
46625
- const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
46626
- [1, 3, 6].forEach((w, i) => {
46627
- block.append("line").attr("x1", i * 26).attr("y1", 8).attr("x2", i * 26 + 20).attr("y2", 8).attr("stroke", palette.textMuted).attr("stroke-width", w);
46628
- });
46629
- block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(wt.metric ?? "weight");
46630
- xCursor += 110;
46631
- });
46632
- }
46633
- for (const draw of blocks) draw();
47246
+ return t;
46634
47247
  }
46635
47248
  var d3Selection18, LABEL_FONT;
46636
47249
  var init_renderer16 = __esm({
@@ -53098,18 +53711,18 @@ function getRotateFn(mode) {
53098
53711
  return () => 0;
53099
53712
  }
53100
53713
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
53101
- return new Promise((resolve2) => {
53714
+ return new Promise((resolve) => {
53102
53715
  d3Selection23.select(container).selectAll(":not([data-d3-tooltip])").remove();
53103
53716
  const { words, cloudOptions } = parsed;
53104
53717
  const title = parsed.noTitle ? null : parsed.title;
53105
53718
  if (words.length === 0) {
53106
- resolve2();
53719
+ resolve();
53107
53720
  return;
53108
53721
  }
53109
53722
  const width = exportDims?.width ?? container.clientWidth;
53110
53723
  const height = exportDims?.height ?? container.clientHeight;
53111
53724
  if (width <= 0 || height <= 0) {
53112
- resolve2();
53725
+ resolve();
53113
53726
  return;
53114
53727
  }
53115
53728
  const titleHeight = title ? 40 : 0;
@@ -53138,7 +53751,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
53138
53751
  "transform",
53139
53752
  (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`
53140
53753
  ).text((d) => d.text);
53141
- resolve2();
53754
+ resolve();
53142
53755
  }).start();
53143
53756
  });
53144
53757
  }