@diagrammo/dgmo 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +919 -298
- package/dist/advanced.d.cts +148 -54
- package/dist/advanced.d.ts +148 -54
- package/dist/advanced.js +922 -300
- package/dist/auto.cjs +904 -297
- package/dist/auto.js +117 -117
- package/dist/auto.mjs +909 -299
- package/dist/cli.cjs +159 -159
- package/dist/index.cjs +903 -296
- package/dist/index.js +908 -298
- package/dist/internal.cjs +919 -298
- package/dist/internal.d.cts +148 -54
- package/dist/internal.d.ts +148 -54
- package/dist/internal.js +922 -300
- package/dist/map-data/PROVENANCE.json +1 -1
- package/dist/map-data/lakes.json +1 -0
- package/dist/map-data/na-lakes.json +1 -0
- package/dist/map-data/na-land.json +1 -0
- package/dist/map-data/rivers.json +1 -0
- package/docs/language-reference.md +12 -7
- package/gallery/fixtures/map-region-scope.dgmo +15 -0
- package/package.json +4 -4
- package/src/advanced.ts +6 -2
- package/src/c4/parser.ts +6 -6
- package/src/completion.ts +6 -2
- package/src/echarts.ts +1 -1
- package/src/infra/parser.ts +10 -10
- package/src/journey-map/parser.ts +1 -1
- package/src/label-layout.ts +36 -0
- package/src/map/data/PROVENANCE.json +1 -1
- package/src/map/data/README.md +2 -0
- package/src/map/data/lakes.json +1 -0
- package/src/map/data/na-lakes.json +1 -0
- package/src/map/data/na-land.json +1 -0
- package/src/map/data/rivers.json +1 -0
- package/src/map/layout.ts +1022 -205
- package/src/map/load-data.ts +29 -2
- package/src/map/parser.ts +22 -13
- package/src/map/renderer.ts +200 -219
- package/src/map/resolved-types.ts +18 -1
- package/src/map/resolver.ts +79 -7
- package/src/map/types.ts +4 -0
- package/src/mindmap/parser.ts +1 -1
- package/src/sitemap/parser.ts +1 -1
- package/src/utils/legend-d3.ts +42 -0
- package/src/utils/legend-layout.ts +83 -3
- package/src/utils/legend-svg.ts +1 -8
- package/src/utils/legend-types.ts +44 -1
package/dist/index.js
CHANGED
|
@@ -51,6 +51,33 @@ function rectCircleOverlap(rect, circle) {
|
|
|
51
51
|
const dy = nearestY - circle.cy;
|
|
52
52
|
return dx * dx + dy * dy < circle.r * circle.r;
|
|
53
53
|
}
|
|
54
|
+
function segmentRectOverlap(x0, y0, x1, y1, rect) {
|
|
55
|
+
const dx = x1 - x0;
|
|
56
|
+
const dy = y1 - y0;
|
|
57
|
+
let t0 = 0;
|
|
58
|
+
let t1 = 1;
|
|
59
|
+
const edges = [
|
|
60
|
+
[-dx, x0 - rect.x],
|
|
61
|
+
[dx, rect.x + rect.w - x0],
|
|
62
|
+
[-dy, y0 - rect.y],
|
|
63
|
+
[dy, rect.y + rect.h - y0]
|
|
64
|
+
];
|
|
65
|
+
for (const [p, q] of edges) {
|
|
66
|
+
if (p === 0) {
|
|
67
|
+
if (q < 0) return false;
|
|
68
|
+
} else {
|
|
69
|
+
const t = q / p;
|
|
70
|
+
if (p < 0) {
|
|
71
|
+
if (t > t1) return false;
|
|
72
|
+
if (t > t0) t0 = t;
|
|
73
|
+
} else {
|
|
74
|
+
if (t < t0) return false;
|
|
75
|
+
if (t < t1) t1 = t;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
54
81
|
function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius, fontSize) {
|
|
55
82
|
const labelHeight = fontSize + 4;
|
|
56
83
|
const stepSize = labelHeight + 2;
|
|
@@ -3254,6 +3281,57 @@ var init_legend_constants = __esm({
|
|
|
3254
3281
|
});
|
|
3255
3282
|
|
|
3256
3283
|
// src/utils/legend-layout.ts
|
|
3284
|
+
function fmtRamp(n) {
|
|
3285
|
+
return Number.isInteger(n) ? String(n) : String(Math.round(n * 10) / 10);
|
|
3286
|
+
}
|
|
3287
|
+
function gradientCapsuleWidth(name, gradient) {
|
|
3288
|
+
const pw = pillWidth(name);
|
|
3289
|
+
const minW = measureLegendText(fmtRamp(gradient.min), LEGEND_ENTRY_FONT_SIZE);
|
|
3290
|
+
const maxW = measureLegendText(fmtRamp(gradient.max), LEGEND_ENTRY_FONT_SIZE);
|
|
3291
|
+
return LEGEND_CAPSULE_PAD + pw + 4 + minW + RAMP_LABEL_GAP + RAMP_LEGEND_W + RAMP_LABEL_GAP + maxW + LEGEND_CAPSULE_PAD;
|
|
3292
|
+
}
|
|
3293
|
+
function buildGradientCapsuleLayout(group, gradient) {
|
|
3294
|
+
const pw = pillWidth(group.name);
|
|
3295
|
+
const minText = fmtRamp(gradient.min);
|
|
3296
|
+
const maxText = fmtRamp(gradient.max);
|
|
3297
|
+
const minW = measureLegendText(minText, LEGEND_ENTRY_FONT_SIZE);
|
|
3298
|
+
const gx = LEGEND_CAPSULE_PAD + pw + 4;
|
|
3299
|
+
const minX = gx;
|
|
3300
|
+
const rampX = gx + minW + RAMP_LABEL_GAP;
|
|
3301
|
+
const maxX = rampX + RAMP_LEGEND_W + RAMP_LABEL_GAP;
|
|
3302
|
+
const width = gradientCapsuleWidth(group.name, gradient);
|
|
3303
|
+
return {
|
|
3304
|
+
groupName: group.name,
|
|
3305
|
+
x: 0,
|
|
3306
|
+
y: 0,
|
|
3307
|
+
width,
|
|
3308
|
+
height: LEGEND_HEIGHT,
|
|
3309
|
+
pill: {
|
|
3310
|
+
groupName: group.name,
|
|
3311
|
+
x: LEGEND_CAPSULE_PAD,
|
|
3312
|
+
y: LEGEND_CAPSULE_PAD,
|
|
3313
|
+
width: pw,
|
|
3314
|
+
height: LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2,
|
|
3315
|
+
isActive: true
|
|
3316
|
+
},
|
|
3317
|
+
entries: [],
|
|
3318
|
+
gradient: {
|
|
3319
|
+
rampX,
|
|
3320
|
+
rampY: (LEGEND_HEIGHT - RAMP_LEGEND_H) / 2,
|
|
3321
|
+
rampW: RAMP_LEGEND_W,
|
|
3322
|
+
rampH: RAMP_LEGEND_H,
|
|
3323
|
+
min: gradient.min,
|
|
3324
|
+
max: gradient.max,
|
|
3325
|
+
minText,
|
|
3326
|
+
minX,
|
|
3327
|
+
maxText,
|
|
3328
|
+
maxX,
|
|
3329
|
+
textY: LEGEND_HEIGHT / 2,
|
|
3330
|
+
hue: gradient.hue,
|
|
3331
|
+
base: gradient.base
|
|
3332
|
+
}
|
|
3333
|
+
};
|
|
3334
|
+
}
|
|
3257
3335
|
function pillWidth(name) {
|
|
3258
3336
|
return measureLegendText(name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
|
|
3259
3337
|
}
|
|
@@ -3396,7 +3474,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3396
3474
|
};
|
|
3397
3475
|
}
|
|
3398
3476
|
const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
|
|
3399
|
-
const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0);
|
|
3477
|
+
const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
|
|
3400
3478
|
if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
|
|
3401
3479
|
return {
|
|
3402
3480
|
height: 0,
|
|
@@ -3475,7 +3553,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3475
3553
|
groupAvailW,
|
|
3476
3554
|
config.capsulePillAddonWidth ?? 0
|
|
3477
3555
|
);
|
|
3478
|
-
} else if (!activeGroupName) {
|
|
3556
|
+
} else if (!activeGroupName || config.showInactivePills) {
|
|
3479
3557
|
const pw = pillWidth(g.name);
|
|
3480
3558
|
pills.push({
|
|
3481
3559
|
groupName: g.name,
|
|
@@ -3513,6 +3591,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3513
3591
|
};
|
|
3514
3592
|
}
|
|
3515
3593
|
function buildCapsuleLayout(group, containerWidth, addonWidth = 0) {
|
|
3594
|
+
if (group.gradient) return buildGradientCapsuleLayout(group, group.gradient);
|
|
3516
3595
|
const pw = pillWidth(group.name);
|
|
3517
3596
|
const info = capsuleWidth(
|
|
3518
3597
|
group.name,
|
|
@@ -3683,7 +3762,7 @@ function getMaxLegendReservedHeight(config, containerWidth) {
|
|
|
3683
3762
|
}
|
|
3684
3763
|
return max;
|
|
3685
3764
|
}
|
|
3686
|
-
var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP;
|
|
3765
|
+
var CONTROL_PILL_PAD, CONTROL_FONT_SIZE, CONTROL_ICON_GAP, CONTROL_GAP, RAMP_LEGEND_W, RAMP_LEGEND_H, RAMP_LABEL_GAP;
|
|
3687
3766
|
var init_legend_layout = __esm({
|
|
3688
3767
|
"src/utils/legend-layout.ts"() {
|
|
3689
3768
|
"use strict";
|
|
@@ -3692,6 +3771,9 @@ var init_legend_layout = __esm({
|
|
|
3692
3771
|
CONTROL_FONT_SIZE = 11;
|
|
3693
3772
|
CONTROL_ICON_GAP = 4;
|
|
3694
3773
|
CONTROL_GAP = 8;
|
|
3774
|
+
RAMP_LEGEND_W = 80;
|
|
3775
|
+
RAMP_LEGEND_H = 8;
|
|
3776
|
+
RAMP_LABEL_GAP = 6;
|
|
3695
3777
|
}
|
|
3696
3778
|
});
|
|
3697
3779
|
|
|
@@ -3779,6 +3861,16 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, _isDark, c
|
|
|
3779
3861
|
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);
|
|
3780
3862
|
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);
|
|
3781
3863
|
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);
|
|
3864
|
+
if (capsule.gradient) {
|
|
3865
|
+
const gr = capsule.gradient;
|
|
3866
|
+
const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
3867
|
+
const def = g.append("defs").append("linearGradient").attr("id", gradId);
|
|
3868
|
+
def.append("stop").attr("offset", "0%").attr("stop-color", mix(gr.hue, gr.base, 15));
|
|
3869
|
+
def.append("stop").attr("offset", "100%").attr("stop-color", gr.hue);
|
|
3870
|
+
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);
|
|
3871
|
+
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})`);
|
|
3872
|
+
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);
|
|
3873
|
+
}
|
|
3782
3874
|
for (const entry of capsule.entries) {
|
|
3783
3875
|
const entryG = g.append("g").attr("data-legend-entry", entry.value.toLowerCase()).attr("data-series-name", entry.value).style("cursor", "pointer");
|
|
3784
3876
|
entryG.append("circle").attr("cx", entry.dotCx).attr("cy", entry.dotCy).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
|
|
@@ -11270,23 +11362,22 @@ function parseC4(content, palette) {
|
|
|
11270
11362
|
}
|
|
11271
11363
|
}
|
|
11272
11364
|
const parent = findParentElement(indent, stack);
|
|
11273
|
-
|
|
11274
|
-
|
|
11365
|
+
const descResult = tryStripDescriptionKeyword(trimmed);
|
|
11366
|
+
if (parent && descResult.isKeyword) {
|
|
11275
11367
|
if (descResult.needsColon) {
|
|
11276
11368
|
pushError(
|
|
11277
11369
|
lineNumber,
|
|
11278
|
-
`Use "description: ${descResult.text}" \u2014
|
|
11370
|
+
`Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`,
|
|
11279
11371
|
"warning"
|
|
11280
11372
|
);
|
|
11281
11373
|
}
|
|
11282
|
-
const descText = descResult.isKeyword ? descResult.text : trimmed;
|
|
11283
11374
|
let desc = elementDescription.get(parent.element);
|
|
11284
11375
|
if (!desc) {
|
|
11285
11376
|
desc = [];
|
|
11286
11377
|
elementDescription.set(parent.element, desc);
|
|
11287
11378
|
parent.element.description = desc;
|
|
11288
11379
|
}
|
|
11289
|
-
desc.push(
|
|
11380
|
+
desc.push(descResult.text);
|
|
11290
11381
|
} else {
|
|
11291
11382
|
pushError(lineNumber, `Unexpected content: "${trimmed}"`);
|
|
11292
11383
|
}
|
|
@@ -11753,7 +11844,7 @@ function parseSitemap(content, palette) {
|
|
|
11753
11844
|
if (descResult.needsColon) {
|
|
11754
11845
|
pushWarning(
|
|
11755
11846
|
lineNumber,
|
|
11756
|
-
`Use "description: ${descResult.text}" \u2014
|
|
11847
|
+
`Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
|
|
11757
11848
|
);
|
|
11758
11849
|
}
|
|
11759
11850
|
const parent = findParentNode(indent, indentStack);
|
|
@@ -12581,23 +12672,22 @@ function parseInfra(content) {
|
|
|
12581
12672
|
}
|
|
12582
12673
|
}
|
|
12583
12674
|
const descResult = tryStripDescriptionKeyword(trimmed);
|
|
12584
|
-
if (descResult.isKeyword
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12675
|
+
if (descResult.isKeyword) {
|
|
12676
|
+
if (currentNode.isEdge) {
|
|
12677
|
+
continue;
|
|
12678
|
+
}
|
|
12588
12679
|
if (descResult.needsColon) {
|
|
12589
12680
|
warn(
|
|
12590
12681
|
lineNumber,
|
|
12591
|
-
`Use "description: ${descResult.text}" \u2014
|
|
12682
|
+
`Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
|
|
12592
12683
|
);
|
|
12593
12684
|
}
|
|
12594
|
-
|
|
12595
|
-
pushDescription(currentNode, descText);
|
|
12685
|
+
pushDescription(currentNode, descResult.text);
|
|
12596
12686
|
continue;
|
|
12597
12687
|
}
|
|
12598
12688
|
warn(
|
|
12599
12689
|
lineNumber,
|
|
12600
|
-
`Unexpected line inside component '${currentNode.label}'. Expected a property (key: value), connection (-> Target), or description text.`
|
|
12690
|
+
`Unexpected line inside component '${currentNode.label}'. Expected a property (key: value), connection (-> Target), or a description (description: text).`
|
|
12601
12691
|
);
|
|
12602
12692
|
continue;
|
|
12603
12693
|
}
|
|
@@ -15734,9 +15824,6 @@ function parseMap(content) {
|
|
|
15734
15824
|
const pushWarning = (line12, message) => {
|
|
15735
15825
|
diagnostics.push(makeDgmoError(line12, message, "warning"));
|
|
15736
15826
|
};
|
|
15737
|
-
const pushInfo = (line12, message) => {
|
|
15738
|
-
diagnostics.push(makeDgmoError(line12, message, "warning"));
|
|
15739
|
-
};
|
|
15740
15827
|
const lines = content.split("\n");
|
|
15741
15828
|
let firstIdx = 0;
|
|
15742
15829
|
while (firstIdx < lines.length) {
|
|
@@ -15866,10 +15953,15 @@ function parseMap(content) {
|
|
|
15866
15953
|
break;
|
|
15867
15954
|
case "projection":
|
|
15868
15955
|
dup(d.projection);
|
|
15869
|
-
if (value && ![
|
|
15956
|
+
if (value && ![
|
|
15957
|
+
"equirectangular",
|
|
15958
|
+
"natural-earth",
|
|
15959
|
+
"albers-usa",
|
|
15960
|
+
"mercator"
|
|
15961
|
+
].includes(value))
|
|
15870
15962
|
pushWarning(
|
|
15871
15963
|
line12,
|
|
15872
|
-
`Unknown projection "${value}" (expected natural-earth | albers-usa | mercator).`
|
|
15964
|
+
`Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
|
|
15873
15965
|
);
|
|
15874
15966
|
d.projection = value;
|
|
15875
15967
|
break;
|
|
@@ -16008,17 +16100,21 @@ function parseMap(content) {
|
|
|
16008
16100
|
scoreNum = void 0;
|
|
16009
16101
|
}
|
|
16010
16102
|
}
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16103
|
+
let regionName = split.name;
|
|
16104
|
+
let regionScope;
|
|
16105
|
+
const rToks = regionName.split(/\s+/);
|
|
16106
|
+
const rLast = rToks[rToks.length - 1];
|
|
16107
|
+
if (rToks.length > 1 && SCOPE_RE.test(rLast)) {
|
|
16108
|
+
regionName = rToks.slice(0, -1).join(" ");
|
|
16109
|
+
regionScope = rLast;
|
|
16110
|
+
}
|
|
16016
16111
|
const region = {
|
|
16017
|
-
name:
|
|
16112
|
+
name: regionName,
|
|
16018
16113
|
tags,
|
|
16019
16114
|
meta,
|
|
16020
16115
|
lineNumber: line12
|
|
16021
16116
|
};
|
|
16117
|
+
if (regionScope !== void 0) region.scope = regionScope;
|
|
16022
16118
|
if (scoreNum !== void 0) region.score = scoreNum;
|
|
16023
16119
|
regions.push(region);
|
|
16024
16120
|
}
|
|
@@ -17142,7 +17238,7 @@ function parseMindmap(content, palette) {
|
|
|
17142
17238
|
if (descResult.needsColon) {
|
|
17143
17239
|
pushWarning(
|
|
17144
17240
|
lineNumber,
|
|
17145
|
-
`Use "description: ${descResult.text}" \u2014
|
|
17241
|
+
`Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
|
|
17146
17242
|
);
|
|
17147
17243
|
}
|
|
17148
17244
|
const parent = findMetadataParent2(indent, indentStack);
|
|
@@ -18930,7 +19026,7 @@ function parseJourneyMap(content, palette) {
|
|
|
18930
19026
|
if (descResult.needsColon) {
|
|
18931
19027
|
warn(
|
|
18932
19028
|
lineNumber,
|
|
18933
|
-
`Use "description: ${descResult.text}" \u2014
|
|
19029
|
+
`Use "description: ${descResult.text}" \u2014 bare "description" is deprecated.`
|
|
18934
19030
|
);
|
|
18935
19031
|
}
|
|
18936
19032
|
currentStep.description = descResult.text;
|
|
@@ -45519,7 +45615,9 @@ function resolveMap(parsed, data) {
|
|
|
45519
45615
|
const usScoped = parsed.directives.region === "us-states" || parsed.directives.defaultCountry?.toUpperCase() === "US" || parsed.regions.some((r) => {
|
|
45520
45616
|
const f = fold(r.name);
|
|
45521
45617
|
return usStateIndex.has(f) && !countryIndex.has(f);
|
|
45522
|
-
}) || parsed.
|
|
45618
|
+
}) || parsed.regions.some(
|
|
45619
|
+
(r) => r.scope === "US" || r.scope?.startsWith("US-")
|
|
45620
|
+
) || parsed.pois.some(
|
|
45523
45621
|
(p) => p.pos.kind === "name" && p.pos.scope?.startsWith("US-")
|
|
45524
45622
|
);
|
|
45525
45623
|
const regions = [];
|
|
@@ -45531,7 +45629,30 @@ function resolveMap(parsed, data) {
|
|
|
45531
45629
|
const inCountry = countryIndex.get(f) ?? (REGION_ALIASES[f] ? countryIndex.get(REGION_ALIASES[f]) : void 0);
|
|
45532
45630
|
const inState = usStateIndex.get(f);
|
|
45533
45631
|
let chosen = null;
|
|
45534
|
-
|
|
45632
|
+
const scope = r.scope;
|
|
45633
|
+
if (scope) {
|
|
45634
|
+
const wantsState = scope === "US" || scope.startsWith("US-");
|
|
45635
|
+
if (wantsState && inState) {
|
|
45636
|
+
if (scope.startsWith("US-") && inState.id !== scope) {
|
|
45637
|
+
err(
|
|
45638
|
+
r.lineNumber,
|
|
45639
|
+
`No subdivision "${r.name}" in scope ${scope} (it is ${inState.id}).`,
|
|
45640
|
+
"E_MAP_SCOPE_MISS"
|
|
45641
|
+
);
|
|
45642
|
+
continue;
|
|
45643
|
+
}
|
|
45644
|
+
chosen = { ...inState, layer: "us-state" };
|
|
45645
|
+
} else if (!wantsState && inCountry) {
|
|
45646
|
+
chosen = { ...inCountry, layer: "country" };
|
|
45647
|
+
} else {
|
|
45648
|
+
err(
|
|
45649
|
+
r.lineNumber,
|
|
45650
|
+
`No region "${r.name}" found in scope ${scope}.`,
|
|
45651
|
+
"E_MAP_SCOPE_MISS"
|
|
45652
|
+
);
|
|
45653
|
+
continue;
|
|
45654
|
+
}
|
|
45655
|
+
} else if (inCountry && inState) {
|
|
45535
45656
|
if (usScoped) {
|
|
45536
45657
|
chosen = { ...inState, layer: "us-state" };
|
|
45537
45658
|
} else {
|
|
@@ -45539,7 +45660,7 @@ function resolveMap(parsed, data) {
|
|
|
45539
45660
|
}
|
|
45540
45661
|
warn(
|
|
45541
45662
|
r.lineNumber,
|
|
45542
|
-
`"${r.name}" is both a country and a US state \u2014 resolved as ${chosen.layer} (${chosen.id}).`,
|
|
45663
|
+
`"${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}").`,
|
|
45543
45664
|
"W_MAP_REGION_AMBIGUOUS"
|
|
45544
45665
|
);
|
|
45545
45666
|
} else if (inState) {
|
|
@@ -45711,9 +45832,17 @@ function resolveMap(parsed, data) {
|
|
|
45711
45832
|
};
|
|
45712
45833
|
registerPoi(id, poi, p.lineNumber);
|
|
45713
45834
|
}
|
|
45835
|
+
const declaredByName = /* @__PURE__ */ new Map();
|
|
45836
|
+
for (const p of pois) {
|
|
45837
|
+
const fn = p.name ? fold(p.name) : void 0;
|
|
45838
|
+
if (fn && fn !== p.id && !declaredByName.has(fn))
|
|
45839
|
+
declaredByName.set(fn, p.id);
|
|
45840
|
+
}
|
|
45714
45841
|
const resolveEndpoint2 = (ref, line12) => {
|
|
45715
45842
|
const f = fold(ref);
|
|
45716
45843
|
if (registry.has(f)) return f;
|
|
45844
|
+
const aliased = declaredByName.get(f);
|
|
45845
|
+
if (aliased) return aliased;
|
|
45717
45846
|
const got = lookupName(ref, void 0, line12, inferredCountry, true);
|
|
45718
45847
|
if (got.kind !== "ok") return null;
|
|
45719
45848
|
noteCountry(got.iso);
|
|
@@ -45793,23 +45922,29 @@ function resolveMap(parsed, data) {
|
|
|
45793
45922
|
[-180, -85],
|
|
45794
45923
|
[180, 85]
|
|
45795
45924
|
];
|
|
45796
|
-
|
|
45925
|
+
let extent2 = unioned ? pad(unioned, PAD_FRACTION) : DEFAULT_EXTENT;
|
|
45797
45926
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
45798
45927
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
45799
45928
|
const span = Math.max(lonSpan, latSpan);
|
|
45800
45929
|
const usDominant = (inferredCountry === "US" || subdivisions.includes("us-states")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
45801
45930
|
let projection;
|
|
45802
45931
|
const override = parsed.directives.projection;
|
|
45803
|
-
if (override === "natural-earth" || override === "albers-usa" || override === "mercator") {
|
|
45932
|
+
if (override === "equirectangular" || override === "natural-earth" || override === "albers-usa" || override === "mercator") {
|
|
45804
45933
|
projection = override;
|
|
45805
45934
|
} else if (usDominant) {
|
|
45806
45935
|
projection = "albers-usa";
|
|
45807
45936
|
} else if (span > WORLD_SPAN) {
|
|
45808
|
-
projection = "
|
|
45937
|
+
projection = "equirectangular";
|
|
45809
45938
|
} else if (span < MERCATOR_MAX_SPAN) {
|
|
45810
45939
|
projection = "mercator";
|
|
45811
45940
|
} else {
|
|
45812
|
-
projection = "
|
|
45941
|
+
projection = "equirectangular";
|
|
45942
|
+
}
|
|
45943
|
+
if (lonSpan >= 180) {
|
|
45944
|
+
extent2 = [
|
|
45945
|
+
[-180, Math.min(extent2[0][1], WORLD_LAT_SOUTH)],
|
|
45946
|
+
[180, Math.max(extent2[1][1], WORLD_LAT_NORTH)]
|
|
45947
|
+
];
|
|
45813
45948
|
}
|
|
45814
45949
|
result.regions = regions;
|
|
45815
45950
|
result.pois = pois;
|
|
@@ -45857,7 +45992,7 @@ function firstError(diags) {
|
|
|
45857
45992
|
const e = diags.find((d) => d.severity === "error");
|
|
45858
45993
|
return e ? formatDgmoError(e) : null;
|
|
45859
45994
|
}
|
|
45860
|
-
var WORLD_SPAN, MERCATOR_MAX_SPAN, PAD_FRACTION, REGION_ALIASES;
|
|
45995
|
+
var WORLD_SPAN, MERCATOR_MAX_SPAN, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES;
|
|
45861
45996
|
var init_resolver2 = __esm({
|
|
45862
45997
|
"src/map/resolver.ts"() {
|
|
45863
45998
|
"use strict";
|
|
@@ -45866,6 +46001,8 @@ var init_resolver2 = __esm({
|
|
|
45866
46001
|
WORLD_SPAN = 90;
|
|
45867
46002
|
MERCATOR_MAX_SPAN = 25;
|
|
45868
46003
|
PAD_FRACTION = 0.05;
|
|
46004
|
+
WORLD_LAT_SOUTH = -58;
|
|
46005
|
+
WORLD_LAT_NORTH = 78;
|
|
45869
46006
|
REGION_ALIASES = {
|
|
45870
46007
|
// Common everyday names → the Natural-Earth display name actually shipped.
|
|
45871
46008
|
"united states": "united states of america",
|
|
@@ -45933,13 +46070,36 @@ function moduleBaseDir() {
|
|
|
45933
46070
|
function loadMapData() {
|
|
45934
46071
|
cache ??= (async () => {
|
|
45935
46072
|
const dir = await firstExistingDir(moduleBaseDir());
|
|
45936
|
-
const [
|
|
46073
|
+
const [
|
|
46074
|
+
worldCoarse,
|
|
46075
|
+
worldDetail,
|
|
46076
|
+
usStates,
|
|
46077
|
+
lakes,
|
|
46078
|
+
rivers,
|
|
46079
|
+
naLand,
|
|
46080
|
+
naLakes,
|
|
46081
|
+
gazetteer
|
|
46082
|
+
] = await Promise.all([
|
|
45937
46083
|
readJson(dir, FILES.worldCoarse),
|
|
45938
46084
|
readJson(dir, FILES.worldDetail),
|
|
45939
46085
|
readJson(dir, FILES.usStates),
|
|
46086
|
+
// Lakes/rivers/NA assets are optional — older bundles may predate them.
|
|
46087
|
+
readJson(dir, FILES.lakes).catch(() => void 0),
|
|
46088
|
+
readJson(dir, FILES.rivers).catch(() => void 0),
|
|
46089
|
+
readJson(dir, FILES.naLand).catch(() => void 0),
|
|
46090
|
+
readJson(dir, FILES.naLakes).catch(() => void 0),
|
|
45940
46091
|
readJson(dir, FILES.gazetteer)
|
|
45941
46092
|
]);
|
|
45942
|
-
return validate({
|
|
46093
|
+
return validate({
|
|
46094
|
+
worldCoarse,
|
|
46095
|
+
worldDetail,
|
|
46096
|
+
usStates,
|
|
46097
|
+
gazetteer,
|
|
46098
|
+
...lakes && { lakes },
|
|
46099
|
+
...rivers && { rivers },
|
|
46100
|
+
...naLand && { naLand },
|
|
46101
|
+
...naLakes && { naLakes }
|
|
46102
|
+
});
|
|
45943
46103
|
})().catch((e) => {
|
|
45944
46104
|
cache = void 0;
|
|
45945
46105
|
throw e;
|
|
@@ -45954,6 +46114,10 @@ var init_load_data = __esm({
|
|
|
45954
46114
|
worldCoarse: "world-coarse.json",
|
|
45955
46115
|
worldDetail: "world-detail.json",
|
|
45956
46116
|
usStates: "us-states.json",
|
|
46117
|
+
lakes: "lakes.json",
|
|
46118
|
+
rivers: "rivers.json",
|
|
46119
|
+
naLand: "na-land.json",
|
|
46120
|
+
naLakes: "na-lakes.json",
|
|
45957
46121
|
gazetteer: "gazetteer.json"
|
|
45958
46122
|
};
|
|
45959
46123
|
CANDIDATE_DIRS = [
|
|
@@ -45969,8 +46133,11 @@ var init_load_data = __esm({
|
|
|
45969
46133
|
import {
|
|
45970
46134
|
geoPath,
|
|
45971
46135
|
geoNaturalEarth1,
|
|
45972
|
-
|
|
45973
|
-
|
|
46136
|
+
geoEquirectangular,
|
|
46137
|
+
geoConicEqualArea,
|
|
46138
|
+
geoMercator,
|
|
46139
|
+
geoBounds as geoBounds2,
|
|
46140
|
+
geoTransform
|
|
45974
46141
|
} from "d3-geo";
|
|
45975
46142
|
import { feature as feature2 } from "topojson-client";
|
|
45976
46143
|
function geomObject2(topo) {
|
|
@@ -45988,36 +46155,67 @@ function decodeLayer(topo) {
|
|
|
45988
46155
|
function projectionFor(family) {
|
|
45989
46156
|
switch (family) {
|
|
45990
46157
|
case "albers-usa":
|
|
45991
|
-
return
|
|
46158
|
+
return usConusProjection();
|
|
45992
46159
|
case "mercator":
|
|
45993
46160
|
return geoMercator();
|
|
45994
46161
|
case "natural-earth":
|
|
45995
|
-
default:
|
|
45996
46162
|
return geoNaturalEarth1();
|
|
46163
|
+
case "equirectangular":
|
|
46164
|
+
default:
|
|
46165
|
+
return geoEquirectangular();
|
|
45997
46166
|
}
|
|
45998
46167
|
}
|
|
46168
|
+
function mapBackgroundColor(palette) {
|
|
46169
|
+
return mix(palette.colors.blue, palette.bg, WATER_TINT);
|
|
46170
|
+
}
|
|
45999
46171
|
function layoutMap(resolved, data, size, opts) {
|
|
46000
46172
|
const { palette, isDark } = opts;
|
|
46001
46173
|
const { width, height } = size;
|
|
46002
|
-
const
|
|
46174
|
+
const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
|
|
46175
|
+
const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
|
|
46176
|
+
const worldTopo = usCrisp ? data.naLand : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
|
|
46003
46177
|
const worldLayer = decodeLayer(worldTopo);
|
|
46004
|
-
const usLayer =
|
|
46005
|
-
const
|
|
46006
|
-
const
|
|
46178
|
+
const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
|
|
46179
|
+
const landTint = isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT;
|
|
46180
|
+
const neutralFill = mix(palette.colors.green, palette.bg, landTint);
|
|
46181
|
+
const water = mapBackgroundColor(palette);
|
|
46182
|
+
const usContext = usLayer !== null;
|
|
46183
|
+
const foreignFill = mix(
|
|
46184
|
+
palette.colors.gray,
|
|
46185
|
+
palette.bg,
|
|
46186
|
+
isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
46187
|
+
);
|
|
46188
|
+
const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
|
|
46007
46189
|
const scores = resolved.regions.filter((r) => r.score !== void 0).map((r) => r.score);
|
|
46008
46190
|
const scaleOverride = resolved.directives.scale;
|
|
46009
46191
|
const rampMin = scaleOverride ? scaleOverride.min : Math.min(...scores);
|
|
46010
46192
|
const rampMax = scaleOverride ? scaleOverride.max : Math.max(...scores);
|
|
46011
|
-
const rampHue = palette.
|
|
46193
|
+
const rampHue = palette.colors.red;
|
|
46012
46194
|
const hasRamp = scores.length > 0;
|
|
46013
|
-
const
|
|
46014
|
-
|
|
46015
|
-
|
|
46016
|
-
|
|
46195
|
+
const SCORE_NAME = hasRamp ? resolved.directives.metric?.trim() || "Score" : null;
|
|
46196
|
+
const matchColorGroup = (v) => {
|
|
46197
|
+
const lv = v.trim().toLowerCase();
|
|
46198
|
+
if (lv === "none") return null;
|
|
46199
|
+
if (SCORE_NAME && (lv === "score" || lv === SCORE_NAME.toLowerCase()))
|
|
46200
|
+
return SCORE_NAME;
|
|
46201
|
+
const tg = resolved.tagGroups.find((g) => g.name.toLowerCase() === lv);
|
|
46202
|
+
return tg ? tg.name : v;
|
|
46203
|
+
};
|
|
46204
|
+
const override = opts.activeGroup;
|
|
46205
|
+
let activeGroup;
|
|
46206
|
+
if (override !== void 0) {
|
|
46207
|
+
activeGroup = override === null ? null : matchColorGroup(override);
|
|
46208
|
+
} else if (resolved.directives.activeTag !== void 0) {
|
|
46209
|
+
activeGroup = matchColorGroup(resolved.directives.activeTag);
|
|
46210
|
+
} else {
|
|
46211
|
+
activeGroup = SCORE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
|
|
46212
|
+
}
|
|
46213
|
+
const activeIsScore = SCORE_NAME !== null && activeGroup === SCORE_NAME;
|
|
46214
|
+
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
46017
46215
|
const fillForScore = (s) => {
|
|
46018
46216
|
const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
|
|
46019
46217
|
const pct = RAMP_FLOOR + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR);
|
|
46020
|
-
return mix(rampHue,
|
|
46218
|
+
return mix(rampHue, rampBase, pct);
|
|
46021
46219
|
};
|
|
46022
46220
|
const tagFill = (tags, groupName) => {
|
|
46023
46221
|
if (!groupName) return null;
|
|
@@ -46031,40 +46229,40 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46031
46229
|
(e) => e.value.toLowerCase() === val.toLowerCase()
|
|
46032
46230
|
);
|
|
46033
46231
|
if (!entry?.color) return null;
|
|
46034
|
-
return
|
|
46232
|
+
return mix(
|
|
46233
|
+
entry.color,
|
|
46234
|
+
palette.bg,
|
|
46235
|
+
isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT
|
|
46236
|
+
);
|
|
46237
|
+
};
|
|
46238
|
+
const regionFill = (r) => {
|
|
46239
|
+
if (activeIsScore) {
|
|
46240
|
+
return r.score !== void 0 ? fillForScore(r.score) : neutralFill;
|
|
46241
|
+
}
|
|
46242
|
+
return tagFill(r.tags, activeGroup) ?? neutralFill;
|
|
46035
46243
|
};
|
|
46036
46244
|
const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
|
|
46037
|
-
const
|
|
46038
|
-
for (const r of resolved.regions) {
|
|
46039
|
-
const f = r.layer === "us-state" ? usLayer?.get(r.iso) : worldLayer.get(r.iso);
|
|
46040
|
-
if (f) regionFeatures.push(f);
|
|
46041
|
-
}
|
|
46042
|
-
const extentCorners = () => {
|
|
46245
|
+
const extentOutline = () => {
|
|
46043
46246
|
const [[w, s], [e, n]] = resolved.extent;
|
|
46247
|
+
const N = 16;
|
|
46248
|
+
const coords = [];
|
|
46249
|
+
for (let i = 0; i <= N; i++) {
|
|
46250
|
+
const t = i / N;
|
|
46251
|
+
const lon = w + (e - w) * t;
|
|
46252
|
+
const lat = s + (n - s) * t;
|
|
46253
|
+
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
46254
|
+
}
|
|
46044
46255
|
return {
|
|
46045
46256
|
type: "Feature",
|
|
46046
46257
|
properties: {},
|
|
46047
|
-
geometry: {
|
|
46048
|
-
type: "MultiPoint",
|
|
46049
|
-
coordinates: [
|
|
46050
|
-
[w, s],
|
|
46051
|
-
[e, s],
|
|
46052
|
-
[e, n],
|
|
46053
|
-
[w, n]
|
|
46054
|
-
]
|
|
46055
|
-
}
|
|
46258
|
+
geometry: { type: "MultiPoint", coordinates: coords }
|
|
46056
46259
|
};
|
|
46057
46260
|
};
|
|
46058
46261
|
let fitFeatures;
|
|
46059
|
-
if (resolved.projection === "albers-usa") {
|
|
46060
|
-
|
|
46061
|
-
else if (usLayer) fitFeatures = [...usLayer.values()];
|
|
46062
|
-
else {
|
|
46063
|
-
const us = worldLayer.get("US");
|
|
46064
|
-
fitFeatures = us ? [us] : [...worldLayer.values()];
|
|
46065
|
-
}
|
|
46262
|
+
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46263
|
+
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
46066
46264
|
} else {
|
|
46067
|
-
fitFeatures = [
|
|
46265
|
+
fitFeatures = [extentOutline()];
|
|
46068
46266
|
}
|
|
46069
46267
|
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
46070
46268
|
const projection = projectionFor(resolved.projection);
|
|
@@ -46073,32 +46271,311 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46073
46271
|
if (centerLon > 180) centerLon -= 360;
|
|
46074
46272
|
projection.rotate([-centerLon, 0]);
|
|
46075
46273
|
}
|
|
46076
|
-
|
|
46274
|
+
const TITLE_GAP = 16;
|
|
46275
|
+
let topPad = FIT_PAD;
|
|
46276
|
+
if (resolved.title && resolved.pois.length > 0) {
|
|
46277
|
+
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
46278
|
+
topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP);
|
|
46279
|
+
}
|
|
46280
|
+
const fitBox = [
|
|
46281
|
+
[FIT_PAD, topPad],
|
|
46077
46282
|
[
|
|
46078
|
-
|
|
46079
|
-
|
|
46080
|
-
|
|
46081
|
-
|
|
46082
|
-
|
|
46083
|
-
|
|
46084
|
-
|
|
46085
|
-
|
|
46086
|
-
|
|
46087
|
-
|
|
46283
|
+
Math.max(FIT_PAD + 1, width - FIT_PAD),
|
|
46284
|
+
Math.max(topPad + 1, height - FIT_PAD)
|
|
46285
|
+
]
|
|
46286
|
+
];
|
|
46287
|
+
projection.fitExtent(fitBox, fitTarget);
|
|
46288
|
+
const fitGB = geoBounds2(fitTarget);
|
|
46289
|
+
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46290
|
+
let path;
|
|
46291
|
+
let project;
|
|
46292
|
+
if (fitIsGlobal) {
|
|
46293
|
+
const cb = geoPath(projection).bounds(fitTarget);
|
|
46294
|
+
const bx0 = cb[0][0];
|
|
46295
|
+
const by0 = cb[0][1];
|
|
46296
|
+
const cw = cb[1][0] - bx0;
|
|
46297
|
+
const ch = cb[1][1] - by0;
|
|
46298
|
+
const ox = fitBox[0][0];
|
|
46299
|
+
const oy = fitBox[0][1];
|
|
46300
|
+
const sx = cw > 0 ? (fitBox[1][0] - ox) / cw : 1;
|
|
46301
|
+
const sy = ch > 0 ? (fitBox[1][1] - oy) / ch : 1;
|
|
46302
|
+
const stretch = (x, y) => [
|
|
46303
|
+
ox + (x - bx0) * sx,
|
|
46304
|
+
oy + (y - by0) * sy
|
|
46305
|
+
];
|
|
46306
|
+
const baseProjection = projection;
|
|
46307
|
+
const tx = geoTransform({
|
|
46308
|
+
point(x, y) {
|
|
46309
|
+
const [px, py] = stretch(x, y);
|
|
46310
|
+
this.stream.point(px, py);
|
|
46311
|
+
}
|
|
46312
|
+
});
|
|
46313
|
+
path = geoPath({
|
|
46314
|
+
stream: (s) => baseProjection.stream(
|
|
46315
|
+
tx.stream(s)
|
|
46316
|
+
)
|
|
46317
|
+
});
|
|
46318
|
+
project = (lon, lat) => {
|
|
46319
|
+
const p = baseProjection([lon, lat]);
|
|
46320
|
+
return p ? stretch(p[0], p[1]) : null;
|
|
46321
|
+
};
|
|
46322
|
+
} else {
|
|
46323
|
+
path = geoPath(projection);
|
|
46324
|
+
project = (lon, lat) => projection([lon, lat]) ?? null;
|
|
46325
|
+
}
|
|
46326
|
+
const insets = [];
|
|
46327
|
+
const insetRegions = [];
|
|
46328
|
+
const insetLabelSeeds = [];
|
|
46329
|
+
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46330
|
+
const PAD = 8;
|
|
46331
|
+
const GAP = 12;
|
|
46332
|
+
const yB = height - FIT_PAD;
|
|
46333
|
+
const BW = 8;
|
|
46334
|
+
const coast = /* @__PURE__ */ new Map();
|
|
46335
|
+
const addPt = (lon, lat) => {
|
|
46336
|
+
const p = projection([lon, lat]);
|
|
46337
|
+
if (!p) return;
|
|
46338
|
+
const bi = Math.floor(p[0] / BW);
|
|
46339
|
+
const cur = coast.get(bi);
|
|
46340
|
+
if (cur === void 0 || p[1] > cur) coast.set(bi, p[1]);
|
|
46341
|
+
};
|
|
46342
|
+
const walk = (co) => {
|
|
46343
|
+
if (Array.isArray(co) && typeof co[0] === "number")
|
|
46344
|
+
addPt(co[0], co[1]);
|
|
46345
|
+
else if (Array.isArray(co)) for (const c of co) walk(c);
|
|
46346
|
+
};
|
|
46347
|
+
for (const [iso, f] of usLayer) {
|
|
46348
|
+
if (US_NON_CONUS.has(iso)) continue;
|
|
46349
|
+
walk(f.geometry.coordinates);
|
|
46350
|
+
}
|
|
46351
|
+
const at = (x) => {
|
|
46352
|
+
const bi = Math.floor(x / BW);
|
|
46353
|
+
let y = -Infinity;
|
|
46354
|
+
for (let k = bi - 1; k <= bi + 1; k++) {
|
|
46355
|
+
const v = coast.get(k);
|
|
46356
|
+
if (v !== void 0 && v > y) y = v;
|
|
46357
|
+
}
|
|
46358
|
+
return y;
|
|
46359
|
+
};
|
|
46360
|
+
const coastTop = (x0, xr) => {
|
|
46361
|
+
const n = 24;
|
|
46362
|
+
const pts = [];
|
|
46363
|
+
let maxY = -Infinity;
|
|
46364
|
+
for (let i = 0; i <= n; i++) {
|
|
46365
|
+
const x = x0 + (xr - x0) * i / n;
|
|
46366
|
+
const y = at(x);
|
|
46367
|
+
if (y > -Infinity) {
|
|
46368
|
+
pts.push([x, y]);
|
|
46369
|
+
if (y > maxY) maxY = y;
|
|
46370
|
+
}
|
|
46371
|
+
}
|
|
46372
|
+
if (pts.length === 0) return () => yB - height * 0.42;
|
|
46373
|
+
let m = 0;
|
|
46374
|
+
if (pts.length >= 2) {
|
|
46375
|
+
let sx = 0, sy = 0, sxx = 0, sxy = 0;
|
|
46376
|
+
for (const [x, y] of pts) {
|
|
46377
|
+
sx += x;
|
|
46378
|
+
sy += y;
|
|
46379
|
+
sxx += x * x;
|
|
46380
|
+
sxy += x * y;
|
|
46381
|
+
}
|
|
46382
|
+
const den = pts.length * sxx - sx * sx;
|
|
46383
|
+
if (den !== 0) m = (pts.length * sxy - sx * sy) / den;
|
|
46384
|
+
}
|
|
46385
|
+
m = Math.max(-0.35, Math.min(0.35, m));
|
|
46386
|
+
let c = -Infinity;
|
|
46387
|
+
for (const [x, y] of pts) {
|
|
46388
|
+
const need = y - m * x + GAP;
|
|
46389
|
+
if (need > c) c = need;
|
|
46390
|
+
}
|
|
46391
|
+
return (x) => m * x + c;
|
|
46392
|
+
};
|
|
46393
|
+
const placeInset = (iso, proj, boxX, iwReq) => {
|
|
46394
|
+
const f = usLayer.get(iso);
|
|
46395
|
+
if (!f) return boxX;
|
|
46396
|
+
const x0 = boxX;
|
|
46397
|
+
const iw = Math.min(iwReq, width - FIT_PAD - x0 - 2 * PAD);
|
|
46398
|
+
if (iw < 24) return boxX;
|
|
46399
|
+
const xr = x0 + iw + 2 * PAD;
|
|
46400
|
+
const top = coastTop(x0, xr);
|
|
46401
|
+
const yL = top(x0);
|
|
46402
|
+
const yR = top(xr);
|
|
46403
|
+
proj.fitWidth(iw, f);
|
|
46404
|
+
const bb = geoPath(proj).bounds(f);
|
|
46405
|
+
const sh = Number.isFinite(bb[0][0]) ? bb[1][1] - bb[0][1] : iw;
|
|
46406
|
+
const needH = sh + 2 * PAD;
|
|
46407
|
+
let topFit = Math.max(yL, yR);
|
|
46408
|
+
const bottom = Math.min(topFit + needH, yB);
|
|
46409
|
+
if (bottom - topFit < needH) topFit = bottom - needH;
|
|
46410
|
+
const lift = topFit - Math.max(yL, yR);
|
|
46411
|
+
const topL = yL + lift;
|
|
46412
|
+
const topR = yR + lift;
|
|
46413
|
+
proj.fitExtent(
|
|
46414
|
+
[
|
|
46415
|
+
[x0 + PAD, topFit + PAD],
|
|
46416
|
+
[xr - PAD, bottom - PAD]
|
|
46417
|
+
],
|
|
46418
|
+
f
|
|
46419
|
+
);
|
|
46420
|
+
const d = geoPath(proj)(f) ?? "";
|
|
46421
|
+
if (!d) return xr;
|
|
46422
|
+
const r = regionById.get(iso);
|
|
46423
|
+
let fill2 = neutralFill;
|
|
46424
|
+
let lineNumber = -1;
|
|
46425
|
+
if (r?.layer === "us-state") {
|
|
46426
|
+
fill2 = regionFill(r);
|
|
46427
|
+
lineNumber = r.lineNumber;
|
|
46428
|
+
}
|
|
46429
|
+
insets.push({
|
|
46430
|
+
x: x0,
|
|
46431
|
+
y: Math.min(topL, topR),
|
|
46432
|
+
w: xr - x0,
|
|
46433
|
+
h: bottom - Math.min(topL, topR),
|
|
46434
|
+
points: [
|
|
46435
|
+
[x0, topL],
|
|
46436
|
+
[xr, topR],
|
|
46437
|
+
[xr, bottom],
|
|
46438
|
+
[x0, bottom]
|
|
46439
|
+
]
|
|
46440
|
+
});
|
|
46441
|
+
insetRegions.push({
|
|
46442
|
+
id: iso,
|
|
46443
|
+
d,
|
|
46444
|
+
fill: fill2,
|
|
46445
|
+
stroke: regionStroke,
|
|
46446
|
+
lineNumber,
|
|
46447
|
+
layer: "us-state",
|
|
46448
|
+
...r?.score !== void 0 && { score: r.score },
|
|
46449
|
+
...r && Object.keys(r.tags).length > 0 && { tags: r.tags }
|
|
46450
|
+
});
|
|
46451
|
+
const ctr = geoPath(proj).centroid(f);
|
|
46452
|
+
if (Number.isFinite(ctr[0])) {
|
|
46453
|
+
const name = f.properties?.name ?? iso;
|
|
46454
|
+
insetLabelSeeds.push({ x: ctr[0], y: ctr[1], iso, name, lineNumber });
|
|
46455
|
+
}
|
|
46456
|
+
return xr;
|
|
46457
|
+
};
|
|
46458
|
+
const akRight = placeInset(
|
|
46459
|
+
"US-AK",
|
|
46460
|
+
alaskaProjection(),
|
|
46461
|
+
FIT_PAD,
|
|
46462
|
+
width * 0.15
|
|
46463
|
+
);
|
|
46464
|
+
placeInset("US-HI", hawaiiProjection(), akRight + 24, width * 0.1);
|
|
46465
|
+
}
|
|
46466
|
+
const conusFit = resolved.projection === "albers-usa" && !!usLayer;
|
|
46467
|
+
const cullExtent = conusFit ? geoBounds2(fitTarget) : resolved.extent;
|
|
46468
|
+
const [[exW, exS], [exE, exN]] = cullExtent;
|
|
46469
|
+
const lonSpan = exE - exW;
|
|
46470
|
+
const latSpan = exN - exS;
|
|
46471
|
+
const isGlobalView = lonSpan >= 270 || latSpan >= 130;
|
|
46472
|
+
const padLon = Math.max(8, lonSpan * 0.35);
|
|
46473
|
+
const padLat = Math.max(8, latSpan * 0.35);
|
|
46474
|
+
const vW = exW - padLon;
|
|
46475
|
+
const vE = exE + padLon;
|
|
46476
|
+
const vS = exS - padLat;
|
|
46477
|
+
const vN = exN + padLat;
|
|
46478
|
+
const vLonCenter = (exW + exE) / 2;
|
|
46479
|
+
const normLon = (lon) => {
|
|
46480
|
+
let L = lon;
|
|
46481
|
+
while (L < vLonCenter - 180) L += 360;
|
|
46482
|
+
while (L > vLonCenter + 180) L -= 360;
|
|
46483
|
+
return L;
|
|
46484
|
+
};
|
|
46485
|
+
const ringOverlapsView = (ring) => {
|
|
46486
|
+
let anyIn = false;
|
|
46487
|
+
let loMin = Infinity, loMax = -Infinity, laMin = Infinity, laMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
|
|
46488
|
+
for (const [rawLon, lat] of ring) {
|
|
46489
|
+
const lon = normLon(rawLon);
|
|
46490
|
+
if (lon >= vW && lon <= vE && lat >= vS && lat <= vN) anyIn = true;
|
|
46491
|
+
if (lon < loMin) loMin = lon;
|
|
46492
|
+
if (lon > loMax) loMax = lon;
|
|
46493
|
+
if (rawLon < rawMin) rawMin = rawLon;
|
|
46494
|
+
if (rawLon > rawMax) rawMax = rawLon;
|
|
46495
|
+
if (lat < laMin) laMin = lat;
|
|
46496
|
+
if (lat > laMax) laMax = lat;
|
|
46497
|
+
}
|
|
46498
|
+
if (loMax - loMin > 270) return false;
|
|
46499
|
+
if (rawMax - rawMin > 180 && loMax - loMin < 90) return false;
|
|
46500
|
+
if (anyIn) return true;
|
|
46501
|
+
if (loMax - loMin > 180) return false;
|
|
46502
|
+
return !(loMax < vW || loMin > vE || laMax < vS || laMin > vN);
|
|
46503
|
+
};
|
|
46504
|
+
const cullFeatureToView = (f) => {
|
|
46505
|
+
if (isGlobalView) return f;
|
|
46506
|
+
const g = f.geometry;
|
|
46507
|
+
if (!g) return f;
|
|
46508
|
+
if (g.type === "Polygon") {
|
|
46509
|
+
const ring = g.coordinates[0];
|
|
46510
|
+
return ringOverlapsView(ring) ? f : null;
|
|
46511
|
+
}
|
|
46512
|
+
if (g.type === "MultiPolygon") {
|
|
46513
|
+
const polys = g.coordinates;
|
|
46514
|
+
const keep = polys.filter(
|
|
46515
|
+
(p) => ringOverlapsView(p[0])
|
|
46516
|
+
);
|
|
46517
|
+
if (!keep.length) return null;
|
|
46518
|
+
if (keep.length === polys.length) return f;
|
|
46519
|
+
return { ...f, geometry: { ...g, coordinates: keep } };
|
|
46520
|
+
}
|
|
46521
|
+
return f;
|
|
46522
|
+
};
|
|
46523
|
+
const SEAM_SLIVER_MAX_SPAN = 100;
|
|
46524
|
+
const ringIsFrameFiller = (ring) => {
|
|
46525
|
+
const lons = ring.map(([lon]) => lon).sort((a, b) => a - b);
|
|
46526
|
+
if (lons.length < 2) return false;
|
|
46527
|
+
let maxGap = -1;
|
|
46528
|
+
let gapIdx = 0;
|
|
46529
|
+
for (let i = 1; i < lons.length; i++) {
|
|
46530
|
+
const g = lons[i] - lons[i - 1];
|
|
46531
|
+
if (g > maxGap) {
|
|
46532
|
+
maxGap = g;
|
|
46533
|
+
gapIdx = i;
|
|
46534
|
+
}
|
|
46535
|
+
}
|
|
46536
|
+
const wrapGap = lons[0] + 360 - lons[lons.length - 1];
|
|
46537
|
+
if (wrapGap >= maxGap) return false;
|
|
46538
|
+
const span = 360 - maxGap;
|
|
46539
|
+
const east = lons[gapIdx - 1] + 360;
|
|
46540
|
+
return east > 180 && span < SEAM_SLIVER_MAX_SPAN;
|
|
46541
|
+
};
|
|
46542
|
+
const dropFrameFillers = (f) => {
|
|
46543
|
+
const g = f.geometry;
|
|
46544
|
+
if (!g) return f;
|
|
46545
|
+
if (g.type === "Polygon") {
|
|
46546
|
+
const ring = g.coordinates[0];
|
|
46547
|
+
return ringIsFrameFiller(ring) ? null : f;
|
|
46548
|
+
}
|
|
46549
|
+
if (g.type === "MultiPolygon") {
|
|
46550
|
+
const polys = g.coordinates;
|
|
46551
|
+
const keep = polys.filter(
|
|
46552
|
+
(p) => !ringIsFrameFiller(p[0])
|
|
46553
|
+
);
|
|
46554
|
+
if (!keep.length) return null;
|
|
46555
|
+
if (keep.length === polys.length) return f;
|
|
46556
|
+
return { ...f, geometry: { ...g, coordinates: keep } };
|
|
46557
|
+
}
|
|
46558
|
+
return f;
|
|
46559
|
+
};
|
|
46088
46560
|
const regions = [];
|
|
46089
|
-
const pushRegionLayer = (layerFeatures, layerKind) => {
|
|
46561
|
+
const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
|
|
46090
46562
|
for (const [iso, f] of layerFeatures) {
|
|
46091
|
-
|
|
46092
|
-
|
|
46563
|
+
if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
|
|
46564
|
+
continue;
|
|
46565
|
+
if (layerKind === "country" && usContext && iso === "US") continue;
|
|
46093
46566
|
const r = regionById.get(iso);
|
|
46567
|
+
const viewF = shouldCull ? cullFeatureToView(f) : dropFrameFillers(f);
|
|
46568
|
+
if (!viewF) continue;
|
|
46569
|
+
const d = path(viewF) ?? "";
|
|
46570
|
+
if (!d) continue;
|
|
46094
46571
|
const isThisLayer = r?.layer === layerKind;
|
|
46095
|
-
|
|
46572
|
+
const isForeign = layerKind === "country" && usContext && iso !== "US";
|
|
46573
|
+
let fill2 = isForeign ? foreignFill : neutralFill;
|
|
46096
46574
|
let label;
|
|
46097
46575
|
let lineNumber = -1;
|
|
46098
46576
|
let layer = "base";
|
|
46099
46577
|
if (isThisLayer) {
|
|
46100
|
-
|
|
46101
|
-
else fill2 = tagFill(r.tags, activeGroup) ?? neutralFill;
|
|
46578
|
+
fill2 = regionFill(r);
|
|
46102
46579
|
lineNumber = r.lineNumber;
|
|
46103
46580
|
layer = layerKind;
|
|
46104
46581
|
label = r.name;
|
|
@@ -46110,12 +46587,42 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46110
46587
|
stroke: regionStroke,
|
|
46111
46588
|
lineNumber,
|
|
46112
46589
|
layer,
|
|
46113
|
-
...label !== void 0 && { label }
|
|
46590
|
+
...label !== void 0 && { label },
|
|
46591
|
+
...isThisLayer && r.score !== void 0 && { score: r.score },
|
|
46592
|
+
...isThisLayer && Object.keys(r.tags).length > 0 && { tags: r.tags }
|
|
46114
46593
|
});
|
|
46115
46594
|
}
|
|
46116
46595
|
};
|
|
46117
|
-
pushRegionLayer(worldLayer, "country");
|
|
46118
|
-
if (usLayer) pushRegionLayer(usLayer, "us-state");
|
|
46596
|
+
pushRegionLayer(worldLayer, "country", !isGlobalView);
|
|
46597
|
+
if (usLayer) pushRegionLayer(usLayer, "us-state", !conusFit && !isGlobalView);
|
|
46598
|
+
const lakesTopo = usCrisp && data.naLakes ? data.naLakes : data.lakes;
|
|
46599
|
+
if (lakesTopo) {
|
|
46600
|
+
for (const [, f] of decodeLayer(lakesTopo)) {
|
|
46601
|
+
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
46602
|
+
if (!viewF) continue;
|
|
46603
|
+
const d = path(viewF) ?? "";
|
|
46604
|
+
if (!d) continue;
|
|
46605
|
+
regions.push({
|
|
46606
|
+
id: "lake",
|
|
46607
|
+
d,
|
|
46608
|
+
fill: water,
|
|
46609
|
+
stroke: "none",
|
|
46610
|
+
lineNumber: -1,
|
|
46611
|
+
layer: "base"
|
|
46612
|
+
});
|
|
46613
|
+
}
|
|
46614
|
+
}
|
|
46615
|
+
const riverColor = water;
|
|
46616
|
+
const rivers = [];
|
|
46617
|
+
if (data.rivers) {
|
|
46618
|
+
for (const [, f] of decodeLayer(data.rivers)) {
|
|
46619
|
+
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
46620
|
+
if (!viewF) continue;
|
|
46621
|
+
const d = path(viewF) ?? "";
|
|
46622
|
+
if (!d) continue;
|
|
46623
|
+
rivers.push({ d, color: riverColor, width: RIVER_WIDTH });
|
|
46624
|
+
}
|
|
46625
|
+
}
|
|
46119
46626
|
const sizeVals = resolved.pois.map((p) => Number(p.meta["size"])).filter((n) => Number.isFinite(n) && n > 0);
|
|
46120
46627
|
const sizeMin = sizeVals.length ? Math.min(...sizeVals) : 0;
|
|
46121
46628
|
const sizeMax = sizeVals.length ? Math.max(...sizeVals) : 0;
|
|
@@ -46136,8 +46643,8 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46136
46643
|
if (hex) return { fill: hex, stroke: mix(hex, palette.text, 18) };
|
|
46137
46644
|
}
|
|
46138
46645
|
return {
|
|
46139
|
-
fill: palette.
|
|
46140
|
-
stroke: mix(palette.
|
|
46646
|
+
fill: palette.colors.orange,
|
|
46647
|
+
stroke: mix(palette.colors.orange, palette.text, 18)
|
|
46141
46648
|
};
|
|
46142
46649
|
};
|
|
46143
46650
|
const routeNumberById = /* @__PURE__ */ new Map();
|
|
@@ -46175,7 +46682,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46175
46682
|
cy += Math.sin(ang) * COLO_R;
|
|
46176
46683
|
}
|
|
46177
46684
|
const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
|
|
46178
|
-
poiScreen.set(e.p.id, { cx, cy });
|
|
46685
|
+
poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
|
|
46179
46686
|
const num = routeNumberById.get(e.p.id);
|
|
46180
46687
|
pois.push({
|
|
46181
46688
|
id: e.p.id,
|
|
@@ -46192,17 +46699,36 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46192
46699
|
});
|
|
46193
46700
|
}
|
|
46194
46701
|
const legs = [];
|
|
46702
|
+
const RIM_GAP = 1.5;
|
|
46195
46703
|
const legPath = (a, b, curved, offset) => {
|
|
46196
|
-
if (!curved && offset === 0) return `M${a.cx},${a.cy}L${b.cx},${b.cy}`;
|
|
46197
46704
|
const mx = (a.cx + b.cx) / 2;
|
|
46198
46705
|
const my = (a.cy + b.cy) / 2;
|
|
46199
46706
|
const dx = b.cx - a.cx;
|
|
46200
46707
|
const dy = b.cy - a.cy;
|
|
46201
46708
|
const len = Math.hypot(dx, dy) || 1;
|
|
46709
|
+
const trimA = Math.min(a.r + RIM_GAP, len * 0.45);
|
|
46710
|
+
const trimB = Math.min(b.r + RIM_GAP, len * 0.45);
|
|
46711
|
+
if (!curved && offset === 0) {
|
|
46712
|
+
const ux = dx / len;
|
|
46713
|
+
const uy = dy / len;
|
|
46714
|
+
const ax2 = a.cx + ux * trimA;
|
|
46715
|
+
const ay2 = a.cy + uy * trimA;
|
|
46716
|
+
const bx2 = b.cx - ux * trimB;
|
|
46717
|
+
const by2 = b.cy - uy * trimB;
|
|
46718
|
+
return `M${ax2},${ay2}L${bx2},${by2}`;
|
|
46719
|
+
}
|
|
46202
46720
|
const nx = -dy / len;
|
|
46203
46721
|
const ny = dx / len;
|
|
46204
46722
|
const bow = offset !== 0 ? offset : len * ARC_CURVE_FRAC;
|
|
46205
|
-
|
|
46723
|
+
const px = mx + nx * bow;
|
|
46724
|
+
const py = my + ny * bow;
|
|
46725
|
+
const ta = Math.hypot(px - a.cx, py - a.cy) || 1;
|
|
46726
|
+
const tb = Math.hypot(b.cx - px, b.cy - py) || 1;
|
|
46727
|
+
const ax = a.cx + (px - a.cx) / ta * trimA;
|
|
46728
|
+
const ay = a.cy + (py - a.cy) / ta * trimA;
|
|
46729
|
+
const bx = b.cx - (b.cx - px) / tb * trimB;
|
|
46730
|
+
const by = b.cy - (b.cy - py) / tb * trimB;
|
|
46731
|
+
return `M${ax},${ay}Q${px},${py} ${bx},${by}`;
|
|
46206
46732
|
};
|
|
46207
46733
|
for (const rt of resolved.routes) {
|
|
46208
46734
|
const curved = rt.meta["style"] === "arc";
|
|
@@ -46213,7 +46739,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46213
46739
|
legs.push({
|
|
46214
46740
|
d: legPath(a, b, curved, 0),
|
|
46215
46741
|
width: W_MIN,
|
|
46216
|
-
color: mix(palette.text, palette.bg,
|
|
46742
|
+
color: mix(palette.text, palette.bg, 72),
|
|
46217
46743
|
arrow: true,
|
|
46218
46744
|
lineNumber: rt.lineNumber
|
|
46219
46745
|
});
|
|
@@ -46249,7 +46775,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46249
46775
|
legs.push({
|
|
46250
46776
|
d: legPath(a, b, curved, offset),
|
|
46251
46777
|
width: widthFor(e),
|
|
46252
|
-
color: mix(palette.text, palette.bg,
|
|
46778
|
+
color: mix(palette.text, palette.bg, 66),
|
|
46253
46779
|
arrow: e.directed,
|
|
46254
46780
|
lineNumber: e.lineNumber,
|
|
46255
46781
|
...e.label !== void 0 && {
|
|
@@ -46261,38 +46787,92 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46261
46787
|
});
|
|
46262
46788
|
}
|
|
46263
46789
|
const labels = [];
|
|
46264
|
-
const pinList = [];
|
|
46265
46790
|
const obstacles = [];
|
|
46266
46791
|
const markers = pois.map((p) => ({
|
|
46267
46792
|
cx: p.cx,
|
|
46268
46793
|
cy: p.cy,
|
|
46269
46794
|
r: p.r
|
|
46270
46795
|
}));
|
|
46271
|
-
const
|
|
46796
|
+
const legSegments = [];
|
|
46797
|
+
for (const leg of legs) {
|
|
46798
|
+
const m = /^M(-?[\d.]+),(-?[\d.]+)(?:L(-?[\d.]+),(-?[\d.]+)|Q(-?[\d.]+),(-?[\d.]+) (-?[\d.]+),(-?[\d.]+))$/.exec(
|
|
46799
|
+
leg.d
|
|
46800
|
+
);
|
|
46801
|
+
if (!m) continue;
|
|
46802
|
+
const x0 = +m[1];
|
|
46803
|
+
const y0 = +m[2];
|
|
46804
|
+
if (m[3] !== void 0) {
|
|
46805
|
+
legSegments.push([x0, y0, +m[3], +m[4]]);
|
|
46806
|
+
} else {
|
|
46807
|
+
const cx = +m[5];
|
|
46808
|
+
const cy = +m[6];
|
|
46809
|
+
const ex = +m[7];
|
|
46810
|
+
const ey = +m[8];
|
|
46811
|
+
const N = 8;
|
|
46812
|
+
let px = x0;
|
|
46813
|
+
let py = y0;
|
|
46814
|
+
for (let i = 1; i <= N; i++) {
|
|
46815
|
+
const t = i / N;
|
|
46816
|
+
const u = 1 - t;
|
|
46817
|
+
const qx = u * u * x0 + 2 * u * t * cx + t * t * ex;
|
|
46818
|
+
const qy = u * u * y0 + 2 * u * t * cy + t * t * ey;
|
|
46819
|
+
legSegments.push([px, py, qx, qy]);
|
|
46820
|
+
px = qx;
|
|
46821
|
+
py = qy;
|
|
46822
|
+
}
|
|
46823
|
+
}
|
|
46824
|
+
}
|
|
46825
|
+
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));
|
|
46272
46826
|
const regionLabelMode = resolved.directives.regionLabels ?? "off";
|
|
46827
|
+
const LABEL_PADX = 6;
|
|
46828
|
+
const LABEL_PADY = 3;
|
|
46829
|
+
const labelW = (text) => measureLegendText(text, FONT) + 2 * LABEL_PADX;
|
|
46830
|
+
const labelH = FONT + 2 * LABEL_PADY;
|
|
46831
|
+
const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
|
|
46832
|
+
const color = contrastText(
|
|
46833
|
+
fill2,
|
|
46834
|
+
palette.textOnFillLight,
|
|
46835
|
+
palette.textOnFillDark
|
|
46836
|
+
);
|
|
46837
|
+
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
46838
|
+
labels.push({
|
|
46839
|
+
x,
|
|
46840
|
+
y,
|
|
46841
|
+
text,
|
|
46842
|
+
anchor: "middle",
|
|
46843
|
+
color,
|
|
46844
|
+
halo: true,
|
|
46845
|
+
haloColor,
|
|
46846
|
+
lineNumber
|
|
46847
|
+
});
|
|
46848
|
+
};
|
|
46849
|
+
const WORLD_LABEL_ANCHORS = {
|
|
46850
|
+
US: [-98.5, 39.5]
|
|
46851
|
+
// CONUS geographic centre (near Lebanon, Kansas)
|
|
46852
|
+
};
|
|
46273
46853
|
if (regionLabelMode === "full" || regionLabelMode === "abbrev") {
|
|
46274
46854
|
for (const r of regions) {
|
|
46275
46855
|
if (r.layer === "base" || r.label === void 0) continue;
|
|
46276
46856
|
const f = r.layer === "us-state" ? usLayer?.get(r.id) : worldLayer.get(r.id);
|
|
46277
46857
|
if (!f) continue;
|
|
46278
46858
|
const [[x0, y0], [x1, y1]] = path.bounds(f);
|
|
46279
|
-
if ((x1 - x0) * (y1 - y0) < TINY_REGION_AREA) continue;
|
|
46280
|
-
const c = path.centroid(f);
|
|
46281
|
-
if (!Number.isFinite(c[0])) continue;
|
|
46282
46859
|
const text = regionLabelMode === "abbrev" ? r.id.replace(/^US-/, "") : r.label;
|
|
46283
|
-
|
|
46284
|
-
|
|
46285
|
-
|
|
46860
|
+
if (labelW(text) > x1 - x0 || labelH > y1 - y0) continue;
|
|
46861
|
+
const anchor = r.layer !== "us-state" ? WORLD_LABEL_ANCHORS[r.id] : void 0;
|
|
46862
|
+
const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
|
|
46863
|
+
if (!c || !Number.isFinite(c[0])) continue;
|
|
46864
|
+
pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
|
|
46865
|
+
}
|
|
46866
|
+
for (const seed of insetLabelSeeds) {
|
|
46867
|
+
const text = regionLabelMode === "abbrev" ? seed.iso.replace(/^US-/, "") : seed.name;
|
|
46868
|
+
const src = regionById.get(seed.iso);
|
|
46869
|
+
pushRegionLabel(
|
|
46870
|
+
seed.x,
|
|
46871
|
+
seed.y,
|
|
46286
46872
|
text,
|
|
46287
|
-
|
|
46288
|
-
|
|
46289
|
-
|
|
46290
|
-
palette.textOnFillLight,
|
|
46291
|
-
palette.textOnFillDark
|
|
46292
|
-
),
|
|
46293
|
-
halo: true,
|
|
46294
|
-
lineNumber: r.lineNumber
|
|
46295
|
-
});
|
|
46873
|
+
src ? regionFill(src) : neutralFill,
|
|
46874
|
+
seed.lineNumber
|
|
46875
|
+
);
|
|
46296
46876
|
}
|
|
46297
46877
|
}
|
|
46298
46878
|
const poiLabelMode = resolved.directives.poiLabels ?? "auto";
|
|
@@ -46305,68 +46885,106 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46305
46885
|
const src = poiById.get(p.id);
|
|
46306
46886
|
return src?.label ?? src?.name ?? p.id;
|
|
46307
46887
|
};
|
|
46308
|
-
|
|
46309
|
-
|
|
46888
|
+
const poiLabH = FONT * 1.25;
|
|
46889
|
+
const labelInfo = (p) => {
|
|
46310
46890
|
const text = labelText(p);
|
|
46311
|
-
|
|
46312
|
-
|
|
46313
|
-
|
|
46314
|
-
|
|
46315
|
-
|
|
46891
|
+
return { text, w: measureLegendText(text, FONT) };
|
|
46892
|
+
};
|
|
46893
|
+
const pushInline = (p, text, w, side) => {
|
|
46894
|
+
const tx = side === "right" ? p.cx + p.r + 3 : p.cx - p.r - 3;
|
|
46895
|
+
obstacles.push({
|
|
46896
|
+
x: side === "right" ? tx : tx - w,
|
|
46897
|
+
y: p.cy - poiLabH / 2,
|
|
46898
|
+
w,
|
|
46899
|
+
h: poiLabH
|
|
46900
|
+
});
|
|
46901
|
+
labels.push({
|
|
46902
|
+
x: tx,
|
|
46903
|
+
y: p.cy + FONT / 3,
|
|
46904
|
+
text,
|
|
46905
|
+
anchor: side === "right" ? "start" : "end",
|
|
46906
|
+
color: palette.text,
|
|
46907
|
+
halo: true,
|
|
46908
|
+
haloColor: palette.bg,
|
|
46909
|
+
poiId: p.id,
|
|
46910
|
+
lineNumber: p.lineNumber
|
|
46911
|
+
});
|
|
46912
|
+
};
|
|
46913
|
+
const inlineFits = (p, w, side) => {
|
|
46914
|
+
const tx = side === "right" ? p.cx + p.r + 3 : p.cx - p.r - 3;
|
|
46915
|
+
const rect = {
|
|
46916
|
+
x: side === "right" ? tx : tx - w,
|
|
46917
|
+
y: p.cy - poiLabH / 2,
|
|
46918
|
+
w,
|
|
46919
|
+
h: poiLabH
|
|
46920
|
+
};
|
|
46921
|
+
return rect.x >= 0 && rect.x + rect.w <= width && !collides(rect);
|
|
46922
|
+
};
|
|
46923
|
+
const GROUP_R = 30;
|
|
46924
|
+
const groups = [];
|
|
46925
|
+
for (const p of ordered) {
|
|
46926
|
+
const near = groups.find(
|
|
46927
|
+
(g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
|
|
46928
|
+
);
|
|
46929
|
+
if (near) near.push(p);
|
|
46930
|
+
else groups.push([p]);
|
|
46931
|
+
}
|
|
46932
|
+
const ROW_GAP2 = 3;
|
|
46933
|
+
const step = poiLabH + ROW_GAP2;
|
|
46934
|
+
const COL_GAP = 16;
|
|
46935
|
+
const placeColumn = (group) => {
|
|
46936
|
+
const items = group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
|
|
46937
|
+
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
46938
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
46939
|
+
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
46940
|
+
const maxW = Math.max(...items.map((o) => o.w));
|
|
46941
|
+
const side = right + COL_GAP + maxW <= width - 2 ? "right" : "left";
|
|
46942
|
+
const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
|
|
46943
|
+
const totalH = items.length * step;
|
|
46944
|
+
let startY = cyMid - totalH / 2;
|
|
46945
|
+
startY = Math.max(2, Math.min(startY, height - totalH - 2));
|
|
46946
|
+
items.forEach((o, i) => {
|
|
46947
|
+
const rowCy = startY + i * step + step / 2;
|
|
46948
|
+
obstacles.push({
|
|
46949
|
+
x: side === "right" ? colX : colX - o.w,
|
|
46950
|
+
y: rowCy - poiLabH / 2,
|
|
46951
|
+
w: o.w,
|
|
46952
|
+
h: poiLabH
|
|
46953
|
+
});
|
|
46316
46954
|
labels.push({
|
|
46317
|
-
x:
|
|
46318
|
-
y:
|
|
46319
|
-
text,
|
|
46320
|
-
anchor: "start",
|
|
46955
|
+
x: colX,
|
|
46956
|
+
y: rowCy + FONT / 3,
|
|
46957
|
+
text: o.text,
|
|
46958
|
+
anchor: side === "right" ? "start" : "end",
|
|
46321
46959
|
color: palette.text,
|
|
46322
46960
|
halo: true,
|
|
46323
|
-
|
|
46961
|
+
haloColor: palette.bg,
|
|
46962
|
+
leader: {
|
|
46963
|
+
x1: o.p.cx,
|
|
46964
|
+
y1: o.p.cy,
|
|
46965
|
+
x2: side === "right" ? colX - 2 : colX + 2,
|
|
46966
|
+
y2: rowCy
|
|
46967
|
+
},
|
|
46968
|
+
leaderColor: o.p.fill,
|
|
46969
|
+
poiId: o.p.id,
|
|
46970
|
+
lineNumber: o.p.lineNumber
|
|
46324
46971
|
});
|
|
46325
|
-
|
|
46326
|
-
|
|
46327
|
-
|
|
46328
|
-
|
|
46329
|
-
|
|
46330
|
-
|
|
46331
|
-
|
|
46332
|
-
|
|
46333
|
-
|
|
46334
|
-
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
if (rect.x < 0 || rect.x + rect.w > width || rect.y < 0 || rect.y + rect.h > height) {
|
|
46339
|
-
continue;
|
|
46340
|
-
}
|
|
46341
|
-
if (collides(rect)) continue;
|
|
46342
|
-
obstacles.push(rect);
|
|
46343
|
-
labels.push({
|
|
46344
|
-
x: cx,
|
|
46345
|
-
y: cy + FONT / 3,
|
|
46346
|
-
text,
|
|
46347
|
-
anchor: dx >= 0 ? "start" : "end",
|
|
46348
|
-
color: palette.text,
|
|
46349
|
-
halo: true,
|
|
46350
|
-
leader: { x1: p.cx, y1: p.cy, x2: cx, y2: cy },
|
|
46351
|
-
lineNumber: p.lineNumber
|
|
46352
|
-
});
|
|
46353
|
-
placed = true;
|
|
46354
|
-
break;
|
|
46972
|
+
});
|
|
46973
|
+
};
|
|
46974
|
+
for (const g of groups) {
|
|
46975
|
+
if (g.length === 1) {
|
|
46976
|
+
const p = g[0];
|
|
46977
|
+
const { text, w } = labelInfo(p);
|
|
46978
|
+
if (inlineFits(p, w, "right")) {
|
|
46979
|
+
pushInline(p, text, w, "right");
|
|
46980
|
+
continue;
|
|
46981
|
+
}
|
|
46982
|
+
if (inlineFits(p, w, "left")) {
|
|
46983
|
+
pushInline(p, text, w, "left");
|
|
46984
|
+
continue;
|
|
46355
46985
|
}
|
|
46356
46986
|
}
|
|
46357
|
-
|
|
46358
|
-
pinCounter += 1;
|
|
46359
|
-
pinList.push({ pin: pinCounter, label: text });
|
|
46360
|
-
labels.push({
|
|
46361
|
-
x: p.cx + p.r + 2,
|
|
46362
|
-
y: p.cy - p.r,
|
|
46363
|
-
text: String(pinCounter),
|
|
46364
|
-
anchor: "start",
|
|
46365
|
-
color: palette.text,
|
|
46366
|
-
halo: true,
|
|
46367
|
-
pin: pinCounter,
|
|
46368
|
-
lineNumber: p.lineNumber
|
|
46369
|
-
});
|
|
46987
|
+
placeColumn(g);
|
|
46370
46988
|
}
|
|
46371
46989
|
}
|
|
46372
46990
|
let legend = null;
|
|
@@ -46375,8 +46993,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46375
46993
|
name: g.name,
|
|
46376
46994
|
entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
|
|
46377
46995
|
}));
|
|
46378
|
-
|
|
46379
|
-
if (hasAnything) {
|
|
46996
|
+
if (tagGroups.length > 0 || hasRamp) {
|
|
46380
46997
|
legend = {
|
|
46381
46998
|
tagGroups,
|
|
46382
46999
|
activeGroup,
|
|
@@ -46387,20 +47004,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46387
47004
|
},
|
|
46388
47005
|
min: rampMin,
|
|
46389
47006
|
max: rampMax,
|
|
46390
|
-
hue: rampHue
|
|
47007
|
+
hue: rampHue,
|
|
47008
|
+
base: rampBase
|
|
46391
47009
|
}
|
|
46392
|
-
},
|
|
46393
|
-
...sizeVals.length > 0 && {
|
|
46394
|
-
size: {
|
|
46395
|
-
...resolved.directives.sizeMetric !== void 0 && {
|
|
46396
|
-
metric: resolved.directives.sizeMetric
|
|
46397
|
-
},
|
|
46398
|
-
min: sizeMin,
|
|
46399
|
-
max: sizeMax
|
|
46400
|
-
}
|
|
46401
|
-
},
|
|
46402
|
-
...weightVals.length > 0 && {
|
|
46403
|
-
weight: { min: wMin, max: wMax }
|
|
46404
47010
|
}
|
|
46405
47011
|
};
|
|
46406
47012
|
}
|
|
@@ -46408,26 +47014,28 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46408
47014
|
return {
|
|
46409
47015
|
width,
|
|
46410
47016
|
height,
|
|
46411
|
-
background:
|
|
47017
|
+
background: water,
|
|
46412
47018
|
title: resolved.title,
|
|
46413
47019
|
...resolved.subtitle !== void 0 && { subtitle: resolved.subtitle },
|
|
46414
47020
|
...resolved.caption !== void 0 && { caption: resolved.caption },
|
|
46415
47021
|
regions,
|
|
47022
|
+
rivers,
|
|
46416
47023
|
legs,
|
|
46417
47024
|
pois,
|
|
46418
47025
|
labels,
|
|
46419
|
-
|
|
46420
|
-
|
|
47026
|
+
legend,
|
|
47027
|
+
insets,
|
|
47028
|
+
insetRegions
|
|
46421
47029
|
};
|
|
46422
47030
|
}
|
|
46423
|
-
var FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT,
|
|
47031
|
+
var 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;
|
|
46424
47032
|
var init_layout15 = __esm({
|
|
46425
47033
|
"src/map/layout.ts"() {
|
|
46426
47034
|
"use strict";
|
|
46427
47035
|
init_color_utils();
|
|
46428
|
-
init_tag_groups();
|
|
46429
47036
|
init_label_layout();
|
|
46430
47037
|
init_legend_constants();
|
|
47038
|
+
init_title_constants();
|
|
46431
47039
|
FIT_PAD = 24;
|
|
46432
47040
|
RAMP_FLOOR = 15;
|
|
46433
47041
|
R_DEFAULT = 6;
|
|
@@ -46436,23 +47044,32 @@ var init_layout15 = __esm({
|
|
|
46436
47044
|
W_MIN = 1.25;
|
|
46437
47045
|
W_MAX = 8;
|
|
46438
47046
|
FONT = 11;
|
|
46439
|
-
LEADER_STEP = 14;
|
|
46440
47047
|
COLO_EPS = 1.5;
|
|
47048
|
+
LAND_TINT_LIGHT = 58;
|
|
47049
|
+
LAND_TINT_DARK = 75;
|
|
47050
|
+
TAG_TINT_LIGHT = 60;
|
|
47051
|
+
TAG_TINT_DARK = 68;
|
|
47052
|
+
WATER_TINT = 55;
|
|
47053
|
+
RIVER_WIDTH = 1.3;
|
|
47054
|
+
FOREIGN_TINT_LIGHT = 30;
|
|
47055
|
+
FOREIGN_TINT_DARK = 62;
|
|
46441
47056
|
COLO_R = 9;
|
|
46442
47057
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
46443
47058
|
FAN_STEP = 16;
|
|
46444
|
-
TINY_REGION_AREA = 600;
|
|
46445
47059
|
ARC_CURVE_FRAC = 0.18;
|
|
46446
|
-
|
|
46447
|
-
|
|
46448
|
-
|
|
46449
|
-
|
|
46450
|
-
|
|
46451
|
-
|
|
46452
|
-
|
|
46453
|
-
|
|
46454
|
-
|
|
46455
|
-
|
|
47060
|
+
usConusProjection = () => geoConicEqualArea().parallels([29.5, 45.5]).rotate([96, 0]);
|
|
47061
|
+
alaskaProjection = () => geoConicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
|
|
47062
|
+
hawaiiProjection = () => geoMercator();
|
|
47063
|
+
INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
|
|
47064
|
+
US_NON_CONUS = /* @__PURE__ */ new Set([
|
|
47065
|
+
"US-AK",
|
|
47066
|
+
"US-HI",
|
|
47067
|
+
"US-AS",
|
|
47068
|
+
"US-GU",
|
|
47069
|
+
"US-MP",
|
|
47070
|
+
"US-PR",
|
|
47071
|
+
"US-VI"
|
|
47072
|
+
]);
|
|
46456
47073
|
}
|
|
46457
47074
|
});
|
|
46458
47075
|
|
|
@@ -46463,7 +47080,7 @@ __export(renderer_exports16, {
|
|
|
46463
47080
|
renderMapForExport: () => renderMapForExport
|
|
46464
47081
|
});
|
|
46465
47082
|
import * as d3Selection18 from "d3-selection";
|
|
46466
|
-
function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims) {
|
|
47083
|
+
function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
|
|
46467
47084
|
d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
46468
47085
|
const width = exportDims?.width ?? container.clientWidth;
|
|
46469
47086
|
const height = exportDims?.height ?? container.clientHeight;
|
|
@@ -46474,27 +47091,29 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
46474
47091
|
{ width, height },
|
|
46475
47092
|
{
|
|
46476
47093
|
palette,
|
|
46477
|
-
isDark
|
|
47094
|
+
isDark,
|
|
47095
|
+
...activeGroupOverride !== void 0 && {
|
|
47096
|
+
activeGroup: activeGroupOverride
|
|
47097
|
+
}
|
|
46478
47098
|
}
|
|
46479
47099
|
);
|
|
46480
|
-
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);
|
|
47100
|
+
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);
|
|
46481
47101
|
svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
|
|
46482
|
-
const arrowColor = mix(palette.text, palette.bg, 50);
|
|
46483
47102
|
const defs = svg.append("defs");
|
|
46484
|
-
|
|
46485
|
-
const haloColor =
|
|
46486
|
-
if (layout.title) {
|
|
46487
|
-
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);
|
|
46488
|
-
}
|
|
46489
|
-
if (layout.subtitle) {
|
|
46490
|
-
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);
|
|
46491
|
-
}
|
|
46492
|
-
if (layout.caption) {
|
|
46493
|
-
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);
|
|
46494
|
-
}
|
|
47103
|
+
const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
|
|
47104
|
+
const haloColor = palette.bg;
|
|
46495
47105
|
const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
|
|
46496
|
-
|
|
46497
|
-
const p =
|
|
47106
|
+
const drawRegion = (g, r, strokeWidth) => {
|
|
47107
|
+
const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
|
|
47108
|
+
if (r.layer !== "base") {
|
|
47109
|
+
p.classed("dgmo-map-region", true).attr("data-region", r.id);
|
|
47110
|
+
if (r.score !== void 0) p.attr("data-score", r.score);
|
|
47111
|
+
if (r.tags) {
|
|
47112
|
+
for (const [group, value] of Object.entries(r.tags)) {
|
|
47113
|
+
p.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
47114
|
+
}
|
|
47115
|
+
}
|
|
47116
|
+
}
|
|
46498
47117
|
if (r.lineNumber >= 0) {
|
|
46499
47118
|
p.attr("data-line-number", r.lineNumber);
|
|
46500
47119
|
if (onClickItem) {
|
|
@@ -46504,11 +47123,31 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
46504
47123
|
);
|
|
46505
47124
|
}
|
|
46506
47125
|
}
|
|
47126
|
+
};
|
|
47127
|
+
for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
|
|
47128
|
+
if (layout.rivers.length) {
|
|
47129
|
+
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
47130
|
+
for (const r of layout.rivers) {
|
|
47131
|
+
gRivers.append("path").attr("d", r.d).attr("stroke", r.color).attr("stroke-width", r.width).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
47132
|
+
}
|
|
47133
|
+
}
|
|
47134
|
+
if (layout.insets.length) {
|
|
47135
|
+
const insetG = svg.append("g").attr("class", "dgmo-map-insets");
|
|
47136
|
+
for (const box of layout.insets) {
|
|
47137
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
47138
|
+
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");
|
|
47139
|
+
}
|
|
47140
|
+
for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
|
|
46507
47141
|
}
|
|
46508
47142
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
46509
|
-
|
|
47143
|
+
layout.legs.forEach((leg, i) => {
|
|
46510
47144
|
const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
|
|
46511
|
-
if (leg.arrow)
|
|
47145
|
+
if (leg.arrow) {
|
|
47146
|
+
const id = `dgmo-map-arrow-${i}`;
|
|
47147
|
+
const s = arrowSize(leg.width);
|
|
47148
|
+
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);
|
|
47149
|
+
p.attr("marker-end", `url(#${id})`);
|
|
47150
|
+
}
|
|
46512
47151
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
46513
47152
|
emitText(
|
|
46514
47153
|
gLegs,
|
|
@@ -46522,13 +47161,13 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
46522
47161
|
LABEL_FONT - 1
|
|
46523
47162
|
);
|
|
46524
47163
|
}
|
|
46525
|
-
}
|
|
47164
|
+
});
|
|
46526
47165
|
const gPois = svg.append("g").attr("class", "dgmo-map-pois");
|
|
46527
47166
|
for (const poi of layout.pois) {
|
|
46528
47167
|
if (poi.isOrigin) {
|
|
46529
47168
|
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);
|
|
46530
47169
|
}
|
|
46531
|
-
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);
|
|
47170
|
+
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);
|
|
46532
47171
|
if (onClickItem) {
|
|
46533
47172
|
c.style("cursor", "pointer").on(
|
|
46534
47173
|
"click",
|
|
@@ -46552,48 +47191,66 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
46552
47191
|
const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
|
|
46553
47192
|
for (const lab of layout.labels) {
|
|
46554
47193
|
if (lab.leader) {
|
|
46555
|
-
gLabels.append("line").attr("x1", lab.leader.x1).attr("y1", lab.leader.y1).attr("x2", lab.leader.x2).attr("y2", lab.leader.y2).attr(
|
|
46556
|
-
|
|
46557
|
-
|
|
46558
|
-
|
|
47194
|
+
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(
|
|
47195
|
+
"stroke",
|
|
47196
|
+
lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
|
|
47197
|
+
).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
|
|
47198
|
+
if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
|
|
46559
47199
|
}
|
|
46560
|
-
emitText(
|
|
47200
|
+
const t = emitText(
|
|
46561
47201
|
gLabels,
|
|
46562
47202
|
lab.x,
|
|
46563
47203
|
lab.y,
|
|
46564
47204
|
lab.text,
|
|
46565
47205
|
lab.anchor,
|
|
46566
47206
|
lab.color,
|
|
46567
|
-
haloColor,
|
|
47207
|
+
lab.haloColor,
|
|
46568
47208
|
lab.halo,
|
|
46569
47209
|
LABEL_FONT
|
|
46570
47210
|
);
|
|
46571
|
-
|
|
46572
|
-
|
|
46573
|
-
|
|
46574
|
-
"transform",
|
|
46575
|
-
`translate(12, ${height - layout.pinList.length * 14 - 8})`
|
|
46576
|
-
);
|
|
46577
|
-
layout.pinList.forEach((entry, i) => {
|
|
46578
|
-
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}`);
|
|
46579
|
-
});
|
|
47211
|
+
if (lab.poiId !== void 0) {
|
|
47212
|
+
t.attr("data-poi", lab.poiId).style("cursor", "default");
|
|
47213
|
+
}
|
|
46580
47214
|
}
|
|
46581
47215
|
if (layout.legend) {
|
|
46582
47216
|
const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
|
|
46583
47217
|
const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
|
|
46584
|
-
const
|
|
47218
|
+
const ramp = layout.legend.ramp;
|
|
47219
|
+
const scoreGroup = ramp ? {
|
|
47220
|
+
name: ramp.metric?.trim() || "Score",
|
|
47221
|
+
entries: [],
|
|
47222
|
+
gradient: {
|
|
47223
|
+
min: ramp.min,
|
|
47224
|
+
max: ramp.max,
|
|
47225
|
+
hue: ramp.hue,
|
|
47226
|
+
base: ramp.base
|
|
47227
|
+
}
|
|
47228
|
+
} : null;
|
|
47229
|
+
const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
|
|
47230
|
+
const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
|
|
46585
47231
|
if (groups.length > 0) {
|
|
46586
47232
|
const config = {
|
|
46587
|
-
groups
|
|
47233
|
+
groups,
|
|
46588
47234
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
46589
47235
|
mode: exportDims ? "export" : "preview",
|
|
46590
|
-
showEmptyGroups: false
|
|
47236
|
+
showEmptyGroups: false,
|
|
47237
|
+
// Keep inactive siblings visible as pills so the user can click to flip
|
|
47238
|
+
// the active colouring dimension (preview only — export shows just the
|
|
47239
|
+
// active group).
|
|
47240
|
+
showInactivePills: true
|
|
46591
47241
|
};
|
|
46592
47242
|
const state = { activeGroup: layout.legend.activeGroup };
|
|
46593
47243
|
renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
|
|
46594
47244
|
}
|
|
46595
|
-
|
|
46596
|
-
|
|
47245
|
+
}
|
|
47246
|
+
if (layout.title) {
|
|
47247
|
+
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);
|
|
47248
|
+
}
|
|
47249
|
+
if (layout.subtitle) {
|
|
47250
|
+
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);
|
|
47251
|
+
}
|
|
47252
|
+
if (layout.caption) {
|
|
47253
|
+
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);
|
|
46597
47254
|
}
|
|
46598
47255
|
}
|
|
46599
47256
|
function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
|
|
@@ -46604,54 +47261,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
|
|
|
46604
47261
|
if (withHalo) {
|
|
46605
47262
|
t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7);
|
|
46606
47263
|
}
|
|
46607
|
-
|
|
46608
|
-
function emitExtraLegend(svg, layout, palette, height, bottomGap) {
|
|
46609
|
-
const { legend } = layout;
|
|
46610
|
-
if (!legend) return;
|
|
46611
|
-
if (!legend.ramp && !legend.size && !legend.weight) return;
|
|
46612
|
-
const blocks = [];
|
|
46613
|
-
const g = svg.append("g").attr("class", "dgmo-map-legend-keys").attr("transform", `translate(12, ${height - 56 - bottomGap})`);
|
|
46614
|
-
let xCursor = 0;
|
|
46615
|
-
if (legend.ramp) {
|
|
46616
|
-
const ramp = legend.ramp;
|
|
46617
|
-
blocks.push(() => {
|
|
46618
|
-
const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
|
|
46619
|
-
const gradId = "dgmo-map-ramp";
|
|
46620
|
-
const grad = block.append("defs").append("linearGradient").attr("id", gradId).attr("x1", "0%").attr("x2", "100%");
|
|
46621
|
-
grad.append("stop").attr("offset", "0%").attr("stop-color", mix(ramp.hue, palette.bg, 15));
|
|
46622
|
-
grad.append("stop").attr("offset", "100%").attr("stop-color", ramp.hue);
|
|
46623
|
-
block.append("rect").attr("width", 80).attr("height", 8).attr("fill", `url(#${gradId})`);
|
|
46624
|
-
block.append("text").attr("x", 0).attr("y", 22).attr("font-size", 9).attr("fill", palette.textMuted).text(String(ramp.min));
|
|
46625
|
-
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));
|
|
46626
|
-
if (ramp.metric) {
|
|
46627
|
-
block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(ramp.metric);
|
|
46628
|
-
}
|
|
46629
|
-
xCursor += 110;
|
|
46630
|
-
});
|
|
46631
|
-
}
|
|
46632
|
-
if (legend.size) {
|
|
46633
|
-
const sz = legend.size;
|
|
46634
|
-
blocks.push(() => {
|
|
46635
|
-
const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
|
|
46636
|
-
[3, 6, 10].forEach((r, i) => {
|
|
46637
|
-
block.append("circle").attr("cx", i * 26 + r).attr("cy", 8).attr("r", r).attr("fill", "none").attr("stroke", palette.textMuted);
|
|
46638
|
-
});
|
|
46639
|
-
block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(sz.metric ?? "size");
|
|
46640
|
-
xCursor += 110;
|
|
46641
|
-
});
|
|
46642
|
-
}
|
|
46643
|
-
if (legend.weight) {
|
|
46644
|
-
const wt = legend.weight;
|
|
46645
|
-
blocks.push(() => {
|
|
46646
|
-
const block = g.append("g").attr("transform", `translate(${xCursor},0)`);
|
|
46647
|
-
[1, 3, 6].forEach((w, i) => {
|
|
46648
|
-
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);
|
|
46649
|
-
});
|
|
46650
|
-
block.append("text").attr("x", 0).attr("y", -4).attr("font-size", 9).attr("fill", palette.textMuted).text(wt.metric ?? "weight");
|
|
46651
|
-
xCursor += 110;
|
|
46652
|
-
});
|
|
46653
|
-
}
|
|
46654
|
-
for (const draw of blocks) draw();
|
|
47264
|
+
return t;
|
|
46655
47265
|
}
|
|
46656
47266
|
var LABEL_FONT;
|
|
46657
47267
|
var init_renderer16 = __esm({
|