@diagrammo/dgmo 0.21.1 → 0.23.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/README.md +16 -6
- package/dist/advanced.cjs +2230 -503
- package/dist/advanced.d.cts +5731 -0
- package/dist/advanced.d.ts +5731 -0
- package/dist/advanced.js +2226 -503
- package/dist/auto.cjs +2272 -479
- package/dist/auto.d.cts +39 -0
- package/dist/auto.d.ts +39 -0
- package/dist/auto.js +124 -124
- package/dist/auto.mjs +2274 -480
- package/dist/cli.cjs +170 -170
- package/dist/editor.cjs +16 -16
- package/dist/editor.js +16 -16
- package/dist/highlight.cjs +18 -13
- package/dist/highlight.js +18 -13
- package/dist/index.cjs +2253 -465
- package/dist/index.d.cts +339 -0
- package/dist/index.d.ts +339 -0
- package/dist/index.js +2255 -466
- package/dist/internal.cjs +2230 -503
- package/dist/internal.d.cts +5731 -0
- package/dist/internal.d.ts +5731 -0
- package/dist/internal.js +2226 -503
- package/dist/map-data/PROVENANCE.json +1 -1
- package/dist/map-data/gazetteer.json +1 -1
- package/dist/map-data/mountain-ranges.json +1 -1
- package/dist/map-data/water-bodies.json +1 -0
- package/dist/map-data/world-coarse.json +1 -1
- package/dist/map-data/world-detail.json +1 -1
- package/docs/language-reference.md +55 -9
- package/gallery/fixtures/boxes-and-lines.dgmo +6 -4
- package/gallery/fixtures/map-categorical-world.dgmo +16 -0
- package/gallery/fixtures/map-categorical.dgmo +0 -1
- package/gallery/fixtures/map-choropleth.dgmo +0 -1
- package/gallery/fixtures/map-coastline.dgmo +7 -0
- package/gallery/fixtures/map-colorize.dgmo +11 -0
- package/gallery/fixtures/map-direct-color.dgmo +0 -1
- package/gallery/fixtures/map-reference-world.dgmo +11 -0
- package/gallery/fixtures/map-region-scope.dgmo +0 -3
- package/gallery/fixtures/map-route.dgmo +0 -1
- package/package.json +1 -1
- package/src/advanced.ts +12 -1
- package/src/boxes-and-lines/parser.ts +39 -0
- package/src/boxes-and-lines/renderer.ts +205 -20
- package/src/boxes-and-lines/types.ts +9 -0
- package/src/cli.ts +1 -1
- package/src/completion.ts +36 -30
- package/src/cycle/renderer.ts +14 -1
- package/src/d3.ts +20 -6
- package/src/editor/highlight-api.ts +4 -0
- package/src/editor/keywords.ts +16 -16
- package/src/infra/renderer.ts +35 -7
- package/src/map/colorize.ts +54 -0
- package/src/map/context-labels.ts +429 -0
- package/src/map/data/PROVENANCE.json +1 -1
- package/src/map/data/README.md +6 -0
- package/src/map/data/gazetteer.json +1 -1
- package/src/map/data/mountain-ranges.json +1 -1
- package/src/map/data/types.ts +34 -0
- package/src/map/data/water-bodies.json +1 -0
- package/src/map/data/world-coarse.json +1 -1
- package/src/map/data/world-detail.json +1 -1
- package/src/map/dimensions.ts +117 -0
- package/src/map/geo-query.ts +21 -3
- package/src/map/geo.ts +47 -1
- package/src/map/layout.ts +1408 -266
- package/src/map/load-data.ts +10 -2
- package/src/map/parser.ts +42 -116
- package/src/map/renderer.ts +604 -14
- package/src/map/resolved-types.ts +16 -2
- package/src/map/resolver.ts +208 -59
- package/src/map/types.ts +30 -32
- package/src/mindmap/renderer.ts +10 -1
- package/src/palettes/atlas.ts +77 -0
- package/src/palettes/blueprint.ts +73 -0
- package/src/palettes/color-utils.ts +58 -1
- package/src/palettes/index.ts +12 -3
- package/src/palettes/slate.ts +73 -0
- package/src/palettes/tidewater.ts +73 -0
- package/src/render.ts +8 -1
- package/src/tech-radar/renderer.ts +3 -0
- package/src/tech-radar/types.ts +3 -0
- package/src/utils/d3-types.ts +5 -0
- package/src/utils/legend-layout.ts +21 -4
- package/src/utils/legend-types.ts +7 -0
- package/src/utils/reserved-key-registry.ts +8 -3
- package/src/palettes/bold.ts +0 -67
package/dist/index.js
CHANGED
|
@@ -91,18 +91,18 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
91
91
|
const results = [];
|
|
92
92
|
for (let i = 0; i < points.length; i++) {
|
|
93
93
|
const pt = points[i];
|
|
94
|
-
const
|
|
94
|
+
const labelWidth2 = pt.label.length * fontSize * CHAR_WIDTH_RATIO + 8;
|
|
95
95
|
let best = null;
|
|
96
96
|
const directions = [
|
|
97
97
|
{
|
|
98
98
|
// Above
|
|
99
99
|
gen: (offset) => {
|
|
100
|
-
const lx = pt.cx -
|
|
100
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
101
101
|
const ly = pt.cy - offset - labelHeight;
|
|
102
|
-
if (ly < chartBounds.top || lx < chartBounds.left || lx +
|
|
102
|
+
if (ly < chartBounds.top || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
|
|
103
103
|
return null;
|
|
104
104
|
return {
|
|
105
|
-
rect: { x: lx, y: ly, w:
|
|
105
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
106
106
|
textX: pt.cx,
|
|
107
107
|
textY: ly + labelHeight / 2,
|
|
108
108
|
anchor: "middle"
|
|
@@ -112,12 +112,12 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
112
112
|
{
|
|
113
113
|
// Below
|
|
114
114
|
gen: (offset) => {
|
|
115
|
-
const lx = pt.cx -
|
|
115
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
116
116
|
const ly = pt.cy + offset;
|
|
117
|
-
if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx +
|
|
117
|
+
if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
|
|
118
118
|
return null;
|
|
119
119
|
return {
|
|
120
|
-
rect: { x: lx, y: ly, w:
|
|
120
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
121
121
|
textX: pt.cx,
|
|
122
122
|
textY: ly + labelHeight / 2,
|
|
123
123
|
anchor: "middle"
|
|
@@ -129,10 +129,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
129
129
|
gen: (offset) => {
|
|
130
130
|
const lx = pt.cx + offset;
|
|
131
131
|
const ly = pt.cy - labelHeight / 2;
|
|
132
|
-
if (lx +
|
|
132
|
+
if (lx + labelWidth2 > chartBounds.right || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
|
|
133
133
|
return null;
|
|
134
134
|
return {
|
|
135
|
-
rect: { x: lx, y: ly, w:
|
|
135
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
136
136
|
textX: lx,
|
|
137
137
|
textY: pt.cy,
|
|
138
138
|
anchor: "start"
|
|
@@ -142,13 +142,13 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
142
142
|
{
|
|
143
143
|
// Left
|
|
144
144
|
gen: (offset) => {
|
|
145
|
-
const lx = pt.cx - offset -
|
|
145
|
+
const lx = pt.cx - offset - labelWidth2;
|
|
146
146
|
const ly = pt.cy - labelHeight / 2;
|
|
147
147
|
if (lx < chartBounds.left || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
|
|
148
148
|
return null;
|
|
149
149
|
return {
|
|
150
|
-
rect: { x: lx, y: ly, w:
|
|
151
|
-
textX: lx +
|
|
150
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
151
|
+
textX: lx + labelWidth2,
|
|
152
152
|
textY: pt.cy,
|
|
153
153
|
anchor: "end"
|
|
154
154
|
};
|
|
@@ -198,10 +198,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
if (!best) {
|
|
201
|
-
const lx = pt.cx -
|
|
201
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
202
202
|
const ly = pt.cy - minGap - labelHeight;
|
|
203
203
|
best = {
|
|
204
|
-
rect: { x: lx, y: ly, w:
|
|
204
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
205
205
|
textX: pt.cx,
|
|
206
206
|
textY: ly + labelHeight / 2,
|
|
207
207
|
anchor: "middle",
|
|
@@ -836,6 +836,9 @@ var init_reserved_key_registry = __esm({
|
|
|
836
836
|
"value",
|
|
837
837
|
"label",
|
|
838
838
|
"style"
|
|
839
|
+
// `surface:` was removed in the 2026-06-02 defaults-on review — it is no longer
|
|
840
|
+
// a recognized metadata key (the route/edge surface feature was cut; §24B.7).
|
|
841
|
+
// A stray `surface: water` is no longer captured as a reserved key.
|
|
839
842
|
]);
|
|
840
843
|
ORG_REGISTRY = staticRegistry([
|
|
841
844
|
"color",
|
|
@@ -890,9 +893,7 @@ var init_reserved_key_registry = __esm({
|
|
|
890
893
|
BOXES_AND_LINES_REGISTRY = staticRegistry([
|
|
891
894
|
"color",
|
|
892
895
|
"description",
|
|
893
|
-
"
|
|
894
|
-
"split",
|
|
895
|
-
"fanout"
|
|
896
|
+
"value"
|
|
896
897
|
]);
|
|
897
898
|
TIMELINE_REGISTRY = staticRegistry([
|
|
898
899
|
"color",
|
|
@@ -1898,77 +1899,266 @@ function getSegmentColors(palette, count) {
|
|
|
1898
1899
|
(_, i) => hslToHex(Math.round((startHue + i * step) % 360), avgS, avgL)
|
|
1899
1900
|
);
|
|
1900
1901
|
}
|
|
1902
|
+
function politicalTints(palette, count, isDark) {
|
|
1903
|
+
if (count <= 0) return [];
|
|
1904
|
+
const base = isDark ? palette.surface : palette.bg;
|
|
1905
|
+
const c = palette.colors;
|
|
1906
|
+
const swatches = [
|
|
1907
|
+
.../* @__PURE__ */ new Set([
|
|
1908
|
+
c.green,
|
|
1909
|
+
c.yellow,
|
|
1910
|
+
c.orange,
|
|
1911
|
+
c.purple,
|
|
1912
|
+
c.red,
|
|
1913
|
+
c.teal,
|
|
1914
|
+
c.cyan,
|
|
1915
|
+
c.blue
|
|
1916
|
+
])
|
|
1917
|
+
];
|
|
1918
|
+
const bands = isDark ? POLITICAL_TINT_BANDS.dark : POLITICAL_TINT_BANDS.light;
|
|
1919
|
+
const out = [];
|
|
1920
|
+
for (const pct of bands) {
|
|
1921
|
+
if (out.length >= count) break;
|
|
1922
|
+
for (const s of swatches) out.push(mix(s, base, pct));
|
|
1923
|
+
}
|
|
1924
|
+
return out.slice(0, count);
|
|
1925
|
+
}
|
|
1926
|
+
var POLITICAL_TINT_BANDS;
|
|
1901
1927
|
var init_color_utils = __esm({
|
|
1902
1928
|
"src/palettes/color-utils.ts"() {
|
|
1903
1929
|
"use strict";
|
|
1930
|
+
POLITICAL_TINT_BANDS = {
|
|
1931
|
+
light: [32, 48, 64, 80],
|
|
1932
|
+
dark: [44, 58, 72, 86]
|
|
1933
|
+
};
|
|
1904
1934
|
}
|
|
1905
1935
|
});
|
|
1906
1936
|
|
|
1907
|
-
// src/palettes/
|
|
1908
|
-
var
|
|
1909
|
-
var
|
|
1910
|
-
"src/palettes/
|
|
1937
|
+
// src/palettes/atlas.ts
|
|
1938
|
+
var atlasPalette;
|
|
1939
|
+
var init_atlas = __esm({
|
|
1940
|
+
"src/palettes/atlas.ts"() {
|
|
1911
1941
|
"use strict";
|
|
1912
1942
|
init_registry();
|
|
1913
|
-
|
|
1914
|
-
id: "
|
|
1915
|
-
name: "
|
|
1943
|
+
atlasPalette = {
|
|
1944
|
+
id: "atlas",
|
|
1945
|
+
name: "Atlas",
|
|
1916
1946
|
light: {
|
|
1917
|
-
bg: "#
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1947
|
+
bg: "#f3ead3",
|
|
1948
|
+
// warm manila / parchment
|
|
1949
|
+
surface: "#ece0c0",
|
|
1950
|
+
// deeper paper (cards, panels)
|
|
1951
|
+
overlay: "#e8dab8",
|
|
1952
|
+
// popovers, dropdowns
|
|
1953
|
+
border: "#bcaa86",
|
|
1954
|
+
// muted sepia rule line
|
|
1955
|
+
text: "#463a26",
|
|
1956
|
+
// aged sepia-brown ink
|
|
1957
|
+
textMuted: "#7a6a4f",
|
|
1958
|
+
// faded annotation ink
|
|
1959
|
+
textOnFillLight: "#f7f1de",
|
|
1960
|
+
// parchment (light text on dark fills)
|
|
1961
|
+
textOnFillDark: "#3a2e1c",
|
|
1962
|
+
// deep ink (dark text on light fills)
|
|
1963
|
+
primary: "#5b7a99",
|
|
1964
|
+
// pull-down map ocean (steel-blue)
|
|
1965
|
+
secondary: "#7e9a6f",
|
|
1966
|
+
// lowland sage / celadon
|
|
1967
|
+
accent: "#b07f7c",
|
|
1968
|
+
// dusty rose
|
|
1969
|
+
destructive: "#b25a45",
|
|
1970
|
+
// brick / terracotta
|
|
1929
1971
|
colors: {
|
|
1930
|
-
red: "#
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1972
|
+
red: "#bf6a52",
|
|
1973
|
+
// terracotta brick
|
|
1974
|
+
orange: "#cf9a5c",
|
|
1975
|
+
// map tan / ochre
|
|
1976
|
+
yellow: "#cdb35e",
|
|
1977
|
+
// straw / muted lemon
|
|
1978
|
+
green: "#7e9a6f",
|
|
1979
|
+
// sage / celadon lowland
|
|
1980
|
+
blue: "#5b7a99",
|
|
1981
|
+
// steel-blue ocean
|
|
1982
|
+
purple: "#9a7fa6",
|
|
1983
|
+
// dusty lilac / mauve
|
|
1984
|
+
teal: "#6fa094",
|
|
1985
|
+
// muted seafoam
|
|
1986
|
+
cyan: "#79a7b5",
|
|
1987
|
+
// shallow-water blue
|
|
1988
|
+
gray: "#8a7d68",
|
|
1989
|
+
// warm taupe
|
|
1990
|
+
black: "#463a26",
|
|
1991
|
+
// ink
|
|
1992
|
+
white: "#ece0c0"
|
|
1993
|
+
// paper
|
|
1941
1994
|
}
|
|
1942
1995
|
},
|
|
1943
1996
|
dark: {
|
|
1944
|
-
bg: "#
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1997
|
+
bg: "#1e2a33",
|
|
1998
|
+
// deep map ocean (night globe)
|
|
1999
|
+
surface: "#27353f",
|
|
2000
|
+
// raised ocean
|
|
2001
|
+
overlay: "#2e3d48",
|
|
2002
|
+
// popovers, dropdowns
|
|
2003
|
+
border: "#3d4f5c",
|
|
2004
|
+
// depth-contour line
|
|
2005
|
+
text: "#e8dcc0",
|
|
2006
|
+
// parchment ink, inverted
|
|
2007
|
+
textMuted: "#a89a7d",
|
|
2008
|
+
// faded label
|
|
2009
|
+
textOnFillLight: "#f7f1de",
|
|
2010
|
+
// parchment
|
|
2011
|
+
textOnFillDark: "#1a242c",
|
|
2012
|
+
// deep ocean ink
|
|
2013
|
+
primary: "#7ba0bf",
|
|
2014
|
+
// brighter ocean
|
|
2015
|
+
secondary: "#9bb588",
|
|
2016
|
+
// sage, lifted
|
|
2017
|
+
accent: "#cf9a96",
|
|
2018
|
+
// dusty rose, lifted
|
|
2019
|
+
destructive: "#c9745c",
|
|
2020
|
+
// brick, lifted
|
|
1956
2021
|
colors: {
|
|
1957
|
-
red: "#
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2022
|
+
red: "#cf7a60",
|
|
2023
|
+
// terracotta
|
|
2024
|
+
orange: "#d9a96a",
|
|
2025
|
+
// tan / ochre
|
|
2026
|
+
yellow: "#d8c074",
|
|
2027
|
+
// straw
|
|
2028
|
+
green: "#9bb588",
|
|
2029
|
+
// sage lowland
|
|
2030
|
+
blue: "#7ba0bf",
|
|
2031
|
+
// ocean
|
|
2032
|
+
purple: "#b59ac0",
|
|
2033
|
+
// lilac / mauve
|
|
2034
|
+
teal: "#85b3a6",
|
|
2035
|
+
// seafoam
|
|
2036
|
+
cyan: "#92bccb",
|
|
2037
|
+
// shallow-water blue
|
|
2038
|
+
gray: "#9a8d76",
|
|
2039
|
+
// warm taupe
|
|
2040
|
+
black: "#27353f",
|
|
2041
|
+
// raised ocean
|
|
2042
|
+
white: "#e8dcc0"
|
|
2043
|
+
// parchment
|
|
1968
2044
|
}
|
|
1969
2045
|
}
|
|
1970
2046
|
};
|
|
1971
|
-
registerPalette(
|
|
2047
|
+
registerPalette(atlasPalette);
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
// src/palettes/blueprint.ts
|
|
2052
|
+
var blueprintPalette;
|
|
2053
|
+
var init_blueprint = __esm({
|
|
2054
|
+
"src/palettes/blueprint.ts"() {
|
|
2055
|
+
"use strict";
|
|
2056
|
+
init_registry();
|
|
2057
|
+
blueprintPalette = {
|
|
2058
|
+
id: "blueprint",
|
|
2059
|
+
name: "Blueprint",
|
|
2060
|
+
light: {
|
|
2061
|
+
bg: "#f4f8fb",
|
|
2062
|
+
// pale drafting white (faint cyan)
|
|
2063
|
+
surface: "#e6eef4",
|
|
2064
|
+
// drafting panel
|
|
2065
|
+
overlay: "#dde9f1",
|
|
2066
|
+
// popovers, dropdowns
|
|
2067
|
+
border: "#aac3d6",
|
|
2068
|
+
// pale blue grid line
|
|
2069
|
+
text: "#123a5e",
|
|
2070
|
+
// blueprint navy ink
|
|
2071
|
+
textMuted: "#4f7390",
|
|
2072
|
+
// faint draft note
|
|
2073
|
+
textOnFillLight: "#f4f8fb",
|
|
2074
|
+
// drafting white
|
|
2075
|
+
textOnFillDark: "#0c2f4d",
|
|
2076
|
+
// deep blueprint navy
|
|
2077
|
+
primary: "#1f5e8c",
|
|
2078
|
+
// blueprint blue
|
|
2079
|
+
secondary: "#5b7d96",
|
|
2080
|
+
// steel
|
|
2081
|
+
accent: "#b08a3e",
|
|
2082
|
+
// draftsman's ochre highlight
|
|
2083
|
+
destructive: "#c0504d",
|
|
2084
|
+
// correction red
|
|
2085
|
+
colors: {
|
|
2086
|
+
red: "#c25a4e",
|
|
2087
|
+
// correction red
|
|
2088
|
+
orange: "#c2823e",
|
|
2089
|
+
// ochre
|
|
2090
|
+
yellow: "#c2a843",
|
|
2091
|
+
// pencil gold
|
|
2092
|
+
green: "#4f8a6b",
|
|
2093
|
+
// drafting green
|
|
2094
|
+
blue: "#1f5e8c",
|
|
2095
|
+
// blueprint blue
|
|
2096
|
+
purple: "#6f5e96",
|
|
2097
|
+
// indigo pencil
|
|
2098
|
+
teal: "#3a8a8a",
|
|
2099
|
+
// teal
|
|
2100
|
+
cyan: "#3f8fb5",
|
|
2101
|
+
// cyan
|
|
2102
|
+
gray: "#7e8e98",
|
|
2103
|
+
// graphite
|
|
2104
|
+
black: "#123a5e",
|
|
2105
|
+
// navy ink
|
|
2106
|
+
white: "#e6eef4"
|
|
2107
|
+
// panel
|
|
2108
|
+
}
|
|
2109
|
+
},
|
|
2110
|
+
dark: {
|
|
2111
|
+
bg: "#103a5e",
|
|
2112
|
+
// deep blueprint blue (cyanotype ground)
|
|
2113
|
+
surface: "#16466e",
|
|
2114
|
+
// raised sheet
|
|
2115
|
+
overlay: "#1c5180",
|
|
2116
|
+
// popovers, dropdowns
|
|
2117
|
+
border: "#3a6f96",
|
|
2118
|
+
// grid line
|
|
2119
|
+
text: "#eaf2f8",
|
|
2120
|
+
// chalk white
|
|
2121
|
+
textMuted: "#9fc0d6",
|
|
2122
|
+
// faint chalk note
|
|
2123
|
+
textOnFillLight: "#eaf2f8",
|
|
2124
|
+
// chalk white
|
|
2125
|
+
textOnFillDark: "#0c2f4d",
|
|
2126
|
+
// deep blueprint navy
|
|
2127
|
+
primary: "#7fb8d8",
|
|
2128
|
+
// chalk cyan
|
|
2129
|
+
secondary: "#9fb8c8",
|
|
2130
|
+
// pale steel
|
|
2131
|
+
accent: "#d8c27a",
|
|
2132
|
+
// chalk amber
|
|
2133
|
+
destructive: "#e08a7a",
|
|
2134
|
+
// chalk correction red
|
|
2135
|
+
colors: {
|
|
2136
|
+
red: "#e0907e",
|
|
2137
|
+
// chalk red
|
|
2138
|
+
orange: "#e0ab78",
|
|
2139
|
+
// chalk amber
|
|
2140
|
+
yellow: "#e3d089",
|
|
2141
|
+
// chalk gold
|
|
2142
|
+
green: "#93c79e",
|
|
2143
|
+
// chalk green
|
|
2144
|
+
blue: "#8ec3e0",
|
|
2145
|
+
// chalk cyan-blue
|
|
2146
|
+
purple: "#b6a6d8",
|
|
2147
|
+
// chalk indigo
|
|
2148
|
+
teal: "#84c7c2",
|
|
2149
|
+
// chalk teal
|
|
2150
|
+
cyan: "#9fd6e0",
|
|
2151
|
+
// chalk cyan
|
|
2152
|
+
gray: "#aebecb",
|
|
2153
|
+
// chalk graphite
|
|
2154
|
+
black: "#16466e",
|
|
2155
|
+
// raised sheet
|
|
2156
|
+
white: "#eaf2f8"
|
|
2157
|
+
// chalk white
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
registerPalette(blueprintPalette);
|
|
1972
2162
|
}
|
|
1973
2163
|
});
|
|
1974
2164
|
|
|
@@ -2465,6 +2655,120 @@ var init_rose_pine = __esm({
|
|
|
2465
2655
|
}
|
|
2466
2656
|
});
|
|
2467
2657
|
|
|
2658
|
+
// src/palettes/slate.ts
|
|
2659
|
+
var slatePalette;
|
|
2660
|
+
var init_slate = __esm({
|
|
2661
|
+
"src/palettes/slate.ts"() {
|
|
2662
|
+
"use strict";
|
|
2663
|
+
init_registry();
|
|
2664
|
+
slatePalette = {
|
|
2665
|
+
id: "slate",
|
|
2666
|
+
name: "Slate",
|
|
2667
|
+
light: {
|
|
2668
|
+
bg: "#ffffff",
|
|
2669
|
+
// clean slide white
|
|
2670
|
+
surface: "#f3f5f8",
|
|
2671
|
+
// light cool-gray panel
|
|
2672
|
+
overlay: "#eaeef3",
|
|
2673
|
+
// popovers, dropdowns
|
|
2674
|
+
border: "#d4dae1",
|
|
2675
|
+
// hairline rule
|
|
2676
|
+
text: "#1f2933",
|
|
2677
|
+
// near-black slate (softer than pure black)
|
|
2678
|
+
textMuted: "#5b6672",
|
|
2679
|
+
// secondary label
|
|
2680
|
+
textOnFillLight: "#ffffff",
|
|
2681
|
+
// light text on dark fills
|
|
2682
|
+
textOnFillDark: "#1f2933",
|
|
2683
|
+
// dark text on light fills
|
|
2684
|
+
primary: "#3b6ea5",
|
|
2685
|
+
// confident corporate blue
|
|
2686
|
+
secondary: "#5b6672",
|
|
2687
|
+
// slate gray
|
|
2688
|
+
accent: "#3a9188",
|
|
2689
|
+
// muted teal accent
|
|
2690
|
+
destructive: "#c0504d",
|
|
2691
|
+
// brick red
|
|
2692
|
+
colors: {
|
|
2693
|
+
red: "#c0504d",
|
|
2694
|
+
// brick
|
|
2695
|
+
orange: "#cc7a33",
|
|
2696
|
+
// muted amber
|
|
2697
|
+
yellow: "#c9a227",
|
|
2698
|
+
// gold (not neon)
|
|
2699
|
+
green: "#5b9357",
|
|
2700
|
+
// forest / sage
|
|
2701
|
+
blue: "#3b6ea5",
|
|
2702
|
+
// corporate blue
|
|
2703
|
+
purple: "#7d5ba6",
|
|
2704
|
+
// muted violet
|
|
2705
|
+
teal: "#3a9188",
|
|
2706
|
+
// teal
|
|
2707
|
+
cyan: "#4f96c4",
|
|
2708
|
+
// steel cyan
|
|
2709
|
+
gray: "#7e8a97",
|
|
2710
|
+
// cool gray
|
|
2711
|
+
black: "#1f2933",
|
|
2712
|
+
// slate ink
|
|
2713
|
+
white: "#f3f5f8"
|
|
2714
|
+
// panel
|
|
2715
|
+
}
|
|
2716
|
+
},
|
|
2717
|
+
dark: {
|
|
2718
|
+
bg: "#161b22",
|
|
2719
|
+
// deep slate (keynote dark)
|
|
2720
|
+
surface: "#202833",
|
|
2721
|
+
// raised panel
|
|
2722
|
+
overlay: "#29323e",
|
|
2723
|
+
// popovers, dropdowns
|
|
2724
|
+
border: "#38424f",
|
|
2725
|
+
// divider
|
|
2726
|
+
text: "#e6eaef",
|
|
2727
|
+
// off-white
|
|
2728
|
+
textMuted: "#9aa5b1",
|
|
2729
|
+
// secondary label
|
|
2730
|
+
textOnFillLight: "#ffffff",
|
|
2731
|
+
// light text on dark fills
|
|
2732
|
+
textOnFillDark: "#161b22",
|
|
2733
|
+
// dark text on light fills
|
|
2734
|
+
primary: "#5b9bd5",
|
|
2735
|
+
// lifted corporate blue
|
|
2736
|
+
secondary: "#8593a3",
|
|
2737
|
+
// slate gray, lifted
|
|
2738
|
+
accent: "#45b3a3",
|
|
2739
|
+
// teal, lifted
|
|
2740
|
+
destructive: "#e07b6e",
|
|
2741
|
+
// brick, lifted
|
|
2742
|
+
colors: {
|
|
2743
|
+
red: "#e07b6e",
|
|
2744
|
+
// brick
|
|
2745
|
+
orange: "#e0975a",
|
|
2746
|
+
// amber
|
|
2747
|
+
yellow: "#d9bd5a",
|
|
2748
|
+
// gold
|
|
2749
|
+
green: "#74b56e",
|
|
2750
|
+
// forest / sage
|
|
2751
|
+
blue: "#5b9bd5",
|
|
2752
|
+
// corporate blue
|
|
2753
|
+
purple: "#a585c9",
|
|
2754
|
+
// violet
|
|
2755
|
+
teal: "#45b3a3",
|
|
2756
|
+
// teal
|
|
2757
|
+
cyan: "#62b0d9",
|
|
2758
|
+
// steel cyan
|
|
2759
|
+
gray: "#95a1ae",
|
|
2760
|
+
// cool gray
|
|
2761
|
+
black: "#202833",
|
|
2762
|
+
// raised panel
|
|
2763
|
+
white: "#e6eaef"
|
|
2764
|
+
// off-white
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
};
|
|
2768
|
+
registerPalette(slatePalette);
|
|
2769
|
+
}
|
|
2770
|
+
});
|
|
2771
|
+
|
|
2468
2772
|
// src/palettes/solarized.ts
|
|
2469
2773
|
var solarizedPalette;
|
|
2470
2774
|
var init_solarized = __esm({
|
|
@@ -2560,6 +2864,120 @@ var init_solarized = __esm({
|
|
|
2560
2864
|
}
|
|
2561
2865
|
});
|
|
2562
2866
|
|
|
2867
|
+
// src/palettes/tidewater.ts
|
|
2868
|
+
var tidewaterPalette;
|
|
2869
|
+
var init_tidewater = __esm({
|
|
2870
|
+
"src/palettes/tidewater.ts"() {
|
|
2871
|
+
"use strict";
|
|
2872
|
+
init_registry();
|
|
2873
|
+
tidewaterPalette = {
|
|
2874
|
+
id: "tidewater",
|
|
2875
|
+
name: "Tidewater",
|
|
2876
|
+
light: {
|
|
2877
|
+
bg: "#eceff0",
|
|
2878
|
+
// weathered sea-mist paper
|
|
2879
|
+
surface: "#e0e4e3",
|
|
2880
|
+
// worn deck panel
|
|
2881
|
+
overlay: "#dadfdf",
|
|
2882
|
+
// popovers, dropdowns
|
|
2883
|
+
border: "#a9b2b3",
|
|
2884
|
+
// muted slate rule
|
|
2885
|
+
text: "#18313f",
|
|
2886
|
+
// ship's-log navy ink
|
|
2887
|
+
textMuted: "#51636b",
|
|
2888
|
+
// faded log entry
|
|
2889
|
+
textOnFillLight: "#f3f5f3",
|
|
2890
|
+
// weathered white
|
|
2891
|
+
textOnFillDark: "#162c38",
|
|
2892
|
+
// deep navy
|
|
2893
|
+
primary: "#1f4e6b",
|
|
2894
|
+
// deep-sea navy
|
|
2895
|
+
secondary: "#b08a4f",
|
|
2896
|
+
// rope / manila tan
|
|
2897
|
+
accent: "#c69a3e",
|
|
2898
|
+
// brass
|
|
2899
|
+
destructive: "#c1433a",
|
|
2900
|
+
// signal-flag red
|
|
2901
|
+
colors: {
|
|
2902
|
+
red: "#c1433a",
|
|
2903
|
+
// signal-flag red
|
|
2904
|
+
orange: "#cc7a38",
|
|
2905
|
+
// weathered amber
|
|
2906
|
+
yellow: "#d6bf5a",
|
|
2907
|
+
// brass gold
|
|
2908
|
+
green: "#4f8a6b",
|
|
2909
|
+
// sea-glass green
|
|
2910
|
+
blue: "#1f4e6b",
|
|
2911
|
+
// deep-sea navy
|
|
2912
|
+
purple: "#6a5a8c",
|
|
2913
|
+
// twilight harbor
|
|
2914
|
+
teal: "#3d8c8c",
|
|
2915
|
+
// sea-glass teal
|
|
2916
|
+
cyan: "#4f9bb5",
|
|
2917
|
+
// shallow water
|
|
2918
|
+
gray: "#8a8d86",
|
|
2919
|
+
// driftwood gray
|
|
2920
|
+
black: "#18313f",
|
|
2921
|
+
// navy ink
|
|
2922
|
+
white: "#e0e4e3"
|
|
2923
|
+
// deck panel
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
dark: {
|
|
2927
|
+
bg: "#0f2230",
|
|
2928
|
+
// night-harbor deep sea
|
|
2929
|
+
surface: "#16303f",
|
|
2930
|
+
// raised hull
|
|
2931
|
+
overlay: "#1d3a4a",
|
|
2932
|
+
// popovers, dropdowns
|
|
2933
|
+
border: "#2c4856",
|
|
2934
|
+
// rigging line
|
|
2935
|
+
text: "#e6ebe8",
|
|
2936
|
+
// weathered white
|
|
2937
|
+
textMuted: "#9aaab0",
|
|
2938
|
+
// faded label
|
|
2939
|
+
textOnFillLight: "#f3f5f3",
|
|
2940
|
+
// weathered white
|
|
2941
|
+
textOnFillDark: "#0f2230",
|
|
2942
|
+
// deep sea
|
|
2943
|
+
primary: "#4f9bc4",
|
|
2944
|
+
// lifted sea blue
|
|
2945
|
+
secondary: "#c9a46a",
|
|
2946
|
+
// rope tan, lifted
|
|
2947
|
+
accent: "#d9b25a",
|
|
2948
|
+
// brass, lifted
|
|
2949
|
+
destructive: "#e06a5e",
|
|
2950
|
+
// signal red, lifted
|
|
2951
|
+
colors: {
|
|
2952
|
+
red: "#e06a5e",
|
|
2953
|
+
// signal-flag red
|
|
2954
|
+
orange: "#df9a52",
|
|
2955
|
+
// amber
|
|
2956
|
+
yellow: "#e0c662",
|
|
2957
|
+
// brass gold
|
|
2958
|
+
green: "#6fb58c",
|
|
2959
|
+
// sea-glass green
|
|
2960
|
+
blue: "#4f9bc4",
|
|
2961
|
+
// sea blue
|
|
2962
|
+
purple: "#9486bf",
|
|
2963
|
+
// twilight harbor
|
|
2964
|
+
teal: "#5cb0ac",
|
|
2965
|
+
// sea-glass teal
|
|
2966
|
+
cyan: "#62b4cf",
|
|
2967
|
+
// shallow water
|
|
2968
|
+
gray: "#9aa39c",
|
|
2969
|
+
// driftwood gray
|
|
2970
|
+
black: "#16303f",
|
|
2971
|
+
// raised hull
|
|
2972
|
+
white: "#e6ebe8"
|
|
2973
|
+
// weathered white
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
registerPalette(tidewaterPalette);
|
|
2978
|
+
}
|
|
2979
|
+
});
|
|
2980
|
+
|
|
2563
2981
|
// src/palettes/tokyo-night.ts
|
|
2564
2982
|
var tokyoNightPalette;
|
|
2565
2983
|
var init_tokyo_night = __esm({
|
|
@@ -2835,7 +3253,8 @@ var init_monokai = __esm({
|
|
|
2835
3253
|
// src/palettes/index.ts
|
|
2836
3254
|
var palettes_exports = {};
|
|
2837
3255
|
__export(palettes_exports, {
|
|
2838
|
-
|
|
3256
|
+
atlasPalette: () => atlasPalette,
|
|
3257
|
+
blueprintPalette: () => blueprintPalette,
|
|
2839
3258
|
catppuccinPalette: () => catppuccinPalette,
|
|
2840
3259
|
contrastText: () => contrastText,
|
|
2841
3260
|
draculaPalette: () => draculaPalette,
|
|
@@ -2856,7 +3275,9 @@ __export(palettes_exports, {
|
|
|
2856
3275
|
rosePinePalette: () => rosePinePalette,
|
|
2857
3276
|
shade: () => shade,
|
|
2858
3277
|
shapeFill: () => shapeFill,
|
|
3278
|
+
slatePalette: () => slatePalette,
|
|
2859
3279
|
solarizedPalette: () => solarizedPalette,
|
|
3280
|
+
tidewaterPalette: () => tidewaterPalette,
|
|
2860
3281
|
tint: () => tint,
|
|
2861
3282
|
tokyoNightPalette: () => tokyoNightPalette
|
|
2862
3283
|
});
|
|
@@ -2866,17 +3287,21 @@ var init_palettes = __esm({
|
|
|
2866
3287
|
"use strict";
|
|
2867
3288
|
init_registry();
|
|
2868
3289
|
init_color_utils();
|
|
2869
|
-
|
|
3290
|
+
init_atlas();
|
|
3291
|
+
init_blueprint();
|
|
2870
3292
|
init_catppuccin();
|
|
2871
3293
|
init_gruvbox();
|
|
2872
3294
|
init_nord();
|
|
2873
3295
|
init_one_dark();
|
|
2874
3296
|
init_rose_pine();
|
|
3297
|
+
init_slate();
|
|
2875
3298
|
init_solarized();
|
|
3299
|
+
init_tidewater();
|
|
2876
3300
|
init_tokyo_night();
|
|
2877
3301
|
init_dracula();
|
|
2878
3302
|
init_monokai();
|
|
2879
|
-
|
|
3303
|
+
init_atlas();
|
|
3304
|
+
init_blueprint();
|
|
2880
3305
|
init_catppuccin();
|
|
2881
3306
|
init_dracula();
|
|
2882
3307
|
init_gruvbox();
|
|
@@ -2884,9 +3309,15 @@ var init_palettes = __esm({
|
|
|
2884
3309
|
init_nord();
|
|
2885
3310
|
init_one_dark();
|
|
2886
3311
|
init_rose_pine();
|
|
3312
|
+
init_slate();
|
|
2887
3313
|
init_solarized();
|
|
3314
|
+
init_tidewater();
|
|
2888
3315
|
init_tokyo_night();
|
|
2889
3316
|
palettes = {
|
|
3317
|
+
atlas: atlasPalette,
|
|
3318
|
+
blueprint: blueprintPalette,
|
|
3319
|
+
slate: slatePalette,
|
|
3320
|
+
tidewater: tidewaterPalette,
|
|
2890
3321
|
nord: nordPalette,
|
|
2891
3322
|
catppuccin: catppuccinPalette,
|
|
2892
3323
|
solarized: solarizedPalette,
|
|
@@ -2895,8 +3326,7 @@ var init_palettes = __esm({
|
|
|
2895
3326
|
oneDark: oneDarkPalette,
|
|
2896
3327
|
rosePine: rosePinePalette,
|
|
2897
3328
|
dracula: draculaPalette,
|
|
2898
|
-
monokai: monokaiPalette
|
|
2899
|
-
bold: boldPalette
|
|
3329
|
+
monokai: monokaiPalette
|
|
2900
3330
|
};
|
|
2901
3331
|
}
|
|
2902
3332
|
});
|
|
@@ -3406,6 +3836,9 @@ function controlsGroupCapsuleWidth(toggles) {
|
|
|
3406
3836
|
}
|
|
3407
3837
|
return w;
|
|
3408
3838
|
}
|
|
3839
|
+
function isAppHostedControls(config, isExport) {
|
|
3840
|
+
return !isExport && config.controlsHost === "app" && !!config.controlsGroup && config.controlsGroup.toggles.length > 0;
|
|
3841
|
+
}
|
|
3409
3842
|
function buildControlsGroupLayout(config, state) {
|
|
3410
3843
|
const cg = config.controlsGroup;
|
|
3411
3844
|
if (!cg || cg.toggles.length === 0) return void 0;
|
|
@@ -3459,6 +3892,7 @@ function buildControlsGroupLayout(config, state) {
|
|
|
3459
3892
|
function computeLegendLayout(config, state, containerWidth) {
|
|
3460
3893
|
const { groups, controls: configControls, mode } = config;
|
|
3461
3894
|
const isExport = mode === "export";
|
|
3895
|
+
const gated = isAppHostedControls(config, isExport);
|
|
3462
3896
|
const activeGroupName = state.activeGroup?.toLowerCase() ?? null;
|
|
3463
3897
|
if (isExport && !activeGroupName) {
|
|
3464
3898
|
return {
|
|
@@ -3469,7 +3903,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3469
3903
|
pills: []
|
|
3470
3904
|
};
|
|
3471
3905
|
}
|
|
3472
|
-
const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
|
|
3906
|
+
const controlsGroupLayout = isExport || gated ? void 0 : buildControlsGroupLayout(config, state);
|
|
3473
3907
|
const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
|
|
3474
3908
|
if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
|
|
3475
3909
|
return {
|
|
@@ -8371,8 +8805,8 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8371
8805
|
const pt = points[i];
|
|
8372
8806
|
const ptSize = pt.size ?? symbolSize;
|
|
8373
8807
|
const minGap = ptSize / 2 + 4;
|
|
8374
|
-
const
|
|
8375
|
-
const labelX = pt.px -
|
|
8808
|
+
const labelWidth2 = pt.name.length * fontSize * 0.6 + 8;
|
|
8809
|
+
const labelX = pt.px - labelWidth2 / 2;
|
|
8376
8810
|
let bestLabelY = 0;
|
|
8377
8811
|
let bestOffset = Infinity;
|
|
8378
8812
|
let placed = false;
|
|
@@ -8384,7 +8818,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8384
8818
|
const candidate = {
|
|
8385
8819
|
x: labelX,
|
|
8386
8820
|
y: labelY,
|
|
8387
|
-
w:
|
|
8821
|
+
w: labelWidth2,
|
|
8388
8822
|
h: labelHeight
|
|
8389
8823
|
};
|
|
8390
8824
|
let collision = false;
|
|
@@ -8426,7 +8860,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8426
8860
|
const labelRect = {
|
|
8427
8861
|
x: labelX,
|
|
8428
8862
|
y: bestLabelY,
|
|
8429
|
-
w:
|
|
8863
|
+
w: labelWidth2,
|
|
8430
8864
|
h: labelHeight
|
|
8431
8865
|
};
|
|
8432
8866
|
placedLabels.push(labelRect);
|
|
@@ -8462,7 +8896,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8462
8896
|
shape: {
|
|
8463
8897
|
x: labelX - bgPad,
|
|
8464
8898
|
y: bestLabelY - bgPad,
|
|
8465
|
-
width:
|
|
8899
|
+
width: labelWidth2 + bgPad * 2,
|
|
8466
8900
|
height: labelHeight + bgPad * 2
|
|
8467
8901
|
},
|
|
8468
8902
|
style: { fill: bg },
|
|
@@ -15898,10 +16332,6 @@ function parseMap(content) {
|
|
|
15898
16332
|
handleTag(trimmed, lineNumber);
|
|
15899
16333
|
continue;
|
|
15900
16334
|
}
|
|
15901
|
-
if ((firstWord === "muted" || firstWord === "natural") && trimmed === firstWord) {
|
|
15902
|
-
handleDirective(firstWord, "", lineNumber);
|
|
15903
|
-
continue;
|
|
15904
|
-
}
|
|
15905
16335
|
if (DIRECTIVE_SET.has(firstWord) && !trimmed.slice(firstWord.length).trimStart().startsWith(":")) {
|
|
15906
16336
|
handleDirective(
|
|
15907
16337
|
firstWord,
|
|
@@ -15948,24 +16378,6 @@ function parseMap(content) {
|
|
|
15948
16378
|
pushWarning(line12, `Duplicate directive "${key}" \u2014 last value wins.`);
|
|
15949
16379
|
};
|
|
15950
16380
|
switch (key) {
|
|
15951
|
-
case "region":
|
|
15952
|
-
dup(d.region);
|
|
15953
|
-
d.region = value;
|
|
15954
|
-
break;
|
|
15955
|
-
case "projection":
|
|
15956
|
-
dup(d.projection);
|
|
15957
|
-
if (value && ![
|
|
15958
|
-
"equirectangular",
|
|
15959
|
-
"natural-earth",
|
|
15960
|
-
"albers-usa",
|
|
15961
|
-
"mercator"
|
|
15962
|
-
].includes(value))
|
|
15963
|
-
pushWarning(
|
|
15964
|
-
line12,
|
|
15965
|
-
`Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
|
|
15966
|
-
);
|
|
15967
|
-
d.projection = value;
|
|
15968
|
-
break;
|
|
15969
16381
|
case "region-metric": {
|
|
15970
16382
|
dup(d.regionMetric);
|
|
15971
16383
|
const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
|
|
@@ -15981,91 +16393,43 @@ function parseMap(content) {
|
|
|
15981
16393
|
dup(d.flowMetric);
|
|
15982
16394
|
d.flowMetric = value;
|
|
15983
16395
|
break;
|
|
15984
|
-
case "
|
|
15985
|
-
dup(d.
|
|
15986
|
-
|
|
15987
|
-
const s = parseScale(value, line12);
|
|
15988
|
-
if (s) d.scale = s;
|
|
15989
|
-
}
|
|
15990
|
-
break;
|
|
15991
|
-
case "region-labels":
|
|
15992
|
-
dup(d.regionLabels);
|
|
15993
|
-
if (value && !["full", "abbrev", "off"].includes(value))
|
|
15994
|
-
pushWarning(
|
|
15995
|
-
line12,
|
|
15996
|
-
`Unknown region-labels "${value}" (expected full | abbrev | off).`
|
|
15997
|
-
);
|
|
15998
|
-
d.regionLabels = value;
|
|
15999
|
-
break;
|
|
16000
|
-
case "poi-labels":
|
|
16001
|
-
dup(d.poiLabels);
|
|
16002
|
-
if (value && !["off", "auto", "all"].includes(value))
|
|
16003
|
-
pushWarning(
|
|
16004
|
-
line12,
|
|
16005
|
-
`Unknown poi-labels "${value}" (expected off | auto | all).`
|
|
16006
|
-
);
|
|
16007
|
-
d.poiLabels = value;
|
|
16008
|
-
break;
|
|
16009
|
-
case "default-country":
|
|
16010
|
-
dup(d.defaultCountry);
|
|
16011
|
-
d.defaultCountry = value;
|
|
16012
|
-
break;
|
|
16013
|
-
case "default-state":
|
|
16014
|
-
dup(d.defaultState);
|
|
16015
|
-
d.defaultState = value;
|
|
16396
|
+
case "locale":
|
|
16397
|
+
dup(d.locale);
|
|
16398
|
+
d.locale = value;
|
|
16016
16399
|
break;
|
|
16017
16400
|
case "active-tag":
|
|
16018
16401
|
dup(d.activeTag);
|
|
16019
16402
|
d.activeTag = value;
|
|
16020
16403
|
break;
|
|
16404
|
+
case "caption":
|
|
16405
|
+
dup(d.caption);
|
|
16406
|
+
d.caption = value;
|
|
16407
|
+
break;
|
|
16408
|
+
// ── Cosmetic `no-*` opt-outs: bare flags, idempotent (mirror `no-legend`,
|
|
16409
|
+
// no dup warning); each defaults the feature ON when absent. ──
|
|
16021
16410
|
case "no-legend":
|
|
16022
16411
|
d.noLegend = true;
|
|
16023
16412
|
break;
|
|
16024
|
-
case "no-
|
|
16025
|
-
d.
|
|
16413
|
+
case "no-coastline":
|
|
16414
|
+
d.noCoastline = true;
|
|
16026
16415
|
break;
|
|
16027
|
-
case "relief":
|
|
16028
|
-
d.
|
|
16416
|
+
case "no-relief":
|
|
16417
|
+
d.noRelief = true;
|
|
16029
16418
|
break;
|
|
16030
|
-
case "
|
|
16031
|
-
|
|
16032
|
-
if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
|
|
16033
|
-
pushWarning(
|
|
16034
|
-
line12,
|
|
16035
|
-
`Conflicting basemap dress \u2014 "${d.basemapStyle}" then "${key}"; last wins.`
|
|
16036
|
-
);
|
|
16037
|
-
d.basemapStyle = key;
|
|
16419
|
+
case "no-context-labels":
|
|
16420
|
+
d.noContextLabels = true;
|
|
16038
16421
|
break;
|
|
16039
|
-
case "
|
|
16040
|
-
|
|
16041
|
-
d.subtitle = value;
|
|
16422
|
+
case "no-region-labels":
|
|
16423
|
+
d.noRegionLabels = true;
|
|
16042
16424
|
break;
|
|
16043
|
-
case "
|
|
16044
|
-
|
|
16045
|
-
|
|
16425
|
+
case "no-poi-labels":
|
|
16426
|
+
d.noPoiLabels = true;
|
|
16427
|
+
break;
|
|
16428
|
+
case "no-colorize":
|
|
16429
|
+
d.noColorize = true;
|
|
16046
16430
|
break;
|
|
16047
16431
|
}
|
|
16048
16432
|
}
|
|
16049
|
-
function parseScale(value, line12) {
|
|
16050
|
-
const toks = value.split(/\s+/).filter(Boolean);
|
|
16051
|
-
const min = Number(toks[0]);
|
|
16052
|
-
const max = Number(toks[1]);
|
|
16053
|
-
if (!Number.isFinite(min) || !Number.isFinite(max)) {
|
|
16054
|
-
pushError(line12, `scale requires numeric <min> <max> (got "${value}").`);
|
|
16055
|
-
return null;
|
|
16056
|
-
}
|
|
16057
|
-
const scale = { min, max };
|
|
16058
|
-
if (toks[2] === "center") {
|
|
16059
|
-
const c = Number(toks[3]);
|
|
16060
|
-
if (Number.isFinite(c)) scale.center = c;
|
|
16061
|
-
else
|
|
16062
|
-
pushError(
|
|
16063
|
-
line12,
|
|
16064
|
-
`scale center requires a number (got "${toks[3] ?? ""}").`
|
|
16065
|
-
);
|
|
16066
|
-
}
|
|
16067
|
-
return scale;
|
|
16068
|
-
}
|
|
16069
16433
|
function handleTag(trimmed, line12) {
|
|
16070
16434
|
const m = matchTagBlockHeading(trimmed);
|
|
16071
16435
|
if (!m) {
|
|
@@ -16265,13 +16629,15 @@ function parseMap(content) {
|
|
|
16265
16629
|
pushError(line12, `Edge has an empty endpoint: "${trimmed}".`);
|
|
16266
16630
|
continue;
|
|
16267
16631
|
}
|
|
16268
|
-
const
|
|
16632
|
+
const isLast = k === links.length - 1;
|
|
16633
|
+
const meta = isLast ? lastSplit.meta : {};
|
|
16634
|
+
const style = links[k].style === "arc" ? "arc" : "straight";
|
|
16269
16635
|
edges.push({
|
|
16270
16636
|
from,
|
|
16271
16637
|
to,
|
|
16272
16638
|
...links[k].label !== void 0 && { label: links[k].label },
|
|
16273
16639
|
directed: links[k].directed,
|
|
16274
|
-
style
|
|
16640
|
+
style,
|
|
16275
16641
|
meta,
|
|
16276
16642
|
lineNumber: line12
|
|
16277
16643
|
});
|
|
@@ -16357,22 +16723,19 @@ var init_parser12 = __esm({
|
|
|
16357
16723
|
LEG_ARROW_RE = /^(-[^>]*?->|->|~[^>]*?~>|~>|--)\s+(.+)$/;
|
|
16358
16724
|
AT_RE = /(^|[\s,])at\s*:/i;
|
|
16359
16725
|
DIRECTIVE_SET = /* @__PURE__ */ new Set([
|
|
16360
|
-
"region",
|
|
16361
|
-
"projection",
|
|
16362
16726
|
"region-metric",
|
|
16363
16727
|
"poi-metric",
|
|
16364
16728
|
"flow-metric",
|
|
16365
|
-
"
|
|
16366
|
-
"region-labels",
|
|
16367
|
-
"poi-labels",
|
|
16368
|
-
"default-country",
|
|
16369
|
-
"default-state",
|
|
16729
|
+
"locale",
|
|
16370
16730
|
"active-tag",
|
|
16731
|
+
"caption",
|
|
16371
16732
|
"no-legend",
|
|
16372
|
-
"no-
|
|
16373
|
-
"relief",
|
|
16374
|
-
"
|
|
16375
|
-
"
|
|
16733
|
+
"no-coastline",
|
|
16734
|
+
"no-relief",
|
|
16735
|
+
"no-context-labels",
|
|
16736
|
+
"no-region-labels",
|
|
16737
|
+
"no-poi-labels",
|
|
16738
|
+
"no-colorize"
|
|
16376
16739
|
]);
|
|
16377
16740
|
}
|
|
16378
16741
|
});
|
|
@@ -16550,6 +16913,21 @@ function parseBoxesAndLines(content) {
|
|
|
16550
16913
|
}
|
|
16551
16914
|
continue;
|
|
16552
16915
|
}
|
|
16916
|
+
if (!contentStarted) {
|
|
16917
|
+
const metricMatch = trimmed.match(/^box-metric\s+(.+)$/i);
|
|
16918
|
+
if (metricMatch) {
|
|
16919
|
+
const { label, colorName } = peelTrailingColorName(
|
|
16920
|
+
metricMatch[1].trim()
|
|
16921
|
+
);
|
|
16922
|
+
result.boxMetric = label;
|
|
16923
|
+
if (colorName !== void 0) result.boxMetricColor = colorName;
|
|
16924
|
+
continue;
|
|
16925
|
+
}
|
|
16926
|
+
if (/^show-values$/i.test(trimmed)) {
|
|
16927
|
+
result.showValues = true;
|
|
16928
|
+
continue;
|
|
16929
|
+
}
|
|
16930
|
+
}
|
|
16553
16931
|
if (!contentStarted) {
|
|
16554
16932
|
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
16555
16933
|
if (optMatch) {
|
|
@@ -16928,6 +17306,19 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16928
17306
|
description = [metadata["description"]];
|
|
16929
17307
|
delete metadata["description"];
|
|
16930
17308
|
}
|
|
17309
|
+
let value;
|
|
17310
|
+
if (metadata["value"] !== void 0) {
|
|
17311
|
+
const raw = metadata["value"];
|
|
17312
|
+
const num = Number(raw);
|
|
17313
|
+
if (Number.isFinite(num)) {
|
|
17314
|
+
value = num;
|
|
17315
|
+
} else {
|
|
17316
|
+
diagnostics.push(
|
|
17317
|
+
makeDgmoError(lineNum, `value must be a number (got "${raw}")`, "error")
|
|
17318
|
+
);
|
|
17319
|
+
}
|
|
17320
|
+
delete metadata["value"];
|
|
17321
|
+
}
|
|
16931
17322
|
if (split.alias) {
|
|
16932
17323
|
nameAliasMap?.set(normalizeName(split.alias), label);
|
|
16933
17324
|
}
|
|
@@ -16936,7 +17327,8 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16936
17327
|
label,
|
|
16937
17328
|
lineNumber: lineNum,
|
|
16938
17329
|
metadata,
|
|
16939
|
-
...description !== void 0 && { description }
|
|
17330
|
+
...description !== void 0 && { description },
|
|
17331
|
+
...value !== void 0 && { value }
|
|
16940
17332
|
};
|
|
16941
17333
|
}
|
|
16942
17334
|
function splitTargetAndMeta(rest, metaAliasMap) {
|
|
@@ -24294,8 +24686,8 @@ function renderKanban(container, parsed, palette, isDark, options) {
|
|
|
24294
24686
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24295
24687
|
for (const meta of tagMeta) {
|
|
24296
24688
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(`${meta.label}: `);
|
|
24297
|
-
const
|
|
24298
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
24689
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
24690
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24299
24691
|
metaY += sCardMetaLineHeight;
|
|
24300
24692
|
}
|
|
24301
24693
|
for (const detail of card.details) {
|
|
@@ -24639,8 +25031,8 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
|
|
|
24639
25031
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24640
25032
|
for (const meta of tagMeta) {
|
|
24641
25033
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", palette.textMuted).text(`${meta.label}: `);
|
|
24642
|
-
const
|
|
24643
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
25034
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
25035
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24644
25036
|
metaY += sCardMetaLineHeight;
|
|
24645
25037
|
}
|
|
24646
25038
|
for (const detail of card.details) {
|
|
@@ -25474,8 +25866,8 @@ function classifyEREntities(tables, relationships) {
|
|
|
25474
25866
|
}
|
|
25475
25867
|
}
|
|
25476
25868
|
const mmParticipants = /* @__PURE__ */ new Set();
|
|
25477
|
-
for (const [id,
|
|
25478
|
-
if (
|
|
25869
|
+
for (const [id, neighbors2] of tableStarNeighbors) {
|
|
25870
|
+
if (neighbors2.size >= 2) mmParticipants.add(id);
|
|
25479
25871
|
}
|
|
25480
25872
|
const indegreeValues = Object.values(indegreeMap);
|
|
25481
25873
|
const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
|
|
@@ -26058,7 +26450,18 @@ function fitLabelToHeader(label, nodeWidth, maxLines) {
|
|
|
26058
26450
|
const truncated = label.length > maxChars ? label.slice(0, maxChars - 1) + "\u2026" : label;
|
|
26059
26451
|
return { lines: [truncated], fontSize: MIN_NODE_FONT_SIZE };
|
|
26060
26452
|
}
|
|
26061
|
-
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, solid) {
|
|
26453
|
+
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, value, solid) {
|
|
26454
|
+
const neutralFill = mix(palette.bg, palette.text, isDark ? 90 : 95);
|
|
26455
|
+
if (value.active) {
|
|
26456
|
+
const fill3 = node.value !== void 0 ? value.fillForValue(node.value) : neutralFill;
|
|
26457
|
+
const stroke3 = value.hue;
|
|
26458
|
+
const text2 = contrastText(
|
|
26459
|
+
fill3,
|
|
26460
|
+
palette.textOnFillLight,
|
|
26461
|
+
palette.textOnFillDark
|
|
26462
|
+
);
|
|
26463
|
+
return { fill: fill3, stroke: stroke3, text: text2 };
|
|
26464
|
+
}
|
|
26062
26465
|
const tagColor = resolveTagColor(
|
|
26063
26466
|
node.metadata,
|
|
26064
26467
|
[...tagGroups],
|
|
@@ -26148,7 +26551,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26148
26551
|
controlsExpanded,
|
|
26149
26552
|
onToggleDescriptions,
|
|
26150
26553
|
onToggleControlsExpand,
|
|
26151
|
-
exportMode = false
|
|
26554
|
+
exportMode = false,
|
|
26555
|
+
controlsHost
|
|
26152
26556
|
} = options ?? {};
|
|
26153
26557
|
d3Selection6.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
26154
26558
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -26166,21 +26570,65 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26166
26570
|
const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
|
|
26167
26571
|
const sTitleFontSize = sctx.text(TITLE_FONT_SIZE);
|
|
26168
26572
|
const sTitleY = sctx.structural(TITLE_Y);
|
|
26169
|
-
const
|
|
26573
|
+
const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
|
|
26574
|
+
const hasRamp = nodeValues.length > 0;
|
|
26575
|
+
const allNonNegative = hasRamp && nodeValues.every((v) => v >= 0);
|
|
26576
|
+
const rampMin = allNonNegative ? 0 : Math.min(...nodeValues);
|
|
26577
|
+
const rampMax = Math.max(...nodeValues);
|
|
26578
|
+
const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
|
|
26579
|
+
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
26580
|
+
const fillForValue = (v) => {
|
|
26581
|
+
const t = rampMax > rampMin ? (v - rampMin) / (rampMax - rampMin) : 1;
|
|
26582
|
+
const pct = RAMP_FLOOR + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR);
|
|
26583
|
+
return mix(rampHue, rampBase, pct);
|
|
26584
|
+
};
|
|
26585
|
+
const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
|
|
26586
|
+
const matchColorGroup = (v) => {
|
|
26587
|
+
const lv = v.trim().toLowerCase();
|
|
26588
|
+
if (lv === "none") return null;
|
|
26589
|
+
const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
|
|
26590
|
+
if (tg) return tg.name;
|
|
26591
|
+
if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
|
|
26592
|
+
return v;
|
|
26593
|
+
};
|
|
26594
|
+
const override = activeTagGroup;
|
|
26595
|
+
let activeGroup;
|
|
26596
|
+
if (override !== void 0) {
|
|
26597
|
+
activeGroup = override === null ? null : matchColorGroup(override);
|
|
26598
|
+
} else if (parsed.options["active-tag"] !== void 0) {
|
|
26599
|
+
activeGroup = matchColorGroup(parsed.options["active-tag"]);
|
|
26600
|
+
} else {
|
|
26601
|
+
activeGroup = VALUE_NAME ?? (parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null);
|
|
26602
|
+
}
|
|
26603
|
+
const activeIsValue = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
26604
|
+
const valueGroup = VALUE_NAME !== null ? {
|
|
26605
|
+
name: VALUE_NAME,
|
|
26606
|
+
entries: [],
|
|
26607
|
+
gradient: {
|
|
26608
|
+
min: rampMin,
|
|
26609
|
+
max: rampMax,
|
|
26610
|
+
hue: rampHue,
|
|
26611
|
+
base: rampBase
|
|
26612
|
+
}
|
|
26613
|
+
} : null;
|
|
26614
|
+
const legendGroups = [
|
|
26615
|
+
...valueGroup ? [valueGroup] : [],
|
|
26616
|
+
...parsed.tagGroups
|
|
26617
|
+
];
|
|
26618
|
+
const reserveHasDescriptions = parsed.nodes.some(
|
|
26619
|
+
(n) => n.description && n.description.length > 0
|
|
26620
|
+
);
|
|
26621
|
+
const willRenderLegend = legendGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app";
|
|
26622
|
+
const sLegendHeight = willRenderLegend ? sctx.structural(
|
|
26170
26623
|
getMaxLegendReservedHeight(
|
|
26171
26624
|
{
|
|
26172
|
-
groups:
|
|
26625
|
+
groups: legendGroups,
|
|
26173
26626
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26174
26627
|
mode: exportMode ? "export" : "preview"
|
|
26175
26628
|
},
|
|
26176
26629
|
width
|
|
26177
26630
|
)
|
|
26178
|
-
);
|
|
26179
|
-
const activeGroup = resolveActiveTagGroup(
|
|
26180
|
-
parsed.tagGroups,
|
|
26181
|
-
parsed.options["active-tag"],
|
|
26182
|
-
activeTagGroup
|
|
26183
|
-
);
|
|
26631
|
+
) : 0;
|
|
26184
26632
|
const hidden = hiddenTagValues ?? parsed.initialHiddenTagValues;
|
|
26185
26633
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
26186
26634
|
for (const node of parsed.nodes) nodeMap.set(node.label, node);
|
|
@@ -26191,7 +26639,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26191
26639
|
const hasAnyDescriptions = parsed.nodes.some(
|
|
26192
26640
|
(n) => n.description && n.description.length > 0
|
|
26193
26641
|
);
|
|
26194
|
-
const needsLegend =
|
|
26642
|
+
const needsLegend = legendGroups.length > 0 || hasAnyDescriptions && onToggleDescriptions;
|
|
26195
26643
|
const legendH = needsLegend ? sLegendHeight + 8 : 0;
|
|
26196
26644
|
const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
|
|
26197
26645
|
let labelZoneExtension = 0;
|
|
@@ -26397,12 +26845,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26397
26845
|
activeGroup,
|
|
26398
26846
|
palette,
|
|
26399
26847
|
isDark,
|
|
26848
|
+
{ active: activeIsValue, hue: rampHue, fillForValue },
|
|
26400
26849
|
parsed.options["solid-fill"] === "on"
|
|
26401
26850
|
);
|
|
26402
26851
|
const nodeG = diagramG.append("g").attr("class", "bl-node").attr("transform", `translate(${ln.x},${ln.y})`).attr("data-line-number", node.lineNumber).attr("data-node-id", node.label).style("cursor", onClickItem ? "pointer" : "default").style("--bl-node-stroke", colors.stroke);
|
|
26403
26852
|
for (const [key, val] of Object.entries(node.metadata)) {
|
|
26404
26853
|
nodeG.attr(`data-tag-${key.toLowerCase()}`, val.toLowerCase());
|
|
26405
26854
|
}
|
|
26855
|
+
if (node.value !== void 0) {
|
|
26856
|
+
nodeG.attr("data-value", node.value);
|
|
26857
|
+
}
|
|
26406
26858
|
if (onClickItem) {
|
|
26407
26859
|
nodeG.on("click", (event) => {
|
|
26408
26860
|
const target = event.target;
|
|
@@ -26486,14 +26938,30 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26486
26938
|
nodeG.append("text").attr("x", 0).attr("y", -totalH / 2 + lineH / 2 + li * lineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", colors.text).text(fitted.lines[li]);
|
|
26487
26939
|
}
|
|
26488
26940
|
}
|
|
26941
|
+
if (parsed.showValues && node.value !== void 0) {
|
|
26942
|
+
const valueText = String(node.value);
|
|
26943
|
+
const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
|
|
26944
|
+
if (descShown) {
|
|
26945
|
+
const padX = 6;
|
|
26946
|
+
const padY = 5;
|
|
26947
|
+
const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
|
|
26948
|
+
const bh = VALUE_FONT_SIZE + 4;
|
|
26949
|
+
const bx = ln.width / 2 - bw - 4;
|
|
26950
|
+
const by = -ln.height / 2 + 4;
|
|
26951
|
+
nodeG.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
|
|
26952
|
+
nodeG.append("text").attr("class", "bl-node-value").attr("x", bx + bw - padX).attr("y", by + padY).attr("text-anchor", "end").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.textMuted).text(valueText);
|
|
26953
|
+
} else {
|
|
26954
|
+
nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", ln.height / 2 - VALUE_FONT_SIZE).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", colors.text).attr("opacity", 0.8).text(valueText);
|
|
26955
|
+
}
|
|
26956
|
+
}
|
|
26489
26957
|
}
|
|
26490
26958
|
const hasDescriptions = parsed.nodes.some(
|
|
26491
26959
|
(n) => n.description && n.description.length > 0
|
|
26492
26960
|
);
|
|
26493
|
-
const hasLegend =
|
|
26961
|
+
const hasLegend = legendGroups.length > 0 || hasDescriptions && controlsHost !== "app";
|
|
26494
26962
|
if (hasLegend) {
|
|
26495
26963
|
let controlsGroup;
|
|
26496
|
-
if (hasDescriptions && onToggleDescriptions) {
|
|
26964
|
+
if (hasDescriptions && (onToggleDescriptions || controlsHost === "app")) {
|
|
26497
26965
|
controlsGroup = {
|
|
26498
26966
|
toggles: [
|
|
26499
26967
|
{
|
|
@@ -26508,10 +26976,17 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26508
26976
|
};
|
|
26509
26977
|
}
|
|
26510
26978
|
const legendConfig = {
|
|
26511
|
-
groups:
|
|
26979
|
+
groups: legendGroups,
|
|
26512
26980
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26513
26981
|
mode: exportMode ? "export" : "preview",
|
|
26514
|
-
|
|
26982
|
+
// Keep inactive sibling tag groups visible as collapsed pills so the user
|
|
26983
|
+
// can click one to flip the active colouring dimension (preview only —
|
|
26984
|
+
// export shows just the active group). Without this, declaring a second
|
|
26985
|
+
// tag group (e.g. Team) leaves it invisible whenever another group is
|
|
26986
|
+
// active. The app's BoxesAndLinesPreview already wires pill clicks.
|
|
26987
|
+
showInactivePills: true,
|
|
26988
|
+
...controlsGroup !== void 0 && { controlsGroup },
|
|
26989
|
+
...controlsHost !== void 0 && { controlsHost }
|
|
26515
26990
|
};
|
|
26516
26991
|
const legendState = {
|
|
26517
26992
|
activeGroup,
|
|
@@ -26556,7 +27031,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
|
|
|
26556
27031
|
}
|
|
26557
27032
|
});
|
|
26558
27033
|
}
|
|
26559
|
-
var DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, CHAR_WIDTH_RATIO2, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, lineGeneratorLR, lineGeneratorTB;
|
|
27034
|
+
var DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, CHAR_WIDTH_RATIO2, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, RAMP_FLOOR, VALUE_FONT_SIZE, lineGeneratorLR, lineGeneratorTB;
|
|
26560
27035
|
var init_renderer6 = __esm({
|
|
26561
27036
|
"src/boxes-and-lines/renderer.ts"() {
|
|
26562
27037
|
"use strict";
|
|
@@ -26565,6 +27040,7 @@ var init_renderer6 = __esm({
|
|
|
26565
27040
|
init_legend_layout();
|
|
26566
27041
|
init_title_constants();
|
|
26567
27042
|
init_color_utils();
|
|
27043
|
+
init_colors();
|
|
26568
27044
|
init_tag_groups();
|
|
26569
27045
|
init_inline_markdown();
|
|
26570
27046
|
init_wrapped_desc();
|
|
@@ -26587,6 +27063,8 @@ var init_renderer6 = __esm({
|
|
|
26587
27063
|
GROUP_RX = 8;
|
|
26588
27064
|
GROUP_LABEL_FONT_SIZE = 14;
|
|
26589
27065
|
GROUP_LABEL_ZONE = 32;
|
|
27066
|
+
RAMP_FLOOR = 15;
|
|
27067
|
+
VALUE_FONT_SIZE = 11;
|
|
26590
27068
|
lineGeneratorLR = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26591
27069
|
lineGeneratorTB = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26592
27070
|
}
|
|
@@ -27758,8 +28236,9 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27758
28236
|
const containerHeight = exportDims?.height ?? (container.getBoundingClientRect().height || 600);
|
|
27759
28237
|
d3Selection7.select(container).selectAll("*").remove();
|
|
27760
28238
|
const svg = d3Selection7.select(container).append("svg").attr("width", containerWidth).attr("height", containerHeight).attr("viewBox", `0 0 ${containerWidth} ${containerHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
|
|
28239
|
+
const appHosted = options?.controlsHost === "app";
|
|
27761
28240
|
const hasControls = !!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
|
|
27762
|
-
const hasLegend = parsed.tagGroups.length > 0 || hasControls;
|
|
28241
|
+
const hasLegend = parsed.tagGroups.length > 0 || hasControls && !appHosted;
|
|
27763
28242
|
const fixedLegend = !isExport && hasLegend;
|
|
27764
28243
|
const legendReserve = fixedLegend ? getMaxLegendReservedHeight(
|
|
27765
28244
|
{
|
|
@@ -27853,7 +28332,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27853
28332
|
}),
|
|
27854
28333
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
27855
28334
|
mode: options?.exportMode ? "export" : "preview",
|
|
27856
|
-
...controlsToggles !== void 0 && { controlsGroup: controlsToggles }
|
|
28335
|
+
...controlsToggles !== void 0 && { controlsGroup: controlsToggles },
|
|
28336
|
+
...options?.controlsHost !== void 0 && {
|
|
28337
|
+
controlsHost: options.controlsHost
|
|
28338
|
+
}
|
|
27857
28339
|
};
|
|
27858
28340
|
const legendState = {
|
|
27859
28341
|
activeGroup: options?.colorByDepth ? null : activeTagGroup !== void 0 ? activeTagGroup : parsed.options["active-tag"] ?? null,
|
|
@@ -28296,8 +28778,8 @@ function computeFieldAlignX(children) {
|
|
|
28296
28778
|
for (const child of children) {
|
|
28297
28779
|
if (child.metadata["_labelField"] === "true" && child.children.length >= 2) {
|
|
28298
28780
|
const labelEl = child.children[0];
|
|
28299
|
-
const
|
|
28300
|
-
maxLabelWidth = Math.max(maxLabelWidth,
|
|
28781
|
+
const labelWidth2 = labelEl.label.length * CHAR_WIDTH5;
|
|
28782
|
+
maxLabelWidth = Math.max(maxLabelWidth, labelWidth2);
|
|
28301
28783
|
labelFieldCount++;
|
|
28302
28784
|
}
|
|
28303
28785
|
}
|
|
@@ -33262,7 +33744,7 @@ function hasRoles(node) {
|
|
|
33262
33744
|
function computeNodeWidth2(node, expanded, options) {
|
|
33263
33745
|
const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
|
|
33264
33746
|
const badgeLen = badgeVal > 0 ? `${badgeVal}x`.length + 2 : 0;
|
|
33265
|
-
const
|
|
33747
|
+
const labelWidth2 = (node.label.length + badgeLen) * CHAR_WIDTH7 + PADDING_X3;
|
|
33266
33748
|
const allKeys = [];
|
|
33267
33749
|
if (node.computedRps > 0) allKeys.push("RPS");
|
|
33268
33750
|
if (expanded) {
|
|
@@ -33306,7 +33788,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33306
33788
|
allKeys.push("overflow");
|
|
33307
33789
|
}
|
|
33308
33790
|
}
|
|
33309
|
-
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2,
|
|
33791
|
+
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
|
|
33310
33792
|
const maxKeyLen = Math.max(...allKeys.map((k) => k.length));
|
|
33311
33793
|
let maxRowWidth = 0;
|
|
33312
33794
|
if (node.computedRps > 0) {
|
|
@@ -33394,7 +33876,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33394
33876
|
truncated.length * META_CHAR_WIDTH3 + PADDING_X3
|
|
33395
33877
|
);
|
|
33396
33878
|
}
|
|
33397
|
-
return Math.max(MIN_NODE_WIDTH2,
|
|
33879
|
+
return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
|
|
33398
33880
|
}
|
|
33399
33881
|
function computeNodeHeight2(node, expanded, options) {
|
|
33400
33882
|
const propCount = countDisplayProps(node, expanded, options);
|
|
@@ -34944,8 +35426,9 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
34944
35426
|
}
|
|
34945
35427
|
return groups;
|
|
34946
35428
|
}
|
|
34947
|
-
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false) {
|
|
35429
|
+
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost) {
|
|
34948
35430
|
if (legendGroups.length === 0 && !playback) return;
|
|
35431
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
34949
35432
|
const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
|
|
34950
35433
|
if (activeGroup) {
|
|
34951
35434
|
legendG.attr("data-legend-active", activeGroup.toLowerCase());
|
|
@@ -34954,14 +35437,29 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
34954
35437
|
name: g.name,
|
|
34955
35438
|
entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
|
|
34956
35439
|
}));
|
|
34957
|
-
if (playback) {
|
|
35440
|
+
if (playback && !appHostedPlayback) {
|
|
34958
35441
|
allGroups.push({ name: "Playback", entries: [] });
|
|
34959
35442
|
}
|
|
34960
35443
|
const legendConfig = {
|
|
34961
35444
|
groups: allGroups,
|
|
34962
35445
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
34963
35446
|
mode: exportMode ? "export" : "preview",
|
|
34964
|
-
showEmptyGroups: true
|
|
35447
|
+
showEmptyGroups: true,
|
|
35448
|
+
...appHostedPlayback && {
|
|
35449
|
+
controlsHost: "app",
|
|
35450
|
+
controlsGroup: {
|
|
35451
|
+
toggles: [
|
|
35452
|
+
{
|
|
35453
|
+
id: "playback",
|
|
35454
|
+
type: "toggle",
|
|
35455
|
+
label: "Playback",
|
|
35456
|
+
active: true,
|
|
35457
|
+
onToggle: () => {
|
|
35458
|
+
}
|
|
35459
|
+
}
|
|
35460
|
+
]
|
|
35461
|
+
}
|
|
35462
|
+
}
|
|
34965
35463
|
};
|
|
34966
35464
|
const legendState = { activeGroup };
|
|
34967
35465
|
renderLegendD3(
|
|
@@ -35012,8 +35510,9 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
35012
35510
|
}
|
|
35013
35511
|
}
|
|
35014
35512
|
}
|
|
35015
|
-
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
35513
|
+
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes, controlsHost) {
|
|
35016
35514
|
d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
35515
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
35017
35516
|
const ctx = ScaleContext.identity();
|
|
35018
35517
|
const sc = buildScaledConstants(ctx);
|
|
35019
35518
|
const legendGroups = computeInfraLegendGroups(
|
|
@@ -35022,7 +35521,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35022
35521
|
palette,
|
|
35023
35522
|
layout.edges
|
|
35024
35523
|
);
|
|
35025
|
-
const hasLegend = legendGroups.length > 0 || !!playback;
|
|
35524
|
+
const hasLegend = legendGroups.length > 0 || !!playback && !appHostedPlayback;
|
|
35026
35525
|
const fixedLegend = !exportMode && hasLegend;
|
|
35027
35526
|
const legendDynamicH = hasLegend ? getMaxLegendReservedHeight(
|
|
35028
35527
|
{
|
|
@@ -35166,7 +35665,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35166
35665
|
isDark,
|
|
35167
35666
|
activeGroup ?? null,
|
|
35168
35667
|
playback ?? void 0,
|
|
35169
|
-
exportMode
|
|
35668
|
+
exportMode,
|
|
35669
|
+
controlsHost
|
|
35170
35670
|
);
|
|
35171
35671
|
legendSvg.selectAll(".infra-legend-group").style("pointer-events", "auto");
|
|
35172
35672
|
} else {
|
|
@@ -35179,7 +35679,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35179
35679
|
isDark,
|
|
35180
35680
|
activeGroup ?? null,
|
|
35181
35681
|
playback ?? void 0,
|
|
35182
|
-
exportMode
|
|
35682
|
+
exportMode,
|
|
35683
|
+
controlsHost
|
|
35183
35684
|
);
|
|
35184
35685
|
}
|
|
35185
35686
|
}
|
|
@@ -42813,6 +43314,9 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
|
|
|
42813
43314
|
onToggle: (active) => options.onToggleListing(active)
|
|
42814
43315
|
}
|
|
42815
43316
|
]
|
|
43317
|
+
},
|
|
43318
|
+
...options.controlsHost !== void 0 && {
|
|
43319
|
+
controlsHost: options.controlsHost
|
|
42816
43320
|
}
|
|
42817
43321
|
};
|
|
42818
43322
|
const legendState = {
|
|
@@ -44635,7 +45139,7 @@ function computeCycleLayout(parsed, options) {
|
|
|
44635
45139
|
const circleNodes = parsed.options["circle-nodes"] === "true";
|
|
44636
45140
|
const nodeDims = parsed.nodes.map((node) => {
|
|
44637
45141
|
const hasDesc = !hideDescriptions && node.description.length > 0;
|
|
44638
|
-
const
|
|
45142
|
+
const labelWidth2 = Math.max(
|
|
44639
45143
|
MIN_NODE_WIDTH4,
|
|
44640
45144
|
node.label.length * LABEL_CHAR_W + NODE_PAD_X * 2
|
|
44641
45145
|
);
|
|
@@ -44644,12 +45148,12 @@ function computeCycleLayout(parsed, options) {
|
|
|
44644
45148
|
}
|
|
44645
45149
|
if (!hasDesc) {
|
|
44646
45150
|
return {
|
|
44647
|
-
width: Math.min(MAX_NODE_WIDTH3,
|
|
45151
|
+
width: Math.min(MAX_NODE_WIDTH3, labelWidth2),
|
|
44648
45152
|
height: PLAIN_NODE_HEIGHT,
|
|
44649
45153
|
wrappedDesc: []
|
|
44650
45154
|
};
|
|
44651
45155
|
}
|
|
44652
|
-
return chooseDescribedRectDims(node.description,
|
|
45156
|
+
return chooseDescribedRectDims(node.description, labelWidth2);
|
|
44653
45157
|
});
|
|
44654
45158
|
if (circleNodes) {
|
|
44655
45159
|
const maxDiam = Math.max(...nodeDims.map((d) => d.width));
|
|
@@ -44845,10 +45349,10 @@ function computeCycleLayout(parsed, options) {
|
|
|
44845
45349
|
scale
|
|
44846
45350
|
};
|
|
44847
45351
|
}
|
|
44848
|
-
function chooseDescribedRectDims(description,
|
|
45352
|
+
function chooseDescribedRectDims(description, labelWidth2) {
|
|
44849
45353
|
const minW = Math.min(
|
|
44850
45354
|
MAX_NODE_WIDTH3,
|
|
44851
|
-
Math.max(MIN_NODE_WIDTH4,
|
|
45355
|
+
Math.max(MIN_NODE_WIDTH4, labelWidth2, DESC_MIN_WIDTH)
|
|
44852
45356
|
);
|
|
44853
45357
|
let best = null;
|
|
44854
45358
|
let bestScore = Infinity;
|
|
@@ -45277,7 +45781,8 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45277
45781
|
const hideDescriptions = (renderOptions?.hideDescriptions ?? false) || parsed.options["no-descriptions"] === "true" || viewState?.hd === true;
|
|
45278
45782
|
const showDescriptions = !hideDescriptions;
|
|
45279
45783
|
const hasDescriptions = parsed.nodes.some((n) => n.description.length > 0) || parsed.edges.some((e) => e.description.length > 0);
|
|
45280
|
-
const
|
|
45784
|
+
const appHostedControls = renderOptions?.controlsHost === "app";
|
|
45785
|
+
const hasLegend = !appHostedControls && hasDescriptions && !!renderOptions?.onToggleDescriptions;
|
|
45281
45786
|
const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
|
|
45282
45787
|
const legendOffset = hasLegend ? sLegendHeight : 0;
|
|
45283
45788
|
const layoutHeight = height - (showTitle ? sTitleAreaHeight : 0) - legendOffset;
|
|
@@ -45314,7 +45819,10 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45314
45819
|
groups: [],
|
|
45315
45820
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
45316
45821
|
mode: renderOptions?.exportMode ? "export" : "preview",
|
|
45317
|
-
controlsGroup
|
|
45822
|
+
controlsGroup,
|
|
45823
|
+
...renderOptions?.controlsHost !== void 0 && {
|
|
45824
|
+
controlsHost: renderOptions.controlsHost
|
|
45825
|
+
}
|
|
45318
45826
|
};
|
|
45319
45827
|
const legendState = {
|
|
45320
45828
|
activeGroup: null,
|
|
@@ -45568,7 +46076,7 @@ var init_renderer15 = __esm({
|
|
|
45568
46076
|
});
|
|
45569
46077
|
|
|
45570
46078
|
// src/map/geo.ts
|
|
45571
|
-
import { feature } from "topojson-client";
|
|
46079
|
+
import { feature, neighbors } from "topojson-client";
|
|
45572
46080
|
import { geoBounds, geoArea } from "d3-geo";
|
|
45573
46081
|
function geomObject(topo) {
|
|
45574
46082
|
const key = Object.keys(topo.objects)[0];
|
|
@@ -45586,6 +46094,107 @@ function featureIndex(topo) {
|
|
|
45586
46094
|
}
|
|
45587
46095
|
return idx;
|
|
45588
46096
|
}
|
|
46097
|
+
function buildAdjacency(topo) {
|
|
46098
|
+
const cached = adjacencyCache.get(topo);
|
|
46099
|
+
if (cached) return cached;
|
|
46100
|
+
const geometries = geomObject(topo).geometries;
|
|
46101
|
+
const nb = neighbors(geometries);
|
|
46102
|
+
const sets = /* @__PURE__ */ new Map();
|
|
46103
|
+
geometries.forEach((g, i) => {
|
|
46104
|
+
if (!g.type || g.type === "null") return;
|
|
46105
|
+
let set = sets.get(g.id);
|
|
46106
|
+
if (!set) {
|
|
46107
|
+
set = /* @__PURE__ */ new Set();
|
|
46108
|
+
sets.set(g.id, set);
|
|
46109
|
+
}
|
|
46110
|
+
for (const j of nb[i] ?? []) {
|
|
46111
|
+
const nid = geometries[j]?.id;
|
|
46112
|
+
if (nid && nid !== g.id) set.add(nid);
|
|
46113
|
+
}
|
|
46114
|
+
});
|
|
46115
|
+
const out = /* @__PURE__ */ new Map();
|
|
46116
|
+
for (const [iso, set] of sets) out.set(iso, [...set].sort());
|
|
46117
|
+
adjacencyCache.set(topo, out);
|
|
46118
|
+
return out;
|
|
46119
|
+
}
|
|
46120
|
+
function decodeFeatures(topo) {
|
|
46121
|
+
return geomObject(topo).geometries.map((g) => {
|
|
46122
|
+
const f = feature(topo, g);
|
|
46123
|
+
return {
|
|
46124
|
+
type: "Feature",
|
|
46125
|
+
id: g.id,
|
|
46126
|
+
properties: g.properties,
|
|
46127
|
+
geometry: f.geometry
|
|
46128
|
+
};
|
|
46129
|
+
});
|
|
46130
|
+
}
|
|
46131
|
+
function pointInRing(lon, lat, ring) {
|
|
46132
|
+
let inside = false;
|
|
46133
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46134
|
+
const xi = ring[i][0];
|
|
46135
|
+
const yi = ring[i][1];
|
|
46136
|
+
const xj = ring[j][0];
|
|
46137
|
+
const yj = ring[j][1];
|
|
46138
|
+
const intersect = yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi;
|
|
46139
|
+
if (intersect) inside = !inside;
|
|
46140
|
+
}
|
|
46141
|
+
return inside;
|
|
46142
|
+
}
|
|
46143
|
+
function pointOnRingEdge(lon, lat, ring) {
|
|
46144
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46145
|
+
const xi = ring[i][0];
|
|
46146
|
+
const yi = ring[i][1];
|
|
46147
|
+
const xj = ring[j][0];
|
|
46148
|
+
const yj = ring[j][1];
|
|
46149
|
+
if (lon < Math.min(xi, xj) - EDGE_EPS || lon > Math.max(xi, xj) + EDGE_EPS)
|
|
46150
|
+
continue;
|
|
46151
|
+
if (lat < Math.min(yi, yj) - EDGE_EPS || lat > Math.max(yi, yj) + EDGE_EPS)
|
|
46152
|
+
continue;
|
|
46153
|
+
const cross = (xj - xi) * (lat - yi) - (yj - yi) * (lon - xi);
|
|
46154
|
+
if (Math.abs(cross) <= EDGE_EPS) return true;
|
|
46155
|
+
}
|
|
46156
|
+
return false;
|
|
46157
|
+
}
|
|
46158
|
+
function pointInGeometry(geometry, lon, lat) {
|
|
46159
|
+
const g = geometry;
|
|
46160
|
+
if (!g) return false;
|
|
46161
|
+
const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
|
|
46162
|
+
for (const rings of polys) {
|
|
46163
|
+
if (!rings.length) continue;
|
|
46164
|
+
if (pointOnRingEdge(lon, lat, rings[0])) return true;
|
|
46165
|
+
if (!pointInRing(lon, lat, rings[0])) continue;
|
|
46166
|
+
let inHole = false;
|
|
46167
|
+
for (let h = 1; h < rings.length; h++) {
|
|
46168
|
+
if (pointInRing(lon, lat, rings[h]) && !pointOnRingEdge(lon, lat, rings[h])) {
|
|
46169
|
+
inHole = true;
|
|
46170
|
+
break;
|
|
46171
|
+
}
|
|
46172
|
+
}
|
|
46173
|
+
if (!inHole) return true;
|
|
46174
|
+
}
|
|
46175
|
+
return false;
|
|
46176
|
+
}
|
|
46177
|
+
function regionAt(lonLat, countries, states) {
|
|
46178
|
+
const lon = lonLat[0];
|
|
46179
|
+
const lat = lonLat[1];
|
|
46180
|
+
let country = null;
|
|
46181
|
+
for (const f of countries) {
|
|
46182
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46183
|
+
country = { iso: f.id, name: f.properties.name };
|
|
46184
|
+
break;
|
|
46185
|
+
}
|
|
46186
|
+
}
|
|
46187
|
+
let state = null;
|
|
46188
|
+
if (country?.iso === "US" && states) {
|
|
46189
|
+
for (const f of states) {
|
|
46190
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46191
|
+
state = { iso: f.id, name: f.properties.name };
|
|
46192
|
+
break;
|
|
46193
|
+
}
|
|
46194
|
+
}
|
|
46195
|
+
}
|
|
46196
|
+
return { country, state };
|
|
46197
|
+
}
|
|
45589
46198
|
function featureBbox(topo, geomId) {
|
|
45590
46199
|
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45591
46200
|
if (!geom) return null;
|
|
@@ -45703,11 +46312,13 @@ function unionLongitudes(lons) {
|
|
|
45703
46312
|
}
|
|
45704
46313
|
return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
|
|
45705
46314
|
}
|
|
45706
|
-
var fold, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
46315
|
+
var fold, adjacencyCache, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
45707
46316
|
var init_geo = __esm({
|
|
45708
46317
|
"src/map/geo.ts"() {
|
|
45709
46318
|
"use strict";
|
|
45710
46319
|
fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
46320
|
+
adjacencyCache = /* @__PURE__ */ new WeakMap();
|
|
46321
|
+
EDGE_EPS = 1e-9;
|
|
45711
46322
|
DETACH_GAP_DEG = 10;
|
|
45712
46323
|
DETACH_AREA_FRAC = 0.25;
|
|
45713
46324
|
}
|
|
@@ -45727,6 +46338,12 @@ function looksUS(lat, lon) {
|
|
|
45727
46338
|
if (lat < 15 || lat > 72) return false;
|
|
45728
46339
|
return lon >= -180 && lon <= -64 || lon >= 172;
|
|
45729
46340
|
}
|
|
46341
|
+
function looksNorthAmericaNeighbor(lat, lon) {
|
|
46342
|
+
return lat >= 14 && lat <= 72 && lon >= -141 && lon <= -52;
|
|
46343
|
+
}
|
|
46344
|
+
function isWholeSphere(bb) {
|
|
46345
|
+
return bb[0][0] <= -179 && bb[1][0] >= 179 && bb[0][1] <= -89 && bb[1][1] >= 89;
|
|
46346
|
+
}
|
|
45730
46347
|
function resolveMap(parsed, data) {
|
|
45731
46348
|
const diagnostics = [...parsed.diagnostics];
|
|
45732
46349
|
const err = (line12, message, code) => {
|
|
@@ -45737,9 +46354,6 @@ function resolveMap(parsed, data) {
|
|
|
45737
46354
|
};
|
|
45738
46355
|
const result = {
|
|
45739
46356
|
title: parsed.title,
|
|
45740
|
-
...parsed.directives.subtitle !== void 0 && {
|
|
45741
|
-
subtitle: parsed.directives.subtitle
|
|
45742
|
-
},
|
|
45743
46357
|
...parsed.directives.caption !== void 0 && {
|
|
45744
46358
|
caption: parsed.directives.caption
|
|
45745
46359
|
},
|
|
@@ -45749,7 +46363,7 @@ function resolveMap(parsed, data) {
|
|
|
45749
46363
|
// renderer's job (step 4) — the resolver only carries `tags` + `tagGroups`
|
|
45750
46364
|
// through; it never resolves a tag value to a palette color (#10).
|
|
45751
46365
|
directives: { ...parsed.directives },
|
|
45752
|
-
basemaps: { world: "
|
|
46366
|
+
basemaps: { world: "detail", subdivisions: [] },
|
|
45753
46367
|
regions: [],
|
|
45754
46368
|
pois: [],
|
|
45755
46369
|
edges: [],
|
|
@@ -45758,7 +46372,8 @@ function resolveMap(parsed, data) {
|
|
|
45758
46372
|
[-180, -85],
|
|
45759
46373
|
[180, 85]
|
|
45760
46374
|
],
|
|
45761
|
-
projection: "
|
|
46375
|
+
projection: "equirectangular",
|
|
46376
|
+
poiFrameContainers: [],
|
|
45762
46377
|
diagnostics,
|
|
45763
46378
|
error: parsed.error
|
|
45764
46379
|
};
|
|
@@ -45768,7 +46383,10 @@ function resolveMap(parsed, data) {
|
|
|
45768
46383
|
...[...countryIndex.values()].map((v) => v.name),
|
|
45769
46384
|
...[...usStateIndex.values()].map((v) => v.name)
|
|
45770
46385
|
];
|
|
45771
|
-
const
|
|
46386
|
+
const localeRaw = parsed.directives.locale?.toUpperCase();
|
|
46387
|
+
const localeCountry = localeRaw ? localeRaw.split("-")[0] : void 0;
|
|
46388
|
+
const localeSubdivision = localeRaw && /^[A-Z]{2}-/.test(localeRaw) ? localeRaw : void 0;
|
|
46389
|
+
const usScoped = localeCountry === "US" || parsed.regions.some((r) => {
|
|
45772
46390
|
const f = fold(r.name);
|
|
45773
46391
|
return usStateIndex.has(f) && !countryIndex.has(f);
|
|
45774
46392
|
}) || parsed.regions.some(
|
|
@@ -45919,7 +46537,7 @@ function resolveMap(parsed, data) {
|
|
|
45919
46537
|
if (!scope)
|
|
45920
46538
|
warn(
|
|
45921
46539
|
line12,
|
|
45922
|
-
`"${name}" is ambiguous \u2014 resolved to the most-populous match.`,
|
|
46540
|
+
`"${name}" is ambiguous \u2014 resolved to the most-populous match. Set a default with \`locale <ISO>\` (e.g. \`locale US\` / \`locale US-GA\`) to steer it.`,
|
|
45923
46541
|
"W_MAP_AMBIGUOUS_NAME"
|
|
45924
46542
|
);
|
|
45925
46543
|
}
|
|
@@ -45932,17 +46550,21 @@ function resolveMap(parsed, data) {
|
|
|
45932
46550
|
return fold(pos.name);
|
|
45933
46551
|
};
|
|
45934
46552
|
const poiCountries = [];
|
|
45935
|
-
let
|
|
46553
|
+
let anyUsPoi = false;
|
|
46554
|
+
let anyNonNaPoi = false;
|
|
45936
46555
|
const noteCountry = (iso) => {
|
|
45937
46556
|
if (iso) {
|
|
45938
46557
|
poiCountries.push(iso);
|
|
45939
|
-
if (iso
|
|
46558
|
+
if (iso === "US") anyUsPoi = true;
|
|
46559
|
+
if (iso !== "US" && iso !== "CA" && iso !== "MX") anyNonNaPoi = true;
|
|
45940
46560
|
}
|
|
45941
46561
|
};
|
|
45942
46562
|
const deferred = [];
|
|
45943
46563
|
for (const p of parsed.pois) {
|
|
45944
46564
|
if (p.pos.kind === "coords") {
|
|
45945
|
-
if (
|
|
46565
|
+
if (looksUS(p.pos.lat, p.pos.lon)) anyUsPoi = true;
|
|
46566
|
+
else if (!looksNorthAmericaNeighbor(p.pos.lat, p.pos.lon))
|
|
46567
|
+
anyNonNaPoi = true;
|
|
45946
46568
|
addResolvedPoi(p.pos.lat, p.pos.lon, p);
|
|
45947
46569
|
continue;
|
|
45948
46570
|
}
|
|
@@ -45960,14 +46582,15 @@ function resolveMap(parsed, data) {
|
|
|
45960
46582
|
deferred.push(p);
|
|
45961
46583
|
}
|
|
45962
46584
|
}
|
|
45963
|
-
const inferredCountry =
|
|
46585
|
+
const inferredCountry = localeCountry ?? mostCommonCountry(regions, poiCountries) ?? void 0;
|
|
46586
|
+
const inferredScope = localeSubdivision ?? inferredCountry;
|
|
45964
46587
|
for (const p of deferred) {
|
|
45965
46588
|
if (p.pos.kind !== "name") continue;
|
|
45966
46589
|
const got = lookupName(
|
|
45967
46590
|
p.pos.name,
|
|
45968
46591
|
p.pos.scope,
|
|
45969
46592
|
p.lineNumber,
|
|
45970
|
-
|
|
46593
|
+
inferredScope,
|
|
45971
46594
|
true
|
|
45972
46595
|
);
|
|
45973
46596
|
if (got.kind === "ok") {
|
|
@@ -46037,7 +46660,8 @@ function resolveMap(parsed, data) {
|
|
|
46037
46660
|
const meta = sizeValue !== void 0 ? { value: sizeValue } : {};
|
|
46038
46661
|
if (pos.kind === "coords") {
|
|
46039
46662
|
const id = alias ? fold(alias) : `@${pos.lat},${pos.lon}`;
|
|
46040
|
-
if (
|
|
46663
|
+
if (looksUS(pos.lat, pos.lon)) anyUsPoi = true;
|
|
46664
|
+
else if (!looksNorthAmericaNeighbor(pos.lat, pos.lon)) anyNonNaPoi = true;
|
|
46041
46665
|
if (!registry.has(id)) {
|
|
46042
46666
|
registerPoi(
|
|
46043
46667
|
id,
|
|
@@ -46060,7 +46684,7 @@ function resolveMap(parsed, data) {
|
|
|
46060
46684
|
if (registry.has(f)) return f;
|
|
46061
46685
|
const aliased = declaredByName.get(f);
|
|
46062
46686
|
if (aliased) return aliased;
|
|
46063
|
-
const got = lookupName(pos.name, pos.scope, line12,
|
|
46687
|
+
const got = lookupName(pos.name, pos.scope, line12, inferredScope, true);
|
|
46064
46688
|
if (got.kind !== "ok") return null;
|
|
46065
46689
|
noteCountry(got.iso);
|
|
46066
46690
|
registerPoi(
|
|
@@ -46117,9 +46741,12 @@ function resolveMap(parsed, data) {
|
|
|
46117
46741
|
}
|
|
46118
46742
|
routes.push({ stopIds, legs, lineNumber: rt.lineNumber });
|
|
46119
46743
|
}
|
|
46744
|
+
const hasUsContent = usSubdivisionReferenced || anyUsPoi || localeCountry === "US";
|
|
46745
|
+
const usOriented = !anyNonNaPoi && !regions.some(
|
|
46746
|
+
(r) => r.layer === "country" && !["US", "CA", "MX"].includes(r.iso)
|
|
46747
|
+
) && hasUsContent;
|
|
46120
46748
|
const subdivisions = [];
|
|
46121
|
-
if (usSubdivisionReferenced ||
|
|
46122
|
-
subdivisions.push("us-states");
|
|
46749
|
+
if (usSubdivisionReferenced || usOriented) subdivisions.push("us-states");
|
|
46123
46750
|
const regionBoxes = [];
|
|
46124
46751
|
for (const ref of referencedRegionIds) {
|
|
46125
46752
|
const bb = featureBbox(data.usStates, ref.id);
|
|
@@ -46137,17 +46764,51 @@ function resolveMap(parsed, data) {
|
|
|
46137
46764
|
[-180, -85],
|
|
46138
46765
|
[180, 85]
|
|
46139
46766
|
];
|
|
46140
|
-
|
|
46767
|
+
const basePad = regions.length > 0 ? REGION_PAD_FRACTION : PAD_FRACTION;
|
|
46768
|
+
let extent2 = unioned ? pad(unioned, basePad) : DEFAULT_EXTENT;
|
|
46769
|
+
const isPoiOnly = pois.length > 0 && regions.length === 0;
|
|
46770
|
+
const containerRegionIds = [];
|
|
46771
|
+
if (isPoiOnly) {
|
|
46772
|
+
const countries = decodeFeatures(data.worldDetail);
|
|
46773
|
+
const states = decodeFeatures(data.usStates);
|
|
46774
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46775
|
+
const containerBoxes = [];
|
|
46776
|
+
for (const p of pois) {
|
|
46777
|
+
const { country, state } = regionAt([p.lon, p.lat], countries, states);
|
|
46778
|
+
const id = state?.iso ?? country?.iso;
|
|
46779
|
+
if (!id || seen.has(id)) continue;
|
|
46780
|
+
seen.add(id);
|
|
46781
|
+
containerRegionIds.push(id);
|
|
46782
|
+
const bb = state ? featureBbox(data.usStates, id) : featureBboxPrimary(data.worldCoarse, id);
|
|
46783
|
+
if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
|
|
46784
|
+
}
|
|
46785
|
+
const containerUnion = unionExtent(containerBoxes, points);
|
|
46786
|
+
if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
|
|
46787
|
+
}
|
|
46788
|
+
if (isPoiOnly) {
|
|
46789
|
+
const cx = (extent2[0][0] + extent2[1][0]) / 2;
|
|
46790
|
+
const cy = (extent2[0][1] + extent2[1][1]) / 2;
|
|
46791
|
+
const lon = extent2[1][0] - extent2[0][0];
|
|
46792
|
+
const lat = extent2[1][1] - extent2[0][1];
|
|
46793
|
+
const longer = Math.max(lon, lat);
|
|
46794
|
+
if (longer > 0 && longer < POI_ZOOM_FLOOR_DEG) {
|
|
46795
|
+
const k = POI_ZOOM_FLOOR_DEG / longer;
|
|
46796
|
+
const halfLon = lon * k / 2;
|
|
46797
|
+
const halfLat = lat * k / 2;
|
|
46798
|
+
extent2 = [
|
|
46799
|
+
[cx - halfLon, cy - halfLat],
|
|
46800
|
+
[cx + halfLon, cy + halfLat]
|
|
46801
|
+
];
|
|
46802
|
+
}
|
|
46803
|
+
}
|
|
46141
46804
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
46142
46805
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
46143
46806
|
const span = Math.max(lonSpan, latSpan);
|
|
46144
46807
|
const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
|
|
46145
|
-
const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
46146
46808
|
let projection;
|
|
46147
|
-
|
|
46148
|
-
|
|
46149
|
-
|
|
46150
|
-
} else if (usDominant) {
|
|
46809
|
+
if (isPoiOnly && usOriented && lonSpan < US_NATIONAL_LON_SPAN) {
|
|
46810
|
+
projection = "mercator";
|
|
46811
|
+
} else if (usOriented) {
|
|
46151
46812
|
projection = "albers-usa";
|
|
46152
46813
|
} else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
|
|
46153
46814
|
projection = "equirectangular";
|
|
@@ -46165,11 +46826,20 @@ function resolveMap(parsed, data) {
|
|
|
46165
46826
|
result.edges = edges;
|
|
46166
46827
|
result.routes = routes;
|
|
46167
46828
|
result.basemaps = {
|
|
46168
|
-
|
|
46829
|
+
// Tier is intentionally pinned to detail (50m) at ALL scales. Diagrammo maps
|
|
46830
|
+
// are presentational (palette tints, relief hachures, POI hubs), not
|
|
46831
|
+
// survey-grade — recognizability > generalization: 110m coarse drops the
|
|
46832
|
+
// Italian boot to a stump at world scale. `WORLD_SPAN` lives on only for the
|
|
46833
|
+
// projection decision (the `usOriented`/`span > WORLD_SPAN` chain above); it
|
|
46834
|
+
// no longer gates basemap resolution.
|
|
46835
|
+
// `worldCoarse` is still loaded — it's the authoritative name/bbox index
|
|
46836
|
+
// (featureIndex, featureBboxPrimary), not dead code.
|
|
46837
|
+
world: "detail",
|
|
46169
46838
|
subdivisions
|
|
46170
46839
|
};
|
|
46171
46840
|
result.extent = extent2;
|
|
46172
46841
|
result.projection = projection;
|
|
46842
|
+
result.poiFrameContainers = containerRegionIds;
|
|
46173
46843
|
result.error = parsed.error ?? firstError(diagnostics);
|
|
46174
46844
|
return result;
|
|
46175
46845
|
}
|
|
@@ -46206,7 +46876,7 @@ function firstError(diags) {
|
|
|
46206
46876
|
const e = diags.find((d) => d.severity === "error");
|
|
46207
46877
|
return e ? formatDgmoError(e) : null;
|
|
46208
46878
|
}
|
|
46209
|
-
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
|
|
46879
|
+
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, US_NATIONAL_LON_SPAN, REGION_ALIASES, US_STATE_POSTAL;
|
|
46210
46880
|
var init_resolver2 = __esm({
|
|
46211
46881
|
"src/map/resolver.ts"() {
|
|
46212
46882
|
"use strict";
|
|
@@ -46215,8 +46885,11 @@ var init_resolver2 = __esm({
|
|
|
46215
46885
|
WORLD_SPAN = 90;
|
|
46216
46886
|
MERCATOR_MAX_LAT = 80;
|
|
46217
46887
|
PAD_FRACTION = 0.05;
|
|
46888
|
+
REGION_PAD_FRACTION = 0.12;
|
|
46218
46889
|
WORLD_LAT_SOUTH = -58;
|
|
46219
46890
|
WORLD_LAT_NORTH = 78;
|
|
46891
|
+
POI_ZOOM_FLOOR_DEG = 7;
|
|
46892
|
+
US_NATIONAL_LON_SPAN = 48;
|
|
46220
46893
|
REGION_ALIASES = {
|
|
46221
46894
|
// Common everyday names → the Natural-Earth display name actually shipped.
|
|
46222
46895
|
"united states": "united states of america",
|
|
@@ -46294,10 +46967,277 @@ var init_resolver2 = __esm({
|
|
|
46294
46967
|
}
|
|
46295
46968
|
});
|
|
46296
46969
|
|
|
46970
|
+
// src/map/colorize.ts
|
|
46971
|
+
function assignColors(isos, adjacency) {
|
|
46972
|
+
const sorted = [...isos].sort();
|
|
46973
|
+
const byIso = /* @__PURE__ */ new Map();
|
|
46974
|
+
let maxIndex = -1;
|
|
46975
|
+
for (const iso of sorted) {
|
|
46976
|
+
const taken = /* @__PURE__ */ new Set();
|
|
46977
|
+
for (const n of adjacency.get(iso) ?? []) {
|
|
46978
|
+
const c = byIso.get(n);
|
|
46979
|
+
if (c !== void 0) taken.add(c);
|
|
46980
|
+
}
|
|
46981
|
+
let h = 0;
|
|
46982
|
+
while (taken.has(h)) h++;
|
|
46983
|
+
byIso.set(iso, h);
|
|
46984
|
+
if (h > maxIndex) maxIndex = h;
|
|
46985
|
+
}
|
|
46986
|
+
return { byIso, huesNeeded: maxIndex + 1 };
|
|
46987
|
+
}
|
|
46988
|
+
var init_colorize = __esm({
|
|
46989
|
+
"src/map/colorize.ts"() {
|
|
46990
|
+
"use strict";
|
|
46991
|
+
}
|
|
46992
|
+
});
|
|
46993
|
+
|
|
46994
|
+
// src/map/context-labels.ts
|
|
46995
|
+
function tierBand(maxSpanDeg) {
|
|
46996
|
+
if (maxSpanDeg >= 90) return "world";
|
|
46997
|
+
if (maxSpanDeg >= 20) return "continental";
|
|
46998
|
+
if (maxSpanDeg >= 5) return "regional";
|
|
46999
|
+
return "local";
|
|
47000
|
+
}
|
|
47001
|
+
function labelBudget(width, height, band) {
|
|
47002
|
+
const bandCap = {
|
|
47003
|
+
world: 6,
|
|
47004
|
+
continental: 5,
|
|
47005
|
+
regional: 4,
|
|
47006
|
+
local: 3
|
|
47007
|
+
};
|
|
47008
|
+
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
47009
|
+
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
47010
|
+
}
|
|
47011
|
+
function waterEligible(tier, kind, band) {
|
|
47012
|
+
switch (band) {
|
|
47013
|
+
case "world":
|
|
47014
|
+
return tier <= 1 && (kind === "ocean" || kind === "sea");
|
|
47015
|
+
case "continental":
|
|
47016
|
+
return tier <= 2;
|
|
47017
|
+
case "regional":
|
|
47018
|
+
return tier <= 3;
|
|
47019
|
+
case "local":
|
|
47020
|
+
return tier <= 4;
|
|
47021
|
+
}
|
|
47022
|
+
}
|
|
47023
|
+
function insideViewport(p, width, height) {
|
|
47024
|
+
return !!p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && p[0] >= 0 && p[0] <= width && p[1] >= 0 && p[1] <= height;
|
|
47025
|
+
}
|
|
47026
|
+
function labelWidth(text, letterSpacing) {
|
|
47027
|
+
const spacing = letterSpacing > 0 ? Math.max(0, text.length - 1) * letterSpacing : 0;
|
|
47028
|
+
return measureLegendText(text, FONT) + spacing + 2 * PADX;
|
|
47029
|
+
}
|
|
47030
|
+
function wrapLabel2(text, letterSpacing) {
|
|
47031
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
47032
|
+
if (words.length <= 1) return [text];
|
|
47033
|
+
const maxLines = words.length >= 4 ? 3 : 2;
|
|
47034
|
+
const n = words.length;
|
|
47035
|
+
let best = null;
|
|
47036
|
+
for (let mask = 0; mask < 1 << n - 1; mask++) {
|
|
47037
|
+
const lines = [];
|
|
47038
|
+
let cur = [words[0]];
|
|
47039
|
+
for (let i = 1; i < n; i++) {
|
|
47040
|
+
if (mask & 1 << i - 1) {
|
|
47041
|
+
lines.push(cur.join(" "));
|
|
47042
|
+
cur = [words[i]];
|
|
47043
|
+
} else cur.push(words[i]);
|
|
47044
|
+
}
|
|
47045
|
+
lines.push(cur.join(" "));
|
|
47046
|
+
if (lines.length > maxLines) continue;
|
|
47047
|
+
const cost = Math.round(
|
|
47048
|
+
Math.max(...lines.map((l) => labelWidth(l, letterSpacing)))
|
|
47049
|
+
);
|
|
47050
|
+
const head = labelWidth(lines[0], letterSpacing);
|
|
47051
|
+
if (!best || cost < best.cost || cost === best.cost && lines.length < best.lines.length || cost === best.cost && lines.length === best.lines.length && head > best.head)
|
|
47052
|
+
best = { lines, cost, head };
|
|
47053
|
+
}
|
|
47054
|
+
return best?.lines ?? [text];
|
|
47055
|
+
}
|
|
47056
|
+
function rectAround(cx, cy, lines, letterSpacing) {
|
|
47057
|
+
const w = Math.max(...lines.map((l) => labelWidth(l, letterSpacing)));
|
|
47058
|
+
const h = (lines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY;
|
|
47059
|
+
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
47060
|
+
}
|
|
47061
|
+
function rectFits(r, width, height) {
|
|
47062
|
+
return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
|
|
47063
|
+
}
|
|
47064
|
+
function overlapsPadded(a, b, pad2) {
|
|
47065
|
+
return a.x - pad2 < b.x + b.w && a.x + a.w + pad2 > b.x && a.y - pad2 < b.y + b.h && a.y + a.h + pad2 > b.y;
|
|
47066
|
+
}
|
|
47067
|
+
function placeContextLabels(args) {
|
|
47068
|
+
const {
|
|
47069
|
+
projection,
|
|
47070
|
+
dLonSpan,
|
|
47071
|
+
dLatSpan,
|
|
47072
|
+
width,
|
|
47073
|
+
height,
|
|
47074
|
+
waterBodies,
|
|
47075
|
+
countries,
|
|
47076
|
+
palette,
|
|
47077
|
+
project,
|
|
47078
|
+
collides,
|
|
47079
|
+
overLand
|
|
47080
|
+
} = args;
|
|
47081
|
+
void projection;
|
|
47082
|
+
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
47083
|
+
const budget = labelBudget(width, height, band);
|
|
47084
|
+
if (budget <= 0) return [];
|
|
47085
|
+
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
47086
|
+
const countryColor = palette.textMuted;
|
|
47087
|
+
const haloColor = palette.bg;
|
|
47088
|
+
const candidates = [];
|
|
47089
|
+
const center = [width / 2, height / 2];
|
|
47090
|
+
for (const e of waterBodies?.entries ?? []) {
|
|
47091
|
+
const [lat, lon, name, tier, kind, alt] = e;
|
|
47092
|
+
if (!waterEligible(tier, kind, band)) continue;
|
|
47093
|
+
const wlines = wrapLabel2(name, WATER_LETTER_SPACING);
|
|
47094
|
+
const anchorsLngLat = [[lon, lat]];
|
|
47095
|
+
for (const a of alt ?? []) anchorsLngLat.push([a[1], a[0]]);
|
|
47096
|
+
let best = null;
|
|
47097
|
+
let bestD = Infinity;
|
|
47098
|
+
let nearestProj = null;
|
|
47099
|
+
let nearestProjD = Infinity;
|
|
47100
|
+
for (const [aLon, aLat] of anchorsLngLat) {
|
|
47101
|
+
const p = project(aLon, aLat);
|
|
47102
|
+
if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
|
|
47103
|
+
const d = (p[0] - center[0]) ** 2 + (p[1] - center[1]) ** 2;
|
|
47104
|
+
if (d < nearestProjD) {
|
|
47105
|
+
nearestProjD = d;
|
|
47106
|
+
nearestProj = p;
|
|
47107
|
+
}
|
|
47108
|
+
if (!insideViewport(p, width, height)) continue;
|
|
47109
|
+
if (d < bestD) {
|
|
47110
|
+
bestD = d;
|
|
47111
|
+
best = p;
|
|
47112
|
+
}
|
|
47113
|
+
}
|
|
47114
|
+
if (!best && tier === 0 && nearestProj) {
|
|
47115
|
+
const overX = Math.max(0, -nearestProj[0], nearestProj[0] - width);
|
|
47116
|
+
const overY = Math.max(0, -nearestProj[1], nearestProj[1] - height);
|
|
47117
|
+
if (overX <= width * EDGE_CLAMP_OVERSHOOT && overY <= height * EDGE_CLAMP_OVERSHOOT) {
|
|
47118
|
+
const halfW = Math.max(...wlines.map((l) => labelWidth(l, WATER_LETTER_SPACING))) / 2;
|
|
47119
|
+
const halfH = ((wlines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY) / 2;
|
|
47120
|
+
const m = EDGE_CLAMP_MARGIN;
|
|
47121
|
+
best = [
|
|
47122
|
+
Math.min(Math.max(nearestProj[0], halfW + m), width - halfW - m),
|
|
47123
|
+
Math.min(Math.max(nearestProj[1], halfH + m), height - halfH - m)
|
|
47124
|
+
];
|
|
47125
|
+
}
|
|
47126
|
+
}
|
|
47127
|
+
if (!best) continue;
|
|
47128
|
+
candidates.push({
|
|
47129
|
+
text: name,
|
|
47130
|
+
lines: wlines,
|
|
47131
|
+
cx: best[0],
|
|
47132
|
+
cy: best[1],
|
|
47133
|
+
italic: true,
|
|
47134
|
+
letterSpacing: WATER_LETTER_SPACING,
|
|
47135
|
+
color: waterColor,
|
|
47136
|
+
// Water before any country (×1000), then by tier, then kind, then name.
|
|
47137
|
+
sort: tier * 10 + KIND_ORDER[kind]
|
|
47138
|
+
});
|
|
47139
|
+
}
|
|
47140
|
+
const ranked = countries.map((c) => {
|
|
47141
|
+
const [x0, y0, x1, y1] = c.bbox;
|
|
47142
|
+
const w = x1 - x0;
|
|
47143
|
+
const h = y1 - y0;
|
|
47144
|
+
return { c, w, h, area: w * h };
|
|
47145
|
+
}).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
|
|
47146
|
+
let ci = 0;
|
|
47147
|
+
for (const r of ranked) {
|
|
47148
|
+
const { c, w, h } = r;
|
|
47149
|
+
if (w > width * 0.66 || h > height * 0.66) continue;
|
|
47150
|
+
if (!insideViewport(c.anchor, width, height)) continue;
|
|
47151
|
+
const text = c.name;
|
|
47152
|
+
const tw = labelWidth(text, 0);
|
|
47153
|
+
if (tw > w || FONT + 2 * PADY > h) continue;
|
|
47154
|
+
candidates.push({
|
|
47155
|
+
text,
|
|
47156
|
+
lines: [text],
|
|
47157
|
+
cx: c.anchor[0],
|
|
47158
|
+
cy: c.anchor[1],
|
|
47159
|
+
italic: false,
|
|
47160
|
+
letterSpacing: 0,
|
|
47161
|
+
color: countryColor,
|
|
47162
|
+
// Always after every water body (+1e6); larger area = earlier.
|
|
47163
|
+
sort: 1e6 + ci++
|
|
47164
|
+
});
|
|
47165
|
+
}
|
|
47166
|
+
candidates.sort((a, b) => a.sort - b.sort);
|
|
47167
|
+
const placed = [];
|
|
47168
|
+
const placedRects = [];
|
|
47169
|
+
for (const cand of candidates) {
|
|
47170
|
+
if (placed.length >= budget) break;
|
|
47171
|
+
const rect = rectAround(cand.cx, cand.cy, cand.lines, cand.letterSpacing);
|
|
47172
|
+
if (!rectFits(rect, width, height)) continue;
|
|
47173
|
+
if (cand.italic && overLand) {
|
|
47174
|
+
const inset = 2;
|
|
47175
|
+
const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
|
|
47176
|
+
const touchesLand = cand.lines.some((line12, li) => {
|
|
47177
|
+
const lw = labelWidth(line12, cand.letterSpacing);
|
|
47178
|
+
const x0 = cand.cx - lw / 2 + inset;
|
|
47179
|
+
const x1 = cand.cx + lw / 2 - inset;
|
|
47180
|
+
const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
|
|
47181
|
+
const base = top + li * LINE_HEIGHT;
|
|
47182
|
+
return [base, base - FONT * 0.4, base - FONT * 0.8].some(
|
|
47183
|
+
(y) => xs.some((x) => overLand(x, y))
|
|
47184
|
+
);
|
|
47185
|
+
});
|
|
47186
|
+
if (touchesLand) continue;
|
|
47187
|
+
}
|
|
47188
|
+
if (collides(rect)) continue;
|
|
47189
|
+
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
47190
|
+
placedRects.push(rect);
|
|
47191
|
+
placed.push({
|
|
47192
|
+
x: cand.cx,
|
|
47193
|
+
y: cand.cy,
|
|
47194
|
+
text: cand.text,
|
|
47195
|
+
anchor: "middle",
|
|
47196
|
+
color: cand.color,
|
|
47197
|
+
// No halo: the bg-coloured outline reads as a ghost box behind the text
|
|
47198
|
+
// over the tinted water/land. Context labels are muted enough to sit
|
|
47199
|
+
// cleanly on the basemap without one.
|
|
47200
|
+
halo: false,
|
|
47201
|
+
haloColor,
|
|
47202
|
+
italic: cand.italic,
|
|
47203
|
+
letterSpacing: cand.letterSpacing,
|
|
47204
|
+
...cand.lines.length > 1 ? { lines: cand.lines } : {},
|
|
47205
|
+
lineNumber: 0
|
|
47206
|
+
});
|
|
47207
|
+
}
|
|
47208
|
+
return placed;
|
|
47209
|
+
}
|
|
47210
|
+
var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, KIND_ORDER;
|
|
47211
|
+
var init_context_labels = __esm({
|
|
47212
|
+
"src/map/context-labels.ts"() {
|
|
47213
|
+
"use strict";
|
|
47214
|
+
init_color_utils();
|
|
47215
|
+
init_legend_constants();
|
|
47216
|
+
FONT = 11;
|
|
47217
|
+
LINE_HEIGHT = FONT + 2;
|
|
47218
|
+
PADX = 4;
|
|
47219
|
+
PADY = 3;
|
|
47220
|
+
WATER_LETTER_SPACING = 1.5;
|
|
47221
|
+
CONTEXT_PAD = 4;
|
|
47222
|
+
EDGE_CLAMP_MARGIN = 8;
|
|
47223
|
+
EDGE_CLAMP_OVERSHOOT = 0.35;
|
|
47224
|
+
KIND_ORDER = {
|
|
47225
|
+
ocean: 0,
|
|
47226
|
+
sea: 1,
|
|
47227
|
+
gulf: 2,
|
|
47228
|
+
bay: 3,
|
|
47229
|
+
strait: 4,
|
|
47230
|
+
channel: 5,
|
|
47231
|
+
sound: 6
|
|
47232
|
+
};
|
|
47233
|
+
}
|
|
47234
|
+
});
|
|
47235
|
+
|
|
46297
47236
|
// src/map/layout.ts
|
|
46298
47237
|
import {
|
|
46299
47238
|
geoPath,
|
|
46300
47239
|
geoNaturalEarth1,
|
|
47240
|
+
geoEqualEarth,
|
|
46301
47241
|
geoEquirectangular,
|
|
46302
47242
|
geoConicEqualArea,
|
|
46303
47243
|
geoMercator,
|
|
@@ -46309,12 +47249,34 @@ function geomObject2(topo) {
|
|
|
46309
47249
|
const key = Object.keys(topo.objects)[0];
|
|
46310
47250
|
return topo.objects[key];
|
|
46311
47251
|
}
|
|
47252
|
+
function mergeFeatures(a, b) {
|
|
47253
|
+
const polysOf = (f) => {
|
|
47254
|
+
const g = f.geometry;
|
|
47255
|
+
if (!g) return null;
|
|
47256
|
+
if (g.type === "Polygon") return [g.coordinates];
|
|
47257
|
+
if (g.type === "MultiPolygon") return g.coordinates;
|
|
47258
|
+
return null;
|
|
47259
|
+
};
|
|
47260
|
+
const pa = polysOf(a);
|
|
47261
|
+
const pb = polysOf(b);
|
|
47262
|
+
if (!pa || !pb) return a;
|
|
47263
|
+
return {
|
|
47264
|
+
...a,
|
|
47265
|
+
geometry: { type: "MultiPolygon", coordinates: [...pa, ...pb] }
|
|
47266
|
+
};
|
|
47267
|
+
}
|
|
46312
47268
|
function decodeLayer(topo) {
|
|
47269
|
+
const cached = decodeCache.get(topo);
|
|
47270
|
+
if (cached) return cached;
|
|
46313
47271
|
const out = /* @__PURE__ */ new Map();
|
|
46314
47272
|
for (const g of geomObject2(topo).geometries) {
|
|
46315
47273
|
const f = feature2(topo, g);
|
|
46316
|
-
|
|
47274
|
+
if (!f.geometry) continue;
|
|
47275
|
+
const tagged = { ...f, id: g.id };
|
|
47276
|
+
const existing = out.get(g.id);
|
|
47277
|
+
out.set(g.id, existing ? mergeFeatures(existing, tagged) : tagged);
|
|
46317
47278
|
}
|
|
47279
|
+
decodeCache.set(topo, out);
|
|
46318
47280
|
return out;
|
|
46319
47281
|
}
|
|
46320
47282
|
function projectionFor(family) {
|
|
@@ -46323,9 +47285,12 @@ function projectionFor(family) {
|
|
|
46323
47285
|
return usConusProjection();
|
|
46324
47286
|
case "mercator":
|
|
46325
47287
|
return geoMercator();
|
|
47288
|
+
case "equal-earth":
|
|
47289
|
+
return geoEqualEarth();
|
|
47290
|
+
case "equirectangular":
|
|
47291
|
+
return geoEquirectangular();
|
|
46326
47292
|
case "natural-earth":
|
|
46327
47293
|
return geoNaturalEarth1();
|
|
46328
|
-
case "equirectangular":
|
|
46329
47294
|
default:
|
|
46330
47295
|
return geoEquirectangular();
|
|
46331
47296
|
}
|
|
@@ -46344,13 +47309,11 @@ function mapNeutralLandColor(palette, isDark, _dataActive = false) {
|
|
|
46344
47309
|
isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT
|
|
46345
47310
|
);
|
|
46346
47311
|
}
|
|
46347
|
-
function
|
|
46348
|
-
const { palette, isDark } = opts;
|
|
46349
|
-
const { width, height } = size;
|
|
47312
|
+
function buildMapProjection(resolved, data) {
|
|
46350
47313
|
const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
|
|
46351
|
-
const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
|
|
47314
|
+
const usCrisp = (resolved.projection === "albers-usa" || resolved.projection === "mercator") && wantsUsStates && !!data.naLand;
|
|
46352
47315
|
const worldTopo = usCrisp ? data.worldDetail : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
|
|
46353
|
-
const worldLayer = decodeLayer(worldTopo);
|
|
47316
|
+
const worldLayer = new Map(decodeLayer(worldTopo));
|
|
46354
47317
|
if (usCrisp && data.naLand) {
|
|
46355
47318
|
const [nbW, nbS, nbE, nbN] = [-140, 10, -52, 66];
|
|
46356
47319
|
const crisp = decodeLayer(data.naLand);
|
|
@@ -46359,16 +47322,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46359
47322
|
if (!base) continue;
|
|
46360
47323
|
const [[bw, bs], [be, bn]] = geoBounds2(base);
|
|
46361
47324
|
if (bw >= nbW && be <= nbE && bs >= nbS && bn <= nbN)
|
|
46362
|
-
worldLayer.set(iso, cf);
|
|
47325
|
+
worldLayer.set(iso, { ...cf, properties: base.properties });
|
|
46363
47326
|
}
|
|
46364
47327
|
}
|
|
46365
47328
|
const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
|
|
47329
|
+
const extentOutline = () => {
|
|
47330
|
+
const [[w, s], [e, n]] = resolved.extent;
|
|
47331
|
+
const N = 16;
|
|
47332
|
+
const coords = [];
|
|
47333
|
+
for (let i = 0; i <= N; i++) {
|
|
47334
|
+
const t = i / N;
|
|
47335
|
+
const lon = w + (e - w) * t;
|
|
47336
|
+
const lat = s + (n - s) * t;
|
|
47337
|
+
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
47338
|
+
}
|
|
47339
|
+
return {
|
|
47340
|
+
type: "Feature",
|
|
47341
|
+
properties: {},
|
|
47342
|
+
geometry: { type: "MultiPoint", coordinates: coords }
|
|
47343
|
+
};
|
|
47344
|
+
};
|
|
47345
|
+
let fitFeatures;
|
|
47346
|
+
if (resolved.projection === "albers-usa" && usLayer) {
|
|
47347
|
+
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
47348
|
+
const neighborPoints = resolved.pois.filter((p) => !inAlaska(p.lon, p.lat) && !inHawaii(p.lon, p.lat)).map((p) => [p.lon, p.lat]);
|
|
47349
|
+
if (neighborPoints.length > 0) {
|
|
47350
|
+
fitFeatures.push({
|
|
47351
|
+
type: "Feature",
|
|
47352
|
+
properties: {},
|
|
47353
|
+
geometry: { type: "MultiPoint", coordinates: neighborPoints }
|
|
47354
|
+
});
|
|
47355
|
+
}
|
|
47356
|
+
for (const r of resolved.regions) {
|
|
47357
|
+
if (r.layer === "country" && (r.iso === "CA" || r.iso === "MX")) {
|
|
47358
|
+
const cf = worldLayer.get(r.iso);
|
|
47359
|
+
if (cf) fitFeatures.push(cf);
|
|
47360
|
+
}
|
|
47361
|
+
}
|
|
47362
|
+
} else {
|
|
47363
|
+
fitFeatures = [extentOutline()];
|
|
47364
|
+
}
|
|
47365
|
+
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
47366
|
+
const projection = projectionFor(resolved.projection);
|
|
47367
|
+
if (resolved.projection !== "albers-usa") {
|
|
47368
|
+
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
47369
|
+
if (centerLon > 180) centerLon -= 360;
|
|
47370
|
+
projection.rotate([-centerLon, 0]);
|
|
47371
|
+
}
|
|
47372
|
+
const fitGB = geoBounds2(fitTarget);
|
|
47373
|
+
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
47374
|
+
return {
|
|
47375
|
+
projection,
|
|
47376
|
+
fitTarget,
|
|
47377
|
+
fitIsGlobal,
|
|
47378
|
+
worldLayer,
|
|
47379
|
+
usLayer,
|
|
47380
|
+
usCrisp,
|
|
47381
|
+
wantsUsStates,
|
|
47382
|
+
worldTopo
|
|
47383
|
+
};
|
|
47384
|
+
}
|
|
47385
|
+
function parsePathRings(d) {
|
|
47386
|
+
const rings = [];
|
|
47387
|
+
let cur = [];
|
|
47388
|
+
const re = /([MLZ])([^MLZ]*)/g;
|
|
47389
|
+
let m;
|
|
47390
|
+
while (m = re.exec(d)) {
|
|
47391
|
+
if (m[1] === "Z") {
|
|
47392
|
+
if (cur.length) rings.push(cur);
|
|
47393
|
+
cur = [];
|
|
47394
|
+
continue;
|
|
47395
|
+
}
|
|
47396
|
+
if (m[1] === "M" && cur.length) {
|
|
47397
|
+
rings.push(cur);
|
|
47398
|
+
cur = [];
|
|
47399
|
+
}
|
|
47400
|
+
const nums = m[2].split(/[ ,]+/).map(Number);
|
|
47401
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
47402
|
+
const x = nums[i];
|
|
47403
|
+
const y = nums[i + 1];
|
|
47404
|
+
if (Number.isFinite(x) && Number.isFinite(y)) cur.push([x, y]);
|
|
47405
|
+
}
|
|
47406
|
+
}
|
|
47407
|
+
if (cur.length) rings.push(cur);
|
|
47408
|
+
return rings;
|
|
47409
|
+
}
|
|
47410
|
+
function dropAntimeridianWrapSlivers(d, width, height) {
|
|
47411
|
+
const rings = parsePathRings(d);
|
|
47412
|
+
if (rings.length <= 1) return d;
|
|
47413
|
+
const eps = 0.75;
|
|
47414
|
+
const minArea = 3e-3 * width * height;
|
|
47415
|
+
const ringArea = (r) => {
|
|
47416
|
+
let s = 0;
|
|
47417
|
+
for (let i = 0; i < r.length; i++) {
|
|
47418
|
+
const a = r[i];
|
|
47419
|
+
const b = r[(i + 1) % r.length];
|
|
47420
|
+
s += a[0] * b[1] - b[0] * a[1];
|
|
47421
|
+
}
|
|
47422
|
+
return Math.abs(s) / 2;
|
|
47423
|
+
};
|
|
47424
|
+
const areas = rings.map(ringArea);
|
|
47425
|
+
const maxArea = Math.max(...areas);
|
|
47426
|
+
const onVEdge = (a, b) => Math.abs(a[0]) <= eps && Math.abs(b[0]) <= eps || Math.abs(a[0] - width) <= eps && Math.abs(b[0] - width) <= eps;
|
|
47427
|
+
let dropped = false;
|
|
47428
|
+
const kept = rings.filter((r, idx) => {
|
|
47429
|
+
if (areas[idx] >= maxArea || areas[idx] >= minArea) return true;
|
|
47430
|
+
const touches = r.some((p, i) => onVEdge(p, r[(i + 1) % r.length]));
|
|
47431
|
+
if (touches) {
|
|
47432
|
+
dropped = true;
|
|
47433
|
+
return false;
|
|
47434
|
+
}
|
|
47435
|
+
return true;
|
|
47436
|
+
});
|
|
47437
|
+
if (!dropped) return d;
|
|
47438
|
+
return kept.map(
|
|
47439
|
+
(r) => r.map((p, i) => (i ? "L" : "M") + p[0] + "," + p[1]).join("") + "Z"
|
|
47440
|
+
).join("");
|
|
47441
|
+
}
|
|
47442
|
+
function layoutMap(resolved, data, size, opts) {
|
|
47443
|
+
const { palette, isDark } = opts;
|
|
47444
|
+
const { width, height } = size;
|
|
47445
|
+
const {
|
|
47446
|
+
projection,
|
|
47447
|
+
fitTarget,
|
|
47448
|
+
fitIsGlobal,
|
|
47449
|
+
worldLayer,
|
|
47450
|
+
usLayer,
|
|
47451
|
+
usCrisp,
|
|
47452
|
+
worldTopo
|
|
47453
|
+
} = buildMapProjection(resolved, data);
|
|
46366
47454
|
const usContext = usLayer !== null;
|
|
46367
47455
|
const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
|
|
46368
47456
|
const values = resolved.regions.filter((r) => r.value !== void 0).map((r) => r.value);
|
|
46369
|
-
const
|
|
46370
|
-
const rampMin =
|
|
46371
|
-
const rampMax =
|
|
47457
|
+
const allNonNegative = values.length > 0 && values.every((v) => v >= 0);
|
|
47458
|
+
const rampMin = allNonNegative ? 0 : Math.min(...values);
|
|
47459
|
+
const rampMax = Math.max(...values);
|
|
46372
47460
|
const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
|
|
46373
47461
|
const hasRamp = values.length > 0;
|
|
46374
47462
|
const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
|
|
@@ -46389,7 +47477,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46389
47477
|
activeGroup = VALUE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
|
|
46390
47478
|
}
|
|
46391
47479
|
const activeIsScore = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
46392
|
-
const mutedBasemap =
|
|
47480
|
+
const mutedBasemap = activeGroup !== null;
|
|
46393
47481
|
const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
|
|
46394
47482
|
const water = mapBackgroundColor(palette, isDark, mutedBasemap);
|
|
46395
47483
|
const lakeStroke = mix(regionStroke, water, 45);
|
|
@@ -46398,10 +47486,43 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46398
47486
|
palette.bg,
|
|
46399
47487
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
46400
47488
|
);
|
|
47489
|
+
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && resolved.tagGroups.length === 0;
|
|
47490
|
+
const colorByIso = /* @__PURE__ */ new Map();
|
|
47491
|
+
if (colorizeActive) {
|
|
47492
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
47493
|
+
const addEdges = (src) => {
|
|
47494
|
+
for (const [iso, ns] of src) {
|
|
47495
|
+
const cur = adjacency.get(iso);
|
|
47496
|
+
if (cur) cur.push(...ns);
|
|
47497
|
+
else adjacency.set(iso, [...ns]);
|
|
47498
|
+
}
|
|
47499
|
+
};
|
|
47500
|
+
addEdges(buildAdjacency(worldTopo));
|
|
47501
|
+
if (usLayer) {
|
|
47502
|
+
addEdges(buildAdjacency(data.usStates));
|
|
47503
|
+
for (const [country, states] of Object.entries(FOREIGN_BORDER)) {
|
|
47504
|
+
const cn = adjacency.get(country);
|
|
47505
|
+
if (!cn) continue;
|
|
47506
|
+
for (const st of states) {
|
|
47507
|
+
const sn = adjacency.get(st);
|
|
47508
|
+
if (!sn) continue;
|
|
47509
|
+
cn.push(st);
|
|
47510
|
+
sn.push(country);
|
|
47511
|
+
}
|
|
47512
|
+
}
|
|
47513
|
+
}
|
|
47514
|
+
const { byIso, huesNeeded } = assignColors(
|
|
47515
|
+
[...adjacency.keys()],
|
|
47516
|
+
adjacency
|
|
47517
|
+
);
|
|
47518
|
+
const tints = politicalTints(palette, huesNeeded, isDark);
|
|
47519
|
+
for (const [iso, idx] of byIso) colorByIso.set(iso, tints[idx]);
|
|
47520
|
+
}
|
|
47521
|
+
const colorizeStroke = (fill2) => mix(fill2, palette.text, 35);
|
|
46401
47522
|
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
46402
47523
|
const fillForValue = (s) => {
|
|
46403
47524
|
const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
|
|
46404
|
-
const pct =
|
|
47525
|
+
const pct = RAMP_FLOOR2 + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR2);
|
|
46405
47526
|
return mix(rampHue, rampBase, pct);
|
|
46406
47527
|
};
|
|
46407
47528
|
const tagFill = (tags, groupName) => {
|
|
@@ -46433,43 +47554,15 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46433
47554
|
if (activeIsScore) {
|
|
46434
47555
|
return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
|
|
46435
47556
|
}
|
|
47557
|
+
if (colorizeActive) return (r.iso && colorByIso.get(r.iso)) ?? neutralFill;
|
|
46436
47558
|
return tagFill(r.tags, activeGroup) ?? neutralFill;
|
|
46437
47559
|
};
|
|
46438
47560
|
const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
|
|
46439
|
-
const
|
|
46440
|
-
const [[w, s], [e, n]] = resolved.extent;
|
|
46441
|
-
const N = 16;
|
|
46442
|
-
const coords = [];
|
|
46443
|
-
for (let i = 0; i <= N; i++) {
|
|
46444
|
-
const t = i / N;
|
|
46445
|
-
const lon = w + (e - w) * t;
|
|
46446
|
-
const lat = s + (n - s) * t;
|
|
46447
|
-
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
46448
|
-
}
|
|
46449
|
-
return {
|
|
46450
|
-
type: "Feature",
|
|
46451
|
-
properties: {},
|
|
46452
|
-
geometry: { type: "MultiPoint", coordinates: coords }
|
|
46453
|
-
};
|
|
46454
|
-
};
|
|
46455
|
-
let fitFeatures;
|
|
46456
|
-
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46457
|
-
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
46458
|
-
} else {
|
|
46459
|
-
fitFeatures = [extentOutline()];
|
|
46460
|
-
}
|
|
46461
|
-
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
46462
|
-
const projection = projectionFor(resolved.projection);
|
|
46463
|
-
if (resolved.projection !== "albers-usa") {
|
|
46464
|
-
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
46465
|
-
if (centerLon > 180) centerLon -= 360;
|
|
46466
|
-
projection.rotate([-centerLon, 0]);
|
|
46467
|
-
}
|
|
46468
|
-
const TITLE_GAP = 16;
|
|
47561
|
+
const TITLE_GAP2 = 16;
|
|
46469
47562
|
let topPad = FIT_PAD;
|
|
46470
47563
|
if (resolved.title && resolved.pois.length > 0) {
|
|
46471
47564
|
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
46472
|
-
topPad = Math.max(FIT_PAD, bannerBottom +
|
|
47565
|
+
topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
|
|
46473
47566
|
}
|
|
46474
47567
|
const fitBox = [
|
|
46475
47568
|
[FIT_PAD, topPad],
|
|
@@ -46479,21 +47572,20 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46479
47572
|
]
|
|
46480
47573
|
];
|
|
46481
47574
|
projection.fitExtent(fitBox, fitTarget);
|
|
46482
|
-
const fitGB = geoBounds2(fitTarget);
|
|
46483
|
-
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46484
47575
|
let path;
|
|
46485
47576
|
let project;
|
|
46486
47577
|
let stretchParams = null;
|
|
46487
|
-
if (fitIsGlobal) {
|
|
47578
|
+
if (fitIsGlobal && !opts.preferContain) {
|
|
46488
47579
|
const cb = geoPath(projection).bounds(fitTarget);
|
|
46489
47580
|
const bx0 = cb[0][0];
|
|
46490
47581
|
const by0 = cb[0][1];
|
|
46491
47582
|
const cw = cb[1][0] - bx0;
|
|
46492
47583
|
const ch = cb[1][1] - by0;
|
|
46493
|
-
const
|
|
46494
|
-
const
|
|
46495
|
-
const
|
|
46496
|
-
const
|
|
47584
|
+
const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
|
|
47585
|
+
const ox = 0;
|
|
47586
|
+
const oy = topReserve;
|
|
47587
|
+
const sx = cw > 0 ? width / cw : 1;
|
|
47588
|
+
const sy = ch > 0 ? (height - topReserve) / ch : 1;
|
|
46497
47589
|
stretchParams = { sx, sy, ox, oy, bx0, by0 };
|
|
46498
47590
|
const stretch = (x, y) => [
|
|
46499
47591
|
ox + (x - bx0) * sx,
|
|
@@ -46526,7 +47618,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46526
47618
|
const insets = [];
|
|
46527
47619
|
const insetRegions = [];
|
|
46528
47620
|
const insetLabelSeeds = [];
|
|
46529
|
-
|
|
47621
|
+
const akRef = resolved.regions.some((r) => r.iso === "US-AK") || resolved.pois.some((p) => inAlaska(p.lon, p.lat));
|
|
47622
|
+
const hiRef = resolved.regions.some((r) => r.iso === "US-HI") || resolved.pois.some((p) => inHawaii(p.lon, p.lat));
|
|
47623
|
+
if (resolved.projection === "albers-usa" && usLayer && (akRef || hiRef)) {
|
|
46530
47624
|
const PAD = 8;
|
|
46531
47625
|
const GAP = 12;
|
|
46532
47626
|
const yB = height - FIT_PAD;
|
|
@@ -46591,8 +47685,18 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46591
47685
|
);
|
|
46592
47686
|
const d = geoPath(proj)(f) ?? "";
|
|
46593
47687
|
if (!d) return xr;
|
|
47688
|
+
let contextLand;
|
|
47689
|
+
if (iso === "US-AK") {
|
|
47690
|
+
const can = worldLayer.get("CA");
|
|
47691
|
+
const cd = can ? geoPath(proj)(can) ?? "" : "";
|
|
47692
|
+
if (cd)
|
|
47693
|
+
contextLand = {
|
|
47694
|
+
d: cd,
|
|
47695
|
+
fill: colorizeActive ? colorByIso.get("CA") ?? foreignFill : foreignFill
|
|
47696
|
+
};
|
|
47697
|
+
}
|
|
46594
47698
|
const r = regionById.get(iso);
|
|
46595
|
-
let fill2 = neutralFill;
|
|
47699
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? neutralFill : neutralFill;
|
|
46596
47700
|
let lineNumber = -1;
|
|
46597
47701
|
if (r?.layer === "us-state") {
|
|
46598
47702
|
fill2 = regionFill(r);
|
|
@@ -46611,13 +47715,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46611
47715
|
],
|
|
46612
47716
|
// The FITTED inset projection (just fit to this box) — captured so the
|
|
46613
47717
|
// geo-query can invert pixels inside the frame back to AK/HI coords.
|
|
46614
|
-
projection: proj
|
|
47718
|
+
projection: proj,
|
|
47719
|
+
...contextLand && { contextLand }
|
|
46615
47720
|
});
|
|
46616
47721
|
insetRegions.push({
|
|
46617
47722
|
id: iso,
|
|
46618
47723
|
d,
|
|
46619
47724
|
fill: fill2,
|
|
46620
|
-
stroke: regionStroke,
|
|
47725
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46621
47726
|
lineNumber,
|
|
46622
47727
|
layer: "us-state",
|
|
46623
47728
|
...r?.value !== void 0 && { value: r.value },
|
|
@@ -46630,13 +47735,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46630
47735
|
}
|
|
46631
47736
|
return xr;
|
|
46632
47737
|
};
|
|
46633
|
-
|
|
46634
|
-
|
|
46635
|
-
alaskaProjection(),
|
|
46636
|
-
|
|
46637
|
-
|
|
46638
|
-
|
|
46639
|
-
|
|
47738
|
+
let akRight = FIT_PAD;
|
|
47739
|
+
if (akRef)
|
|
47740
|
+
akRight = placeInset("US-AK", alaskaProjection(), FIT_PAD, width * 0.15);
|
|
47741
|
+
if (hiRef)
|
|
47742
|
+
placeInset(
|
|
47743
|
+
"US-HI",
|
|
47744
|
+
hawaiiProjection(),
|
|
47745
|
+
akRef ? akRight + 24 : FIT_PAD,
|
|
47746
|
+
width * 0.1
|
|
47747
|
+
);
|
|
46640
47748
|
}
|
|
46641
47749
|
const conusFit = resolved.projection === "albers-usa" && !!usLayer;
|
|
46642
47750
|
const classifyExtent = conusFit ? geoBounds2(fitTarget) : resolved.extent;
|
|
@@ -46652,15 +47760,24 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46652
47760
|
};
|
|
46653
47761
|
const ringOverlapsView = (ring) => {
|
|
46654
47762
|
let loMin = Infinity, loMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
|
|
47763
|
+
const lons = [];
|
|
46655
47764
|
for (const [rawLon] of ring) {
|
|
46656
47765
|
const lon = normLon(rawLon);
|
|
47766
|
+
lons.push(lon);
|
|
46657
47767
|
if (lon < loMin) loMin = lon;
|
|
46658
47768
|
if (lon > loMax) loMax = lon;
|
|
46659
47769
|
if (rawLon < rawMin) rawMin = rawLon;
|
|
46660
47770
|
if (rawLon > rawMax) rawMax = rawLon;
|
|
46661
47771
|
}
|
|
46662
|
-
|
|
46663
|
-
|
|
47772
|
+
lons.sort((a, b) => a - b);
|
|
47773
|
+
let maxGap = 0;
|
|
47774
|
+
for (let i = 1; i < lons.length; i++)
|
|
47775
|
+
maxGap = Math.max(maxGap, lons[i] - lons[i - 1]);
|
|
47776
|
+
if (lons.length > 1)
|
|
47777
|
+
maxGap = Math.max(maxGap, lons[0] + 360 - lons[lons.length - 1]);
|
|
47778
|
+
const occupiedArc = 360 - maxGap;
|
|
47779
|
+
if (occupiedArc > 270) return false;
|
|
47780
|
+
if (rawMax - rawMin > 180 && occupiedArc < 90) return false;
|
|
46664
47781
|
let px0 = Infinity, py0 = Infinity, px1 = -Infinity, py1 = -Infinity, anyFinite = false;
|
|
46665
47782
|
for (const [lon, lat] of ring) {
|
|
46666
47783
|
const p = project(lon, lat);
|
|
@@ -46733,7 +47850,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46733
47850
|
const regions = [];
|
|
46734
47851
|
const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
|
|
46735
47852
|
for (const [iso, f] of layerFeatures) {
|
|
46736
|
-
if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
|
|
47853
|
+
if (layerKind === "us-state" && usContext && resolved.projection === "albers-usa" && INSET_STATES.has(iso))
|
|
46737
47854
|
continue;
|
|
46738
47855
|
if (layerKind === "country" && usContext && iso === "US") continue;
|
|
46739
47856
|
if (layerKind === "country" && iso === "AQ" && !regionById.has("AQ"))
|
|
@@ -46741,11 +47858,13 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46741
47858
|
const r = regionById.get(iso);
|
|
46742
47859
|
const viewF = shouldCull ? cullFeatureToView(f) : dropFrameFillers(f);
|
|
46743
47860
|
if (!viewF) continue;
|
|
46744
|
-
const
|
|
47861
|
+
const raw = path(viewF) ?? "";
|
|
47862
|
+
const d = fitIsGlobal ? dropAntimeridianWrapSlivers(raw, width, height) : raw;
|
|
46745
47863
|
if (!d) continue;
|
|
46746
47864
|
const isThisLayer = r?.layer === layerKind;
|
|
46747
47865
|
const isForeign = layerKind === "country" && usContext && iso !== "US";
|
|
46748
|
-
|
|
47866
|
+
const baseFill = isForeign ? foreignFill : neutralFill;
|
|
47867
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? baseFill : baseFill;
|
|
46749
47868
|
let label;
|
|
46750
47869
|
let lineNumber = -1;
|
|
46751
47870
|
let layer = "base";
|
|
@@ -46754,15 +47873,21 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46754
47873
|
lineNumber = r.lineNumber;
|
|
46755
47874
|
layer = layerKind;
|
|
46756
47875
|
label = r.name;
|
|
47876
|
+
} else {
|
|
47877
|
+
label = f.properties?.name;
|
|
46757
47878
|
}
|
|
47879
|
+
const labelAnchor = WORLD_LABEL_ANCHORS[iso];
|
|
47880
|
+
const c = labelAnchor ? project(labelAnchor[0], labelAnchor[1]) : path.centroid(viewF);
|
|
47881
|
+
const hasCentroid = c != null && Number.isFinite(c[0]) && Number.isFinite(c[1]);
|
|
46758
47882
|
regions.push({
|
|
46759
47883
|
id: iso,
|
|
46760
47884
|
d,
|
|
46761
47885
|
fill: fill2,
|
|
46762
|
-
stroke: regionStroke,
|
|
47886
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46763
47887
|
lineNumber,
|
|
46764
47888
|
layer,
|
|
46765
47889
|
...label !== void 0 && { label },
|
|
47890
|
+
...hasCentroid && { labelX: c[0], labelY: c[1] },
|
|
46766
47891
|
...isThisLayer && r.value !== void 0 && { value: r.value },
|
|
46767
47892
|
...isThisLayer && Object.keys(r.tags).length > 0 && { tags: r.tags }
|
|
46768
47893
|
});
|
|
@@ -46787,9 +47912,41 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46787
47912
|
});
|
|
46788
47913
|
}
|
|
46789
47914
|
}
|
|
47915
|
+
const pointInRings = (px, py, rings) => {
|
|
47916
|
+
let inside = false;
|
|
47917
|
+
for (const ring of rings) {
|
|
47918
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
47919
|
+
const [xi, yi] = ring[i];
|
|
47920
|
+
const [xj, yj] = ring[j];
|
|
47921
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
47922
|
+
inside = !inside;
|
|
47923
|
+
}
|
|
47924
|
+
}
|
|
47925
|
+
return inside;
|
|
47926
|
+
};
|
|
47927
|
+
const fillHitTargets = [...regions, ...insetRegions].map((r) => ({
|
|
47928
|
+
fill: r.fill,
|
|
47929
|
+
rings: parsePathRings(r.d)
|
|
47930
|
+
}));
|
|
47931
|
+
const fillAt = (x, y) => {
|
|
47932
|
+
let hit = water;
|
|
47933
|
+
for (const t of fillHitTargets)
|
|
47934
|
+
if (pointInRings(x, y, t.rings)) hit = t.fill;
|
|
47935
|
+
return hit;
|
|
47936
|
+
};
|
|
47937
|
+
const labelOnFill = (fill2) => {
|
|
47938
|
+
const color = contrastRatio(fill2, palette.textOnFillDark) >= contrastRatio(fill2, palette.textOnFillLight) ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47939
|
+
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47940
|
+
return {
|
|
47941
|
+
color,
|
|
47942
|
+
halo: contrastRatio(fill2, color) < REGION_LABEL_HALO_RATIO,
|
|
47943
|
+
haloColor
|
|
47944
|
+
};
|
|
47945
|
+
};
|
|
47946
|
+
const reliefAllowed = resolved.directives.noRelief !== true;
|
|
46790
47947
|
const relief = [];
|
|
46791
47948
|
let reliefHatch = null;
|
|
46792
|
-
if (
|
|
47949
|
+
if (reliefAllowed && data.mountainRanges) {
|
|
46793
47950
|
for (const [, f] of decodeLayer(data.mountainRanges)) {
|
|
46794
47951
|
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
46795
47952
|
if (!viewF) continue;
|
|
@@ -46805,16 +47962,32 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46805
47962
|
if (relief.length) {
|
|
46806
47963
|
const darkTone = isDark ? palette.bg : palette.text;
|
|
46807
47964
|
const lightTone = isDark ? palette.text : palette.bg;
|
|
46808
|
-
const
|
|
47965
|
+
const reliefLandRef = colorizeActive ? isDark ? palette.surface : palette.bg : neutralFill;
|
|
47966
|
+
const landLum = relativeLuminance(reliefLandRef);
|
|
46809
47967
|
const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
|
|
46810
47968
|
reliefHatch = {
|
|
46811
|
-
color: mix(tone,
|
|
47969
|
+
color: mix(tone, reliefLandRef, RELIEF_HATCH_STRENGTH),
|
|
46812
47970
|
spacing: RELIEF_HATCH_SPACING,
|
|
46813
47971
|
width: RELIEF_HATCH_WIDTH
|
|
46814
47972
|
};
|
|
46815
47973
|
}
|
|
46816
47974
|
}
|
|
46817
|
-
|
|
47975
|
+
let coastlineStyle = null;
|
|
47976
|
+
if (resolved.directives.noCoastline !== true) {
|
|
47977
|
+
const minDim = Math.min(width, height);
|
|
47978
|
+
coastlineStyle = {
|
|
47979
|
+
color: mix(regionStroke, water, COASTLINE_STROKE_MIX),
|
|
47980
|
+
// N equal-width rings: distance steps outward by COASTLINE_STEP; opacity
|
|
47981
|
+
// fades linearly from NEAR (innermost) to FAR (outermost).
|
|
47982
|
+
lines: Array.from({ length: COASTLINE_RING_COUNT }, (_, k) => ({
|
|
47983
|
+
d: (COASTLINE_D0 + k * COASTLINE_STEP) * minDim,
|
|
47984
|
+
thickness: COASTLINE_THICKNESS * minDim,
|
|
47985
|
+
opacity: COASTLINE_OPACITY_NEAR + (COASTLINE_OPACITY_FAR - COASTLINE_OPACITY_NEAR) * k / (COASTLINE_RING_COUNT - 1)
|
|
47986
|
+
})),
|
|
47987
|
+
minExtent: (isGlobalView ? COASTLINE_MIN_EXTENT_GLOBAL : COASTLINE_MIN_EXTENT) * minDim
|
|
47988
|
+
};
|
|
47989
|
+
}
|
|
47990
|
+
const riverColor = mix(palette.colors.blue, water, 32);
|
|
46818
47991
|
const rivers = [];
|
|
46819
47992
|
if (data.rivers) {
|
|
46820
47993
|
for (const [, f] of decodeLayer(data.rivers)) {
|
|
@@ -46870,38 +48043,108 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46870
48043
|
const xy = project(p.lon, p.lat);
|
|
46871
48044
|
if (xy) projected.push({ p, xy });
|
|
46872
48045
|
}
|
|
46873
|
-
const
|
|
48046
|
+
const placePoi = (e, cx, cy, clusterId) => {
|
|
48047
|
+
const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
|
|
48048
|
+
poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
|
|
48049
|
+
const num = routeNumberById.get(e.p.id);
|
|
48050
|
+
pois.push({
|
|
48051
|
+
id: e.p.id,
|
|
48052
|
+
cx,
|
|
48053
|
+
cy,
|
|
48054
|
+
r: radiusFor(e.p),
|
|
48055
|
+
fill: fill2,
|
|
48056
|
+
stroke: stroke2,
|
|
48057
|
+
lineNumber: e.p.lineNumber,
|
|
48058
|
+
implicit: !!e.p.implicit,
|
|
48059
|
+
isOrigin: originIds.has(e.p.id),
|
|
48060
|
+
...num !== void 0 && { routeNumber: num },
|
|
48061
|
+
...Object.keys(e.p.tags).length > 0 && { tags: e.p.tags },
|
|
48062
|
+
...clusterId !== void 0 && { clusterId }
|
|
48063
|
+
});
|
|
48064
|
+
};
|
|
48065
|
+
const clusters = [];
|
|
48066
|
+
const connected = /* @__PURE__ */ new Set();
|
|
48067
|
+
for (const e of resolved.edges) {
|
|
48068
|
+
connected.add(e.fromId);
|
|
48069
|
+
connected.add(e.toId);
|
|
48070
|
+
}
|
|
48071
|
+
for (const rt of resolved.routes) {
|
|
48072
|
+
rt.stopIds.forEach((id) => connected.add(id));
|
|
48073
|
+
}
|
|
48074
|
+
const radiusOf = (e) => radiusFor(e.p);
|
|
46874
48075
|
for (const e of projected) {
|
|
46875
|
-
|
|
46876
|
-
|
|
46877
|
-
|
|
46878
|
-
|
|
46879
|
-
|
|
46880
|
-
|
|
46881
|
-
|
|
46882
|
-
|
|
46883
|
-
|
|
46884
|
-
|
|
46885
|
-
|
|
46886
|
-
|
|
46887
|
-
|
|
46888
|
-
|
|
46889
|
-
|
|
46890
|
-
|
|
46891
|
-
|
|
46892
|
-
|
|
46893
|
-
|
|
46894
|
-
|
|
46895
|
-
|
|
46896
|
-
|
|
46897
|
-
|
|
46898
|
-
|
|
46899
|
-
|
|
46900
|
-
|
|
46901
|
-
|
|
46902
|
-
|
|
46903
|
-
|
|
46904
|
-
|
|
48076
|
+
if (connected.has(e.p.id)) placePoi(e, e.xy[0], e.xy[1]);
|
|
48077
|
+
}
|
|
48078
|
+
const groups = [];
|
|
48079
|
+
for (const e of projected) {
|
|
48080
|
+
if (connected.has(e.p.id)) continue;
|
|
48081
|
+
const r = radiusOf(e);
|
|
48082
|
+
const near = groups.find(
|
|
48083
|
+
(g) => g.some(
|
|
48084
|
+
(q) => Math.hypot(q.xy[0] - e.xy[0], q.xy[1] - e.xy[1]) < (r + radiusOf(q)) * STACK_OVERLAP
|
|
48085
|
+
)
|
|
48086
|
+
);
|
|
48087
|
+
if (near) near.push(e);
|
|
48088
|
+
else groups.push([e]);
|
|
48089
|
+
}
|
|
48090
|
+
for (const g of groups) {
|
|
48091
|
+
if (g.length === 1) {
|
|
48092
|
+
placePoi(g[0], g[0].xy[0], g[0].xy[1]);
|
|
48093
|
+
continue;
|
|
48094
|
+
}
|
|
48095
|
+
const clusterId = g[0].p.id;
|
|
48096
|
+
const cx0 = g.reduce((s, e) => s + e.xy[0], 0) / g.length;
|
|
48097
|
+
const cy0 = g.reduce((s, e) => s + e.xy[1], 0) / g.length;
|
|
48098
|
+
const maxR = Math.max(...g.map(radiusOf));
|
|
48099
|
+
const sep = 2 * maxR + STACK_RING_GAP;
|
|
48100
|
+
const ringR = Math.max(
|
|
48101
|
+
COLO_R,
|
|
48102
|
+
sep / (2 * Math.sin(Math.PI / Math.max(g.length, 2)))
|
|
48103
|
+
);
|
|
48104
|
+
const positions = g.map((e, i) => {
|
|
48105
|
+
if (g.length <= STACK_RING_MAX) {
|
|
48106
|
+
const ang2 = -Math.PI / 2 + i * 2 * Math.PI / g.length;
|
|
48107
|
+
return {
|
|
48108
|
+
e,
|
|
48109
|
+
mx: cx0 + Math.cos(ang2) * ringR,
|
|
48110
|
+
my: cy0 + Math.sin(ang2) * ringR
|
|
48111
|
+
};
|
|
48112
|
+
}
|
|
48113
|
+
const ang = i * GOLDEN_ANGLE;
|
|
48114
|
+
const rr = ringR * Math.sqrt((i + 1) / g.length);
|
|
48115
|
+
return { e, mx: cx0 + Math.cos(ang) * rr, my: cy0 + Math.sin(ang) * rr };
|
|
48116
|
+
});
|
|
48117
|
+
let minX = cx0 - maxR;
|
|
48118
|
+
let maxX = cx0 + maxR;
|
|
48119
|
+
let minY = cy0 - maxR;
|
|
48120
|
+
let maxY = cy0 + maxR;
|
|
48121
|
+
for (const { mx, my, e } of positions) {
|
|
48122
|
+
const r = radiusOf(e);
|
|
48123
|
+
minX = Math.min(minX, mx - r);
|
|
48124
|
+
maxX = Math.max(maxX, mx + r);
|
|
48125
|
+
minY = Math.min(minY, my - r);
|
|
48126
|
+
maxY = Math.max(maxY, my + r);
|
|
48127
|
+
}
|
|
48128
|
+
let dx = 0;
|
|
48129
|
+
let dy = 0;
|
|
48130
|
+
if (minX + dx < 2) dx = 2 - minX;
|
|
48131
|
+
if (maxX + dx > width - 2) dx = width - 2 - maxX;
|
|
48132
|
+
if (minY + dy < 2) dy = 2 - minY;
|
|
48133
|
+
if (maxY + dy > height - 2) dy = height - 2 - maxY;
|
|
48134
|
+
const legsOut = [];
|
|
48135
|
+
for (const { e, mx, my } of positions) {
|
|
48136
|
+
const fx = mx + dx;
|
|
48137
|
+
const fy = my + dy;
|
|
48138
|
+
placePoi(e, fx, fy, clusterId);
|
|
48139
|
+
legsOut.push({ x2: fx, y2: fy, color: poiFill(e.p).fill });
|
|
48140
|
+
}
|
|
48141
|
+
clusters.push({
|
|
48142
|
+
id: clusterId,
|
|
48143
|
+
cx: cx0 + dx,
|
|
48144
|
+
cy: cy0 + dy,
|
|
48145
|
+
count: g.length,
|
|
48146
|
+
hitR: ringR + maxR + 6,
|
|
48147
|
+
legs: legsOut
|
|
46905
48148
|
});
|
|
46906
48149
|
}
|
|
46907
48150
|
const legs = [];
|
|
@@ -46951,16 +48194,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46951
48194
|
if (!a || !b) continue;
|
|
46952
48195
|
const mx = (a.cx + b.cx) / 2;
|
|
46953
48196
|
const my = (a.cy + b.cy) / 2;
|
|
48197
|
+
const bow = {
|
|
48198
|
+
curved: leg.style === "arc",
|
|
48199
|
+
offset: 0,
|
|
48200
|
+
labelX: mx,
|
|
48201
|
+
labelY: my - 4
|
|
48202
|
+
};
|
|
48203
|
+
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46954
48204
|
legs.push({
|
|
46955
|
-
d: legPath(a, b,
|
|
48205
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46956
48206
|
width: routeWidthFor(Number(leg.value)),
|
|
46957
48207
|
color: mix(palette.text, palette.bg, 72),
|
|
46958
48208
|
arrow: true,
|
|
46959
48209
|
lineNumber: leg.lineNumber,
|
|
46960
48210
|
...leg.label !== void 0 && {
|
|
46961
48211
|
label: leg.label,
|
|
46962
|
-
labelX:
|
|
46963
|
-
labelY:
|
|
48212
|
+
labelX: bow.labelX,
|
|
48213
|
+
labelY: bow.labelY,
|
|
48214
|
+
labelColor: routeLabelStyle.color,
|
|
48215
|
+
labelHalo: routeLabelStyle.halo,
|
|
48216
|
+
labelHaloColor: routeLabelStyle.haloColor
|
|
46964
48217
|
}
|
|
46965
48218
|
});
|
|
46966
48219
|
}
|
|
@@ -46988,20 +48241,29 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46988
48241
|
const a = poiScreen.get(e.fromId);
|
|
46989
48242
|
const b = poiScreen.get(e.toId);
|
|
46990
48243
|
if (!a || !b) return;
|
|
46991
|
-
const
|
|
46992
|
-
const offset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
48244
|
+
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
46993
48245
|
const mx = (a.cx + b.cx) / 2;
|
|
46994
48246
|
const my = (a.cy + b.cy) / 2;
|
|
48247
|
+
const bow = {
|
|
48248
|
+
curved: e.style === "arc" || n > 1,
|
|
48249
|
+
offset: fanOffset,
|
|
48250
|
+
labelX: mx,
|
|
48251
|
+
labelY: my - 4
|
|
48252
|
+
};
|
|
48253
|
+
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46995
48254
|
legs.push({
|
|
46996
|
-
d: legPath(a, b, curved, offset),
|
|
48255
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46997
48256
|
width: widthFor(e),
|
|
46998
48257
|
color: mix(palette.text, palette.bg, 66),
|
|
46999
48258
|
arrow: e.directed,
|
|
47000
48259
|
lineNumber: e.lineNumber,
|
|
47001
48260
|
...e.label !== void 0 && {
|
|
47002
48261
|
label: e.label,
|
|
47003
|
-
labelX:
|
|
47004
|
-
labelY:
|
|
48262
|
+
labelX: bow.labelX,
|
|
48263
|
+
labelY: bow.labelY,
|
|
48264
|
+
labelColor: edgeLabelStyle.color,
|
|
48265
|
+
labelHalo: edgeLabelStyle.halo,
|
|
48266
|
+
labelHaloColor: edgeLabelStyle.haloColor
|
|
47005
48267
|
}
|
|
47006
48268
|
});
|
|
47007
48269
|
});
|
|
@@ -47043,48 +48305,73 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47043
48305
|
}
|
|
47044
48306
|
}
|
|
47045
48307
|
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));
|
|
47046
|
-
const
|
|
48308
|
+
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
48309
|
+
const isCompact = width < COMPACT_WIDTH_PX;
|
|
47047
48310
|
const LABEL_PADX = 6;
|
|
47048
48311
|
const LABEL_PADY = 3;
|
|
47049
|
-
const labelW = (text) => measureLegendText(text,
|
|
47050
|
-
const labelH =
|
|
48312
|
+
const labelW = (text) => measureLegendText(text, FONT2) + 2 * LABEL_PADX;
|
|
48313
|
+
const labelH = FONT2 + 2 * LABEL_PADY;
|
|
47051
48314
|
const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
|
|
47052
|
-
const color =
|
|
47053
|
-
|
|
47054
|
-
|
|
47055
|
-
|
|
48315
|
+
const { color, haloColor } = labelOnFill(fill2);
|
|
48316
|
+
const halfW = measureLegendText(text, FONT2) / 2;
|
|
48317
|
+
const overflows = [y - FONT2 * 0.55, y - FONT2 * 0.1].some(
|
|
48318
|
+
(sy) => fillAt(x - halfW, sy) !== fill2 || fillAt(x + halfW, sy) !== fill2
|
|
47056
48319
|
);
|
|
47057
|
-
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47058
48320
|
labels.push({
|
|
47059
48321
|
x,
|
|
47060
48322
|
y,
|
|
47061
48323
|
text,
|
|
47062
48324
|
anchor: "middle",
|
|
47063
48325
|
color,
|
|
47064
|
-
halo:
|
|
48326
|
+
halo: overflows,
|
|
47065
48327
|
haloColor,
|
|
47066
48328
|
lineNumber
|
|
47067
48329
|
});
|
|
47068
48330
|
};
|
|
47069
|
-
const
|
|
47070
|
-
|
|
47071
|
-
|
|
48331
|
+
const REGION_LABEL_GAP = 2;
|
|
48332
|
+
const regionLabelRect = (cx, cy, text) => {
|
|
48333
|
+
const w = measureLegendText(text, FONT2) + 2 * REGION_LABEL_GAP;
|
|
48334
|
+
return { x: cx - w / 2, y: cy - FONT2 / 2, w, h: FONT2 };
|
|
47072
48335
|
};
|
|
47073
|
-
if (
|
|
47074
|
-
|
|
47075
|
-
|
|
47076
|
-
const
|
|
47077
|
-
if (!
|
|
48336
|
+
if (showRegionLabels) {
|
|
48337
|
+
const frameContainers = new Set(resolved.poiFrameContainers);
|
|
48338
|
+
const entries = regions.map((r) => {
|
|
48339
|
+
const isContainer = frameContainers.has(r.id);
|
|
48340
|
+
if (r.layer === "base" && !isContainer || r.label === void 0)
|
|
48341
|
+
return null;
|
|
48342
|
+
const isUsState = r.layer === "us-state" || r.id.startsWith("US-");
|
|
48343
|
+
const f = isUsState ? usLayer?.get(r.id) : worldLayer.get(r.id);
|
|
48344
|
+
if (!f) return null;
|
|
47078
48345
|
const [[x0, y0], [x1, y1]] = path.bounds(f);
|
|
47079
|
-
const
|
|
47080
|
-
|
|
47081
|
-
const
|
|
48346
|
+
const boxW = x1 - x0;
|
|
48347
|
+
const boxH = y1 - y0;
|
|
48348
|
+
const abbrev = isUsState ? r.id.replace(/^US-/, "") : void 0;
|
|
48349
|
+
const candidates = abbrev !== void 0 ? isCompact ? [abbrev, r.label] : [r.label, abbrev] : [r.label];
|
|
48350
|
+
const anchor = !isUsState ? WORLD_LABEL_ANCHORS[r.id] : void 0;
|
|
47082
48351
|
const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
|
|
47083
|
-
if (!c || !Number.isFinite(c[0]))
|
|
48352
|
+
if (!c || !Number.isFinite(c[0])) return null;
|
|
48353
|
+
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
48354
|
+
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
48355
|
+
const placedRegionRects = [];
|
|
48356
|
+
const POI_LABEL_PAD = 14;
|
|
48357
|
+
const poiObstacles = pois.map((p) => ({
|
|
48358
|
+
x: p.cx - p.r - POI_LABEL_PAD,
|
|
48359
|
+
y: p.cy - p.r - POI_LABEL_PAD,
|
|
48360
|
+
w: 2 * (p.r + POI_LABEL_PAD),
|
|
48361
|
+
h: 2 * (p.r + POI_LABEL_PAD)
|
|
48362
|
+
}));
|
|
48363
|
+
for (const { r, c, boxW, boxH, candidates } of entries) {
|
|
48364
|
+
const text = candidates.find((t) => {
|
|
48365
|
+
if (labelW(t) > boxW || labelH > boxH) return false;
|
|
48366
|
+
const rect = regionLabelRect(c[0], c[1], t);
|
|
48367
|
+
return !placedRegionRects.some((p) => rectsOverlap(rect, p)) && !poiObstacles.some((o) => rectsOverlap(rect, o));
|
|
48368
|
+
});
|
|
48369
|
+
if (text === void 0) continue;
|
|
48370
|
+
placedRegionRects.push(regionLabelRect(c[0], c[1], text));
|
|
47084
48371
|
pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
|
|
47085
48372
|
}
|
|
47086
48373
|
for (const seed of insetLabelSeeds) {
|
|
47087
|
-
const text =
|
|
48374
|
+
const text = isCompact ? seed.iso.replace(/^US-/, "") : seed.name;
|
|
47088
48375
|
const src = regionById.get(seed.iso);
|
|
47089
48376
|
pushRegionLabel(
|
|
47090
48377
|
seed.x,
|
|
@@ -47095,22 +48382,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47095
48382
|
);
|
|
47096
48383
|
}
|
|
47097
48384
|
}
|
|
47098
|
-
|
|
47099
|
-
|
|
47100
|
-
const ordered = [...pois].sort(
|
|
47101
|
-
(a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1)
|
|
47102
|
-
);
|
|
48385
|
+
if (resolved.directives.noPoiLabels !== true) {
|
|
48386
|
+
const ordered = [...pois].filter((p) => p.clusterId === void 0).sort((a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1));
|
|
47103
48387
|
const poiById = new Map(resolved.pois.map((q) => [q.id, q]));
|
|
47104
48388
|
const labelText = (p) => {
|
|
47105
48389
|
const src = poiById.get(p.id);
|
|
47106
48390
|
return src?.label ?? src?.name ?? p.id;
|
|
47107
48391
|
};
|
|
47108
|
-
const poiLabH =
|
|
48392
|
+
const poiLabH = FONT2 * 1.25;
|
|
47109
48393
|
const labelInfo = (p) => {
|
|
47110
48394
|
const text = labelText(p);
|
|
47111
|
-
return { text, w: measureLegendText(text,
|
|
48395
|
+
return { text, w: measureLegendText(text, FONT2) };
|
|
47112
48396
|
};
|
|
47113
48397
|
const GAP = 3;
|
|
48398
|
+
const clusterMembersById = /* @__PURE__ */ new Map();
|
|
48399
|
+
for (const p of pois) {
|
|
48400
|
+
if (p.clusterId === void 0) continue;
|
|
48401
|
+
const arr = clusterMembersById.get(p.clusterId);
|
|
48402
|
+
if (arr) arr.push(p);
|
|
48403
|
+
else clusterMembersById.set(p.clusterId, [p]);
|
|
48404
|
+
}
|
|
47114
48405
|
const inlineRect = (p, w, side) => {
|
|
47115
48406
|
switch (side) {
|
|
47116
48407
|
case "right":
|
|
@@ -47140,11 +48431,11 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47140
48431
|
const x = side === "right" ? rect.x : side === "left" ? rect.x + w : p.cx;
|
|
47141
48432
|
labels.push({
|
|
47142
48433
|
x,
|
|
47143
|
-
y: rect.y + poiLabH / 2 +
|
|
48434
|
+
y: rect.y + poiLabH / 2 + FONT2 / 3,
|
|
47144
48435
|
text,
|
|
47145
48436
|
anchor,
|
|
47146
48437
|
color: palette.text,
|
|
47147
|
-
halo:
|
|
48438
|
+
halo: false,
|
|
47148
48439
|
haloColor: palette.bg,
|
|
47149
48440
|
poiId: p.id,
|
|
47150
48441
|
lineNumber: p.lineNumber
|
|
@@ -47155,43 +48446,60 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47155
48446
|
return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
|
|
47156
48447
|
};
|
|
47157
48448
|
const GROUP_R = 30;
|
|
47158
|
-
const
|
|
48449
|
+
const groups2 = [];
|
|
47159
48450
|
for (const p of ordered) {
|
|
47160
|
-
const near =
|
|
48451
|
+
const near = groups2.find(
|
|
47161
48452
|
(g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
|
|
47162
48453
|
);
|
|
47163
48454
|
if (near) near.push(p);
|
|
47164
|
-
else
|
|
48455
|
+
else groups2.push([p]);
|
|
47165
48456
|
}
|
|
47166
48457
|
const ROW_GAP2 = 3;
|
|
47167
48458
|
const step = poiLabH + ROW_GAP2;
|
|
47168
48459
|
const COL_GAP = 16;
|
|
47169
|
-
const
|
|
47170
|
-
|
|
48460
|
+
const makeItems = (group) => group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
|
|
48461
|
+
const columnRows = (items, side) => {
|
|
47171
48462
|
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
47172
48463
|
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
47173
|
-
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
47174
48464
|
const maxW = Math.max(...items.map((o) => o.w));
|
|
47175
|
-
const
|
|
47176
|
-
const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
|
|
48465
|
+
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
48466
|
+
const colX = side === "right" ? Math.min(right + COL_GAP, width - 2 - maxW) : Math.max(left - COL_GAP, 2 + maxW);
|
|
47177
48467
|
const totalH = items.length * step;
|
|
47178
48468
|
let startY = cyMid - totalH / 2;
|
|
47179
48469
|
startY = Math.max(2, Math.min(startY, height - totalH - 2));
|
|
47180
|
-
items.
|
|
48470
|
+
return items.map((o, i) => {
|
|
47181
48471
|
const rowCy = startY + i * step + step / 2;
|
|
47182
|
-
|
|
47183
|
-
|
|
47184
|
-
|
|
47185
|
-
|
|
47186
|
-
|
|
47187
|
-
|
|
48472
|
+
return {
|
|
48473
|
+
o,
|
|
48474
|
+
colX,
|
|
48475
|
+
rowCy,
|
|
48476
|
+
rect: {
|
|
48477
|
+
x: side === "right" ? colX : colX - o.w,
|
|
48478
|
+
y: rowCy - poiLabH / 2,
|
|
48479
|
+
w: o.w,
|
|
48480
|
+
h: poiLabH
|
|
48481
|
+
}
|
|
48482
|
+
};
|
|
48483
|
+
});
|
|
48484
|
+
};
|
|
48485
|
+
const wouldColumnBeClean = (items, side) => columnRows(items, side).every(
|
|
48486
|
+
({ rect }) => rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect)
|
|
48487
|
+
);
|
|
48488
|
+
const defaultColumnSide = (items) => {
|
|
48489
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48490
|
+
const maxW = Math.max(...items.map((o) => o.w));
|
|
48491
|
+
return right + COL_GAP + maxW <= width - 2 ? "right" : "left";
|
|
48492
|
+
};
|
|
48493
|
+
const commitColumn = (items, side, clusterId) => {
|
|
48494
|
+
for (const { o, colX, rowCy, rect } of columnRows(items, side)) {
|
|
48495
|
+
obstacles.push(rect);
|
|
47188
48496
|
labels.push({
|
|
47189
48497
|
x: colX,
|
|
47190
|
-
y: rowCy +
|
|
48498
|
+
y: rowCy + FONT2 / 3,
|
|
47191
48499
|
text: o.text,
|
|
47192
48500
|
anchor: side === "right" ? "start" : "end",
|
|
47193
48501
|
color: palette.text,
|
|
47194
|
-
halo:
|
|
48502
|
+
halo: false,
|
|
47195
48503
|
haloColor: palette.bg,
|
|
47196
48504
|
leader: {
|
|
47197
48505
|
x1: o.p.cx,
|
|
@@ -47201,24 +48509,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47201
48509
|
},
|
|
47202
48510
|
leaderColor: o.p.fill,
|
|
47203
48511
|
poiId: o.p.id,
|
|
47204
|
-
lineNumber: o.p.lineNumber
|
|
48512
|
+
lineNumber: o.p.lineNumber,
|
|
48513
|
+
...clusterId !== void 0 && { clusterMember: clusterId }
|
|
47205
48514
|
});
|
|
48515
|
+
}
|
|
48516
|
+
};
|
|
48517
|
+
const pushHidden = (p) => {
|
|
48518
|
+
const { text, w } = labelInfo(p);
|
|
48519
|
+
let x = p.cx + p.r + GAP;
|
|
48520
|
+
let anchor = "start";
|
|
48521
|
+
if (x + w > width) {
|
|
48522
|
+
x = p.cx - p.r - GAP - w;
|
|
48523
|
+
anchor = "end";
|
|
48524
|
+
}
|
|
48525
|
+
const y = Math.max(0, Math.min(p.cy - poiLabH / 2, height - poiLabH));
|
|
48526
|
+
labels.push({
|
|
48527
|
+
x: anchor === "start" ? x : x + w,
|
|
48528
|
+
y: y + poiLabH / 2 + FONT2 / 3,
|
|
48529
|
+
text,
|
|
48530
|
+
anchor,
|
|
48531
|
+
color: palette.text,
|
|
48532
|
+
halo: false,
|
|
48533
|
+
haloColor: palette.bg,
|
|
48534
|
+
poiId: p.id,
|
|
48535
|
+
hidden: true,
|
|
48536
|
+
lineNumber: p.lineNumber
|
|
47206
48537
|
});
|
|
47207
48538
|
};
|
|
47208
|
-
for (const
|
|
48539
|
+
for (const [clusterId, members] of clusterMembersById) {
|
|
48540
|
+
if (members.length === 0) continue;
|
|
48541
|
+
const items = makeItems(members);
|
|
48542
|
+
const side = wouldColumnBeClean(items, "right") ? "right" : wouldColumnBeClean(items, "left") ? "left" : defaultColumnSide(items);
|
|
48543
|
+
commitColumn(items, side, clusterId);
|
|
48544
|
+
}
|
|
48545
|
+
const maxExtent = MAX_CLUSTER_EXTENT_FACTOR * Math.min(width, height);
|
|
48546
|
+
const clusterPending = [];
|
|
48547
|
+
for (const g of groups2) {
|
|
48548
|
+
const items = makeItems(g);
|
|
47209
48549
|
if (g.length === 1) {
|
|
47210
|
-
const p =
|
|
47211
|
-
const { text, w } = labelInfo(p);
|
|
48550
|
+
const { p, text, w } = items[0];
|
|
47212
48551
|
const side = ["right", "left", "above", "below"].find(
|
|
47213
48552
|
(s) => inlineFits(p, w, s)
|
|
47214
48553
|
);
|
|
47215
|
-
if (side)
|
|
47216
|
-
|
|
47217
|
-
|
|
48554
|
+
if (side) pushInline(p, text, w, side);
|
|
48555
|
+
else commitColumn(items, defaultColumnSide(items));
|
|
48556
|
+
continue;
|
|
48557
|
+
}
|
|
48558
|
+
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
48559
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48560
|
+
const minCy = Math.min(...items.map((o) => o.p.cy));
|
|
48561
|
+
const maxCy = Math.max(...items.map((o) => o.p.cy));
|
|
48562
|
+
const diag = Math.hypot(right - left, maxCy - minCy);
|
|
48563
|
+
if (diag > maxExtent || items.length > MAX_COLUMN_ROWS) {
|
|
48564
|
+
items.forEach((o) => pushHidden(o.p));
|
|
48565
|
+
} else {
|
|
48566
|
+
clusterPending.push(items);
|
|
48567
|
+
}
|
|
48568
|
+
}
|
|
48569
|
+
for (const items of clusterPending) {
|
|
48570
|
+
const side = ["right", "left"].find(
|
|
48571
|
+
(s) => wouldColumnBeClean(items, s)
|
|
48572
|
+
);
|
|
48573
|
+
if (side) commitColumn(items, side);
|
|
48574
|
+
else items.forEach((o) => pushHidden(o.p));
|
|
48575
|
+
}
|
|
48576
|
+
}
|
|
48577
|
+
if (resolved.directives.noContextLabels !== true) {
|
|
48578
|
+
for (const l of labels) {
|
|
48579
|
+
if (l.hidden) continue;
|
|
48580
|
+
const w = labelW(l.text);
|
|
48581
|
+
const x = l.anchor === "start" ? l.x : l.anchor === "end" ? l.x - w : l.x - w / 2;
|
|
48582
|
+
obstacles.push({ x, y: l.y - labelH / 2, w, h: labelH });
|
|
48583
|
+
}
|
|
48584
|
+
for (const box of insets)
|
|
48585
|
+
obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
|
|
48586
|
+
const countryCandidates = [];
|
|
48587
|
+
for (const f of worldLayer.values()) {
|
|
48588
|
+
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
48589
|
+
if (!iso || regionById.has(iso)) continue;
|
|
48590
|
+
let hasReferencedSub = false;
|
|
48591
|
+
for (const k of regionById.keys())
|
|
48592
|
+
if (k.startsWith(iso + "-")) {
|
|
48593
|
+
hasReferencedSub = true;
|
|
48594
|
+
break;
|
|
47218
48595
|
}
|
|
48596
|
+
if (hasReferencedSub) continue;
|
|
48597
|
+
const b = path.bounds(f);
|
|
48598
|
+
const [x0, y0] = b[0];
|
|
48599
|
+
const [x1, y1] = b[1];
|
|
48600
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48601
|
+
const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
|
|
48602
|
+
const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
|
|
48603
|
+
countryCandidates.push({
|
|
48604
|
+
name: f.properties?.name ?? iso,
|
|
48605
|
+
bbox: [x0, y0, x1, y1],
|
|
48606
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48607
|
+
});
|
|
48608
|
+
}
|
|
48609
|
+
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
48610
|
+
(id) => id.startsWith("US-")
|
|
48611
|
+
);
|
|
48612
|
+
if (usLayer && framedStateContainers) {
|
|
48613
|
+
const containerSet = new Set(resolved.poiFrameContainers);
|
|
48614
|
+
for (const [iso, f] of usLayer) {
|
|
48615
|
+
if (containerSet.has(iso) || regionById.has(iso)) continue;
|
|
48616
|
+
const viewF = cullFeatureToView(f);
|
|
48617
|
+
if (!viewF) continue;
|
|
48618
|
+
const b = path.bounds(viewF);
|
|
48619
|
+
const [x0, y0] = b[0];
|
|
48620
|
+
const [x1, y1] = b[1];
|
|
48621
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48622
|
+
const a = path.centroid(viewF);
|
|
48623
|
+
countryCandidates.push({
|
|
48624
|
+
name: f.properties?.name ?? iso,
|
|
48625
|
+
bbox: [x0, y0, x1, y1],
|
|
48626
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48627
|
+
});
|
|
47219
48628
|
}
|
|
47220
|
-
placeColumn(g);
|
|
47221
48629
|
}
|
|
48630
|
+
const contextLabels = placeContextLabels({
|
|
48631
|
+
projection: resolved.projection,
|
|
48632
|
+
dLonSpan,
|
|
48633
|
+
dLatSpan,
|
|
48634
|
+
width,
|
|
48635
|
+
height,
|
|
48636
|
+
waterBodies: data.waterBodies,
|
|
48637
|
+
countries: countryCandidates,
|
|
48638
|
+
palette,
|
|
48639
|
+
project,
|
|
48640
|
+
collides,
|
|
48641
|
+
// Water labels must stay over open water — `fillAt` returns the ocean
|
|
48642
|
+
// backdrop colour off-land and a region fill on-land (lakes/states count
|
|
48643
|
+
// as land here, which is the safe side for an ocean name).
|
|
48644
|
+
overLand: (x, y) => fillAt(x, y) !== water
|
|
48645
|
+
});
|
|
48646
|
+
labels.push(...contextLabels);
|
|
47222
48647
|
}
|
|
47223
48648
|
let legend = null;
|
|
47224
48649
|
if (!resolved.directives.noLegend) {
|
|
@@ -47255,58 +48680,102 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47255
48680
|
rivers,
|
|
47256
48681
|
relief,
|
|
47257
48682
|
reliefHatch,
|
|
48683
|
+
coastlineStyle,
|
|
47258
48684
|
legs,
|
|
47259
48685
|
pois,
|
|
48686
|
+
clusters,
|
|
47260
48687
|
labels,
|
|
47261
48688
|
legend,
|
|
47262
48689
|
insets,
|
|
47263
48690
|
insetRegions,
|
|
47264
48691
|
projection,
|
|
47265
|
-
stretch: stretchParams
|
|
48692
|
+
stretch: stretchParams,
|
|
48693
|
+
diagnostics: []
|
|
47266
48694
|
};
|
|
47267
48695
|
}
|
|
47268
|
-
var FIT_PAD,
|
|
48696
|
+
var FIT_PAD, RAMP_FLOOR2, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT2, WORLD_LABEL_ANCHORS, MAX_CLUSTER_EXTENT_FACTOR, MAX_COLUMN_ROWS, REGION_LABEL_HALO_RATIO, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT_LIGHT, WATER_TINT_DARK, RIVER_WIDTH, COMPACT_WIDTH_PX, RELIEF_MIN_AREA, RELIEF_MIN_DIM, RELIEF_HATCH_SPACING, RELIEF_HATCH_WIDTH, RELIEF_HATCH_STRENGTH, COASTLINE_RING_COUNT, COASTLINE_D0, COASTLINE_STEP, COASTLINE_THICKNESS, COASTLINE_OPACITY_NEAR, COASTLINE_OPACITY_FAR, COASTLINE_MIN_EXTENT, COASTLINE_MIN_EXTENT_GLOBAL, COASTLINE_STROKE_MIX, FOREIGN_TINT_LIGHT, FOREIGN_TINT_DARK, MUTED_FOREIGN_LIGHT, MUTED_FOREIGN_DARK, COLO_R, GOLDEN_ANGLE, STACK_OVERLAP, STACK_RING_MAX, STACK_RING_GAP, FAN_STEP, ARC_CURVE_FRAC, decodeCache, usConusProjection, alaskaProjection, hawaiiProjection, INSET_STATES, inAlaska, inHawaii, FOREIGN_BORDER, US_NON_CONUS;
|
|
47269
48697
|
var init_layout15 = __esm({
|
|
47270
48698
|
"src/map/layout.ts"() {
|
|
47271
48699
|
"use strict";
|
|
47272
48700
|
init_color_utils();
|
|
48701
|
+
init_geo();
|
|
48702
|
+
init_colorize();
|
|
47273
48703
|
init_colors();
|
|
47274
48704
|
init_label_layout();
|
|
47275
48705
|
init_legend_constants();
|
|
47276
48706
|
init_title_constants();
|
|
48707
|
+
init_context_labels();
|
|
47277
48708
|
FIT_PAD = 24;
|
|
47278
|
-
|
|
48709
|
+
RAMP_FLOOR2 = 15;
|
|
47279
48710
|
R_DEFAULT = 6;
|
|
47280
48711
|
R_MIN = 4;
|
|
47281
48712
|
R_MAX = 22;
|
|
47282
48713
|
W_MIN = 1.25;
|
|
47283
48714
|
W_MAX = 8;
|
|
47284
|
-
|
|
47285
|
-
|
|
48715
|
+
FONT2 = 11;
|
|
48716
|
+
WORLD_LABEL_ANCHORS = {
|
|
48717
|
+
US: [-98.5, 39.5]
|
|
48718
|
+
// CONUS geographic centre (near Lebanon, Kansas)
|
|
48719
|
+
};
|
|
48720
|
+
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
48721
|
+
MAX_COLUMN_ROWS = 7;
|
|
48722
|
+
REGION_LABEL_HALO_RATIO = 4.5;
|
|
47286
48723
|
LAND_TINT_LIGHT = 12;
|
|
47287
48724
|
LAND_TINT_DARK = 24;
|
|
47288
48725
|
TAG_TINT_LIGHT = 60;
|
|
47289
48726
|
TAG_TINT_DARK = 68;
|
|
47290
|
-
WATER_TINT_LIGHT =
|
|
47291
|
-
WATER_TINT_DARK =
|
|
48727
|
+
WATER_TINT_LIGHT = 24;
|
|
48728
|
+
WATER_TINT_DARK = 24;
|
|
47292
48729
|
RIVER_WIDTH = 1.3;
|
|
48730
|
+
COMPACT_WIDTH_PX = 480;
|
|
47293
48731
|
RELIEF_MIN_AREA = 12;
|
|
47294
48732
|
RELIEF_MIN_DIM = 2;
|
|
47295
|
-
RELIEF_HATCH_SPACING =
|
|
47296
|
-
RELIEF_HATCH_WIDTH = 0.
|
|
47297
|
-
RELIEF_HATCH_STRENGTH =
|
|
48733
|
+
RELIEF_HATCH_SPACING = 1.5;
|
|
48734
|
+
RELIEF_HATCH_WIDTH = 0.2;
|
|
48735
|
+
RELIEF_HATCH_STRENGTH = 26;
|
|
48736
|
+
COASTLINE_RING_COUNT = 5;
|
|
48737
|
+
COASTLINE_D0 = 16e-4;
|
|
48738
|
+
COASTLINE_STEP = 28e-4;
|
|
48739
|
+
COASTLINE_THICKNESS = 14e-4;
|
|
48740
|
+
COASTLINE_OPACITY_NEAR = 0.5;
|
|
48741
|
+
COASTLINE_OPACITY_FAR = 0.1;
|
|
48742
|
+
COASTLINE_MIN_EXTENT = 6e-4;
|
|
48743
|
+
COASTLINE_MIN_EXTENT_GLOBAL = 6e-4;
|
|
48744
|
+
COASTLINE_STROKE_MIX = 32;
|
|
47298
48745
|
FOREIGN_TINT_LIGHT = 30;
|
|
47299
48746
|
FOREIGN_TINT_DARK = 62;
|
|
47300
48747
|
MUTED_FOREIGN_LIGHT = 28;
|
|
47301
48748
|
MUTED_FOREIGN_DARK = 16;
|
|
47302
48749
|
COLO_R = 9;
|
|
47303
48750
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
48751
|
+
STACK_OVERLAP = 1;
|
|
48752
|
+
STACK_RING_MAX = 8;
|
|
48753
|
+
STACK_RING_GAP = 4;
|
|
47304
48754
|
FAN_STEP = 16;
|
|
47305
48755
|
ARC_CURVE_FRAC = 0.18;
|
|
48756
|
+
decodeCache = /* @__PURE__ */ new WeakMap();
|
|
47306
48757
|
usConusProjection = () => geoConicEqualArea().parallels([29.5, 45.5]).rotate([96, 0]);
|
|
47307
48758
|
alaskaProjection = () => geoConicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
|
|
47308
48759
|
hawaiiProjection = () => geoMercator();
|
|
47309
48760
|
INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
|
|
48761
|
+
inAlaska = (lon, lat) => lat >= 51 && (lon <= -129 || lon >= 172);
|
|
48762
|
+
inHawaii = (lon, lat) => lat >= 18 && lat <= 23 && lon >= -161 && lon <= -154;
|
|
48763
|
+
FOREIGN_BORDER = {
|
|
48764
|
+
CA: [
|
|
48765
|
+
"US-AK",
|
|
48766
|
+
"US-WA",
|
|
48767
|
+
"US-ID",
|
|
48768
|
+
"US-MT",
|
|
48769
|
+
"US-ND",
|
|
48770
|
+
"US-MN",
|
|
48771
|
+
"US-MI",
|
|
48772
|
+
"US-NY",
|
|
48773
|
+
"US-VT",
|
|
48774
|
+
"US-NH",
|
|
48775
|
+
"US-ME"
|
|
48776
|
+
],
|
|
48777
|
+
MX: ["US-CA", "US-AZ", "US-NM", "US-TX"]
|
|
48778
|
+
};
|
|
47310
48779
|
US_NON_CONUS = /* @__PURE__ */ new Set([
|
|
47311
48780
|
"US-AK",
|
|
47312
48781
|
"US-HI",
|
|
@@ -47326,6 +48795,98 @@ __export(renderer_exports16, {
|
|
|
47326
48795
|
renderMapForExport: () => renderMapForExport
|
|
47327
48796
|
});
|
|
47328
48797
|
import * as d3Selection18 from "d3-selection";
|
|
48798
|
+
function pointInRing2(px, py, ring) {
|
|
48799
|
+
let inside = false;
|
|
48800
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
48801
|
+
const [xi, yi] = ring[i];
|
|
48802
|
+
const [xj, yj] = ring[j];
|
|
48803
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
48804
|
+
inside = !inside;
|
|
48805
|
+
}
|
|
48806
|
+
return inside;
|
|
48807
|
+
}
|
|
48808
|
+
function ringToPath(ring) {
|
|
48809
|
+
let d = "";
|
|
48810
|
+
for (let i = 0; i < ring.length; i++)
|
|
48811
|
+
d += (i ? "L" : "M") + ring[i][0] + "," + ring[i][1];
|
|
48812
|
+
return d + "Z";
|
|
48813
|
+
}
|
|
48814
|
+
function polylineToPath(pts) {
|
|
48815
|
+
let d = "";
|
|
48816
|
+
for (let i = 0; i < pts.length; i++)
|
|
48817
|
+
d += (i ? "L" : "M") + pts[i][0] + "," + pts[i][1];
|
|
48818
|
+
return d;
|
|
48819
|
+
}
|
|
48820
|
+
function ringToCoastPaths(ring, frame) {
|
|
48821
|
+
if (!frame) return [ringToPath(ring)];
|
|
48822
|
+
const n = ring.length;
|
|
48823
|
+
const eps = 0.75;
|
|
48824
|
+
const onL = (x) => Math.abs(x) <= eps;
|
|
48825
|
+
const onR = (x) => Math.abs(x - frame.w) <= eps;
|
|
48826
|
+
const onT = (y) => Math.abs(y) <= eps;
|
|
48827
|
+
const onB = (y) => Math.abs(y - frame.h) <= eps;
|
|
48828
|
+
const isFrameEdge = (a, b) => onL(a[0]) && onL(b[0]) || onR(a[0]) && onR(b[0]) || onT(a[1]) && onT(b[1]) || onB(a[1]) && onB(b[1]);
|
|
48829
|
+
let firstBreak = -1;
|
|
48830
|
+
for (let i = 0; i < n; i++)
|
|
48831
|
+
if (isFrameEdge(ring[i], ring[(i + 1) % n])) {
|
|
48832
|
+
firstBreak = i;
|
|
48833
|
+
break;
|
|
48834
|
+
}
|
|
48835
|
+
if (firstBreak === -1) return [ringToPath(ring)];
|
|
48836
|
+
const paths = [];
|
|
48837
|
+
let cur = [];
|
|
48838
|
+
const start = (firstBreak + 1) % n;
|
|
48839
|
+
for (let k = 0; k < n; k++) {
|
|
48840
|
+
const i = (start + k) % n;
|
|
48841
|
+
const a = ring[i];
|
|
48842
|
+
const b = ring[(i + 1) % n];
|
|
48843
|
+
if (isFrameEdge(a, b)) {
|
|
48844
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48845
|
+
cur = [];
|
|
48846
|
+
continue;
|
|
48847
|
+
}
|
|
48848
|
+
if (cur.length === 0) cur.push(a);
|
|
48849
|
+
cur.push(b);
|
|
48850
|
+
}
|
|
48851
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48852
|
+
return paths;
|
|
48853
|
+
}
|
|
48854
|
+
function coastlineOuterRings(regions, minExtent, frame) {
|
|
48855
|
+
const paths = [];
|
|
48856
|
+
for (const r of regions) {
|
|
48857
|
+
const rings = parsePathRings(r.d);
|
|
48858
|
+
for (let i = 0; i < rings.length; i++) {
|
|
48859
|
+
const ring = rings[i];
|
|
48860
|
+
if (ring.length < 3) continue;
|
|
48861
|
+
let minX = Infinity;
|
|
48862
|
+
let minY = Infinity;
|
|
48863
|
+
let maxX = -Infinity;
|
|
48864
|
+
let maxY = -Infinity;
|
|
48865
|
+
for (const [x, y] of ring) {
|
|
48866
|
+
if (x < minX) minX = x;
|
|
48867
|
+
if (x > maxX) maxX = x;
|
|
48868
|
+
if (y < minY) minY = y;
|
|
48869
|
+
if (y > maxY) maxY = y;
|
|
48870
|
+
}
|
|
48871
|
+
if (Math.max(maxX - minX, maxY - minY) < minExtent) continue;
|
|
48872
|
+
const [fx, fy] = ring[0];
|
|
48873
|
+
let depth = 0;
|
|
48874
|
+
for (let j = 0; j < rings.length; j++)
|
|
48875
|
+
if (j !== i && pointInRing2(fx, fy, rings[j])) depth++;
|
|
48876
|
+
if (depth % 2 === 1) continue;
|
|
48877
|
+
paths.push(...ringToCoastPaths(ring, frame));
|
|
48878
|
+
}
|
|
48879
|
+
}
|
|
48880
|
+
return paths;
|
|
48881
|
+
}
|
|
48882
|
+
function appendWaterLines(g, outerRings, style, flatWater) {
|
|
48883
|
+
const d = outerRings.join(" ");
|
|
48884
|
+
const linesOuterFirst = [...style.lines].sort((a, b) => b.d - a.d);
|
|
48885
|
+
for (const line12 of linesOuterFirst) {
|
|
48886
|
+
g.append("path").attr("d", d).attr("stroke", style.color).attr("stroke-width", 2 * (line12.d + line12.thickness)).attr("stroke-opacity", line12.opacity).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
|
|
48887
|
+
g.append("path").attr("d", d).attr("stroke", flatWater).attr("stroke-width", 2 * line12.d).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
|
|
48888
|
+
}
|
|
48889
|
+
}
|
|
47329
48890
|
function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
|
|
47330
48891
|
d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
47331
48892
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -47338,6 +48899,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47338
48899
|
{
|
|
47339
48900
|
palette,
|
|
47340
48901
|
isDark,
|
|
48902
|
+
// Export-only: forward the contain-fit request from mapExportDimensions so a
|
|
48903
|
+
// clamped/floored (off-aspect) export canvas letterboxes instead of
|
|
48904
|
+
// stretch-distorting. The in-app preview pane passes no exportDims → unset →
|
|
48905
|
+
// keeps the global stretch-fill.
|
|
48906
|
+
preferContain: exportDims?.preferContain ?? false,
|
|
47341
48907
|
...activeGroupOverride !== void 0 && {
|
|
47342
48908
|
activeGroup: activeGroupOverride
|
|
47343
48909
|
}
|
|
@@ -47351,6 +48917,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47351
48917
|
const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
|
|
47352
48918
|
const drawRegion = (g, r, strokeWidth) => {
|
|
47353
48919
|
const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
|
|
48920
|
+
if (r.label) p.attr("data-region-name", r.label);
|
|
48921
|
+
if (r.id && r.id !== "lake") p.attr("data-iso", r.id);
|
|
48922
|
+
if (r.labelX !== void 0 && r.labelY !== void 0) {
|
|
48923
|
+
p.attr("data-label-x", r.labelX).attr("data-label-y", r.labelY);
|
|
48924
|
+
}
|
|
47354
48925
|
if (r.layer !== "base") {
|
|
47355
48926
|
p.classed("dgmo-map-region", true).attr("data-region", r.id);
|
|
47356
48927
|
if (r.value !== void 0) p.attr("data-value", r.value);
|
|
@@ -47380,28 +48951,112 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47380
48951
|
const landClip = defs.append("clipPath").attr("id", landClipId);
|
|
47381
48952
|
for (const r of layout.regions)
|
|
47382
48953
|
if (r.id !== "lake") landClip.append("path").attr("d", r.d);
|
|
47383
|
-
const gRelief = svg.append("g").attr("clip-path", `url(#${landClipId})`).append("g").attr("class", "dgmo-map-relief").attr("clip-path", `url(#${rangeClipId})`).attr("stroke", h.color).attr("stroke-width", h.width).attr("vector-effect", "non-scaling-stroke");
|
|
48954
|
+
const gRelief = svg.append("g").attr("clip-path", `url(#${landClipId})`).style("pointer-events", "none").append("g").attr("class", "dgmo-map-relief").attr("clip-path", `url(#${rangeClipId})`).attr("stroke", h.color).attr("stroke-width", h.width).attr("vector-effect", "non-scaling-stroke");
|
|
47384
48955
|
for (let y = h.spacing; y < height; y += h.spacing) {
|
|
47385
48956
|
gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
|
|
47386
48957
|
}
|
|
47387
48958
|
}
|
|
48959
|
+
if (layout.coastlineStyle) {
|
|
48960
|
+
const cs = layout.coastlineStyle;
|
|
48961
|
+
const maskId = "dgmo-map-water-mask";
|
|
48962
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
48963
|
+
mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
|
|
48964
|
+
const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
|
|
48965
|
+
const lakeD = layout.regions.filter((r) => r.id === "lake").map((r) => r.d).join(" ");
|
|
48966
|
+
if (landD) mask.append("path").attr("d", landD).attr("fill", "black");
|
|
48967
|
+
if (lakeD) mask.append("path").attr("d", lakeD).attr("fill", "white");
|
|
48968
|
+
if (layout.insets.length) {
|
|
48969
|
+
const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
|
|
48970
|
+
for (const box of layout.insets) {
|
|
48971
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48972
|
+
mask.append("path").attr("d", d).attr("fill", "black").attr("stroke", "black").attr("stroke-width", 2 * reach).attr("stroke-linejoin", "round");
|
|
48973
|
+
}
|
|
48974
|
+
}
|
|
48975
|
+
const gWater = svg.append("g").attr("class", "dgmo-map-water-lines").attr("fill", "none").style("pointer-events", "none").attr("mask", `url(#${maskId})`);
|
|
48976
|
+
appendWaterLines(
|
|
48977
|
+
gWater,
|
|
48978
|
+
// Pass the canvas frame so edges collinear with it (the antimeridian on a
|
|
48979
|
+
// world map, regional clipExtent cuts) don't get ringed as fake coast —
|
|
48980
|
+
// land runs cleanly to the render-area edge.
|
|
48981
|
+
coastlineOuterRings(layout.regions, cs.minExtent, {
|
|
48982
|
+
w: width,
|
|
48983
|
+
h: height
|
|
48984
|
+
}),
|
|
48985
|
+
cs,
|
|
48986
|
+
layout.background
|
|
48987
|
+
);
|
|
48988
|
+
const byStroke = /* @__PURE__ */ new Map();
|
|
48989
|
+
for (const r of layout.regions) {
|
|
48990
|
+
const arr = byStroke.get(r.stroke);
|
|
48991
|
+
if (arr) arr.push(r.d);
|
|
48992
|
+
else byStroke.set(r.stroke, [r.d]);
|
|
48993
|
+
}
|
|
48994
|
+
for (const [stroke2, ds] of byStroke)
|
|
48995
|
+
gWater.append("path").attr("d", ds.join(" ")).attr("stroke", stroke2).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
48996
|
+
}
|
|
47388
48997
|
if (layout.rivers.length) {
|
|
47389
|
-
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
48998
|
+
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none").style("pointer-events", "none");
|
|
47390
48999
|
for (const r of layout.rivers) {
|
|
47391
49000
|
gRivers.append("path").attr("d", r.d).attr("stroke", r.color).attr("stroke-width", r.width).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
47392
49001
|
}
|
|
47393
49002
|
}
|
|
47394
49003
|
if (layout.insets.length) {
|
|
47395
49004
|
const insetG = svg.append("g").attr("class", "dgmo-map-insets");
|
|
47396
|
-
|
|
49005
|
+
layout.insets.forEach((box, bi) => {
|
|
47397
49006
|
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
47398
49007
|
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");
|
|
47399
|
-
|
|
49008
|
+
if (box.contextLand) {
|
|
49009
|
+
const clipId = `dgmo-map-inset-clip-${bi}`;
|
|
49010
|
+
defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
|
|
49011
|
+
insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
|
|
49012
|
+
}
|
|
49013
|
+
});
|
|
47400
49014
|
for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
|
|
47401
|
-
|
|
49015
|
+
if (layout.coastlineStyle) {
|
|
49016
|
+
const cs = layout.coastlineStyle;
|
|
49017
|
+
const maskId = "dgmo-map-inset-water-mask";
|
|
49018
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
49019
|
+
for (const box of layout.insets) {
|
|
49020
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
49021
|
+
mask.append("path").attr("d", d).attr("fill", "white");
|
|
49022
|
+
}
|
|
49023
|
+
layout.insets.forEach((box, bi) => {
|
|
49024
|
+
if (box.contextLand)
|
|
49025
|
+
mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
|
|
49026
|
+
});
|
|
49027
|
+
for (const r of layout.insetRegions)
|
|
49028
|
+
if (r.id !== "lake")
|
|
49029
|
+
mask.append("path").attr("d", r.d).attr("fill", "black");
|
|
49030
|
+
for (const r of layout.insetRegions)
|
|
49031
|
+
if (r.id === "lake")
|
|
49032
|
+
mask.append("path").attr("d", r.d).attr("fill", "white");
|
|
49033
|
+
const clipId = "dgmo-map-inset-water-clip";
|
|
49034
|
+
const clip = defs.append("clipPath").attr("id", clipId);
|
|
49035
|
+
for (const box of layout.insets) {
|
|
49036
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
49037
|
+
clip.append("path").attr("d", d);
|
|
49038
|
+
}
|
|
49039
|
+
const gInsetWater = insetG.append("g").attr("clip-path", `url(#${clipId})`).append("g").attr("class", "dgmo-map-inset-water-lines").attr("fill", "none").style("pointer-events", "none").attr("mask", `url(#${maskId})`);
|
|
49040
|
+
appendWaterLines(
|
|
49041
|
+
gInsetWater,
|
|
49042
|
+
coastlineOuterRings(layout.insetRegions, cs.minExtent),
|
|
49043
|
+
cs,
|
|
49044
|
+
layout.background
|
|
49045
|
+
);
|
|
49046
|
+
for (const r of layout.insetRegions)
|
|
49047
|
+
gInsetWater.append("path").attr("d", r.d).attr("stroke", r.stroke).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
49048
|
+
}
|
|
49049
|
+
}
|
|
49050
|
+
const wireSync = (sel, lineNumber) => {
|
|
49051
|
+
if (lineNumber < 1) return;
|
|
49052
|
+
sel.attr("data-line-number", lineNumber);
|
|
49053
|
+
if (onClickItem)
|
|
49054
|
+
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
49055
|
+
};
|
|
47402
49056
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
47403
49057
|
layout.legs.forEach((leg, i) => {
|
|
47404
49058
|
const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
|
|
49059
|
+
wireSync(p, leg.lineNumber);
|
|
47405
49060
|
if (leg.arrow) {
|
|
47406
49061
|
const id = `dgmo-map-arrow-${i}`;
|
|
47407
49062
|
const s = arrowSize(leg.width);
|
|
@@ -47409,25 +49064,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47409
49064
|
p.attr("marker-end", `url(#${id})`);
|
|
47410
49065
|
}
|
|
47411
49066
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
47412
|
-
emitText(
|
|
49067
|
+
const lt = emitText(
|
|
47413
49068
|
gLegs,
|
|
47414
49069
|
leg.labelX,
|
|
47415
49070
|
leg.labelY ?? 0,
|
|
47416
49071
|
leg.label,
|
|
47417
49072
|
"middle",
|
|
47418
|
-
palette.textMuted,
|
|
47419
|
-
haloColor,
|
|
47420
|
-
true,
|
|
49073
|
+
leg.labelColor ?? palette.textMuted,
|
|
49074
|
+
leg.labelHaloColor ?? haloColor,
|
|
49075
|
+
leg.labelHalo ?? true,
|
|
47421
49076
|
LABEL_FONT - 1
|
|
47422
49077
|
);
|
|
49078
|
+
wireSync(lt, leg.lineNumber);
|
|
47423
49079
|
}
|
|
47424
49080
|
});
|
|
49081
|
+
const gSpider = svg.append("g").attr("class", "dgmo-map-spider");
|
|
49082
|
+
for (const cl of layout.clusters) {
|
|
49083
|
+
if (!exportDims) {
|
|
49084
|
+
gSpider.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", cl.hitR).attr("fill", "transparent").attr("data-cluster-hit", cl.id).style("cursor", "pointer");
|
|
49085
|
+
}
|
|
49086
|
+
for (const leg of cl.legs) {
|
|
49087
|
+
gSpider.append("line").attr("x1", cl.cx).attr("y1", cl.cy).attr("x2", leg.x2).attr("y2", leg.y2).attr("stroke", leg.color).attr("stroke-width", 1).attr("data-cluster-deco", cl.id).style("pointer-events", "none");
|
|
49088
|
+
}
|
|
49089
|
+
gSpider.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", 2).attr("fill", mix(palette.textMuted, palette.bg, 40)).attr("data-cluster-deco", cl.id).style("pointer-events", "none");
|
|
49090
|
+
}
|
|
47425
49091
|
const gPois = svg.append("g").attr("class", "dgmo-map-pois");
|
|
47426
49092
|
for (const poi of layout.pois) {
|
|
47427
49093
|
if (poi.isOrigin) {
|
|
47428
49094
|
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);
|
|
47429
49095
|
}
|
|
47430
49096
|
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);
|
|
49097
|
+
if (poi.clusterId !== void 0)
|
|
49098
|
+
c.attr("data-cluster-member", poi.clusterId);
|
|
47431
49099
|
if (poi.tags) {
|
|
47432
49100
|
for (const [group, value] of Object.entries(poi.tags)) {
|
|
47433
49101
|
c.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
@@ -47455,12 +49123,32 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47455
49123
|
}
|
|
47456
49124
|
const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
|
|
47457
49125
|
for (const lab of layout.labels) {
|
|
49126
|
+
if (lab.hidden) {
|
|
49127
|
+
if (exportDims) continue;
|
|
49128
|
+
emitText(
|
|
49129
|
+
gLabels,
|
|
49130
|
+
lab.x,
|
|
49131
|
+
lab.y,
|
|
49132
|
+
lab.text,
|
|
49133
|
+
lab.anchor,
|
|
49134
|
+
lab.color,
|
|
49135
|
+
lab.haloColor,
|
|
49136
|
+
lab.halo,
|
|
49137
|
+
LABEL_FONT,
|
|
49138
|
+
lab.italic,
|
|
49139
|
+
lab.letterSpacing
|
|
49140
|
+
).attr("data-poi", lab.poiId ?? null).attr("data-poi-hidden", "").style("opacity", 0).style("pointer-events", "none");
|
|
49141
|
+
continue;
|
|
49142
|
+
}
|
|
47458
49143
|
if (lab.leader) {
|
|
47459
49144
|
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(
|
|
47460
49145
|
"stroke",
|
|
47461
49146
|
lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
|
|
47462
49147
|
).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
|
|
47463
49148
|
if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
|
|
49149
|
+
if (lab.clusterMember !== void 0)
|
|
49150
|
+
line12.attr("data-cluster-member", lab.clusterMember);
|
|
49151
|
+
wireSync(line12, lab.lineNumber);
|
|
47464
49152
|
}
|
|
47465
49153
|
const t = emitText(
|
|
47466
49154
|
gLabels,
|
|
@@ -47471,11 +49159,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47471
49159
|
lab.color,
|
|
47472
49160
|
lab.haloColor,
|
|
47473
49161
|
lab.halo,
|
|
47474
|
-
LABEL_FONT
|
|
49162
|
+
LABEL_FONT,
|
|
49163
|
+
lab.italic,
|
|
49164
|
+
lab.letterSpacing,
|
|
49165
|
+
lab.lines
|
|
47475
49166
|
);
|
|
47476
49167
|
if (lab.poiId !== void 0) {
|
|
47477
49168
|
t.attr("data-poi", lab.poiId).style("cursor", "default");
|
|
47478
49169
|
}
|
|
49170
|
+
if (lab.clusterMember !== void 0) {
|
|
49171
|
+
t.attr("data-cluster-member", lab.clusterMember);
|
|
49172
|
+
}
|
|
49173
|
+
wireSync(t, lab.lineNumber);
|
|
49174
|
+
}
|
|
49175
|
+
if (!exportDims && layout.clusters.length) {
|
|
49176
|
+
const gBadge = svg.append("g").attr("class", "dgmo-map-cluster-badges");
|
|
49177
|
+
for (const cl of layout.clusters) {
|
|
49178
|
+
const g = gBadge.append("g").attr("data-cluster", cl.id).style("opacity", 0).style("pointer-events", "none");
|
|
49179
|
+
const R = 9;
|
|
49180
|
+
g.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", R).attr("fill", mix(palette.textMuted, palette.bg, 35)).attr("stroke", palette.textMuted).attr("stroke-width", 1);
|
|
49181
|
+
g.append("circle").attr("cx", cl.cx).attr("cy", cl.cy).attr("r", R + 2.5).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1);
|
|
49182
|
+
emitText(
|
|
49183
|
+
g,
|
|
49184
|
+
cl.cx,
|
|
49185
|
+
cl.cy + 3,
|
|
49186
|
+
String(cl.count),
|
|
49187
|
+
"middle",
|
|
49188
|
+
palette.text,
|
|
49189
|
+
palette.bg,
|
|
49190
|
+
false,
|
|
49191
|
+
LABEL_FONT
|
|
49192
|
+
);
|
|
49193
|
+
}
|
|
47479
49194
|
}
|
|
47480
49195
|
if (layout.legend) {
|
|
47481
49196
|
const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
|
|
@@ -47512,7 +49227,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47512
49227
|
svg.append("text").attr("class", "dgmo-map-title").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);
|
|
47513
49228
|
}
|
|
47514
49229
|
if (layout.subtitle) {
|
|
47515
|
-
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);
|
|
49230
|
+
svg.append("text").attr("class", "dgmo-map-subtitle").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);
|
|
47516
49231
|
}
|
|
47517
49232
|
if (layout.caption) {
|
|
47518
49233
|
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);
|
|
@@ -47521,10 +49236,21 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47521
49236
|
function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
|
|
47522
49237
|
renderMap(container, resolved, data, palette, isDark, void 0, exportDims);
|
|
47523
49238
|
}
|
|
47524
|
-
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
|
|
47525
|
-
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color)
|
|
49239
|
+
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic, letterSpacing, lines) {
|
|
49240
|
+
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color);
|
|
49241
|
+
if (lines && lines.length > 1) {
|
|
49242
|
+
const lineHeight = fontSize + 2;
|
|
49243
|
+
const startDy = -((lines.length - 1) / 2) * lineHeight;
|
|
49244
|
+
lines.forEach((ln, i) => {
|
|
49245
|
+
t.append("tspan").attr("x", x).attr("dy", i === 0 ? startDy : lineHeight).text(ln);
|
|
49246
|
+
});
|
|
49247
|
+
} else {
|
|
49248
|
+
t.text(text);
|
|
49249
|
+
}
|
|
49250
|
+
if (italic) t.attr("font-style", "italic");
|
|
49251
|
+
if (letterSpacing) t.attr("letter-spacing", letterSpacing);
|
|
47526
49252
|
if (withHalo) {
|
|
47527
|
-
t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width",
|
|
49253
|
+
t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width", 2).attr("stroke-linejoin", "round").attr("stroke-linecap", "round").attr("stroke-opacity", 0.55);
|
|
47528
49254
|
}
|
|
47529
49255
|
return t;
|
|
47530
49256
|
}
|
|
@@ -47541,6 +49267,56 @@ var init_renderer16 = __esm({
|
|
|
47541
49267
|
}
|
|
47542
49268
|
});
|
|
47543
49269
|
|
|
49270
|
+
// src/map/dimensions.ts
|
|
49271
|
+
var dimensions_exports = {};
|
|
49272
|
+
__export(dimensions_exports, {
|
|
49273
|
+
mapContentAspect: () => mapContentAspect,
|
|
49274
|
+
mapExportDimensions: () => mapExportDimensions
|
|
49275
|
+
});
|
|
49276
|
+
import { geoPath as geoPath2 } from "d3-geo";
|
|
49277
|
+
function mapContentAspect(resolved, data, ref = REF) {
|
|
49278
|
+
const { projection, fitTarget } = buildMapProjection(resolved, data);
|
|
49279
|
+
projection.fitSize([ref, ref], fitTarget);
|
|
49280
|
+
const b = geoPath2(projection).bounds(fitTarget);
|
|
49281
|
+
const w = b[1][0] - b[0][0];
|
|
49282
|
+
const h = b[1][1] - b[0][1];
|
|
49283
|
+
const aspect = w / h;
|
|
49284
|
+
return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
|
|
49285
|
+
}
|
|
49286
|
+
function mapExportDimensions(resolved, data, baseWidth = 1200) {
|
|
49287
|
+
const raw = mapContentAspect(resolved, data);
|
|
49288
|
+
const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
|
|
49289
|
+
const width = baseWidth;
|
|
49290
|
+
let height = Math.round(width / clamped);
|
|
49291
|
+
let chromeReserve = 0;
|
|
49292
|
+
if (resolved.title && resolved.pois.length > 0) {
|
|
49293
|
+
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
49294
|
+
chromeReserve += Math.max(FIT_PAD2, bannerBottom + TITLE_GAP) - FIT_PAD2;
|
|
49295
|
+
}
|
|
49296
|
+
let floored = false;
|
|
49297
|
+
if (height - chromeReserve < MIN_MAP_BAND) {
|
|
49298
|
+
height = Math.round(chromeReserve + MIN_MAP_BAND);
|
|
49299
|
+
floored = true;
|
|
49300
|
+
}
|
|
49301
|
+
const preferContain = clamped !== raw || floored;
|
|
49302
|
+
return { width, height, preferContain };
|
|
49303
|
+
}
|
|
49304
|
+
var FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
|
|
49305
|
+
var init_dimensions = __esm({
|
|
49306
|
+
"src/map/dimensions.ts"() {
|
|
49307
|
+
"use strict";
|
|
49308
|
+
init_title_constants();
|
|
49309
|
+
init_layout15();
|
|
49310
|
+
FIT_PAD2 = 24;
|
|
49311
|
+
TITLE_GAP = 16;
|
|
49312
|
+
ASPECT_MAX = 3;
|
|
49313
|
+
ASPECT_MIN = 0.9;
|
|
49314
|
+
MIN_MAP_BAND = 200;
|
|
49315
|
+
FALLBACK_ASPECT = 1.5;
|
|
49316
|
+
REF = 1e3;
|
|
49317
|
+
}
|
|
49318
|
+
});
|
|
49319
|
+
|
|
47544
49320
|
// src/map/load-data.ts
|
|
47545
49321
|
var load_data_exports = {};
|
|
47546
49322
|
__export(load_data_exports, {
|
|
@@ -47599,12 +49375,17 @@ function loadMapData() {
|
|
|
47599
49375
|
mountainRanges,
|
|
47600
49376
|
naLand,
|
|
47601
49377
|
naLakes,
|
|
49378
|
+
waterBodies,
|
|
47602
49379
|
gazetteer
|
|
47603
49380
|
] = await Promise.all([
|
|
49381
|
+
// worldCoarse (110m) is LOAD-BEARING but NOT a render source: the world
|
|
49382
|
+
// basemap renders from worldDetail (50m) at all scales (resolver pins
|
|
49383
|
+
// basemaps.world = 'detail'). Coarse stays as the authoritative region
|
|
49384
|
+
// name index + dominant-landmass bbox source in resolver.ts. Do not drop it.
|
|
47604
49385
|
readJson(nb, dir, FILES.worldCoarse),
|
|
47605
49386
|
readJson(nb, dir, FILES.worldDetail),
|
|
47606
49387
|
readJson(nb, dir, FILES.usStates),
|
|
47607
|
-
// Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
|
|
49388
|
+
// Lakes/rivers/mountain/NA/water assets are optional — older bundles may predate them.
|
|
47608
49389
|
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
47609
49390
|
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
47610
49391
|
readJson(nb, dir, FILES.mountainRanges).catch(
|
|
@@ -47612,6 +49393,7 @@ function loadMapData() {
|
|
|
47612
49393
|
),
|
|
47613
49394
|
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
47614
49395
|
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
49396
|
+
readJson(nb, dir, FILES.waterBodies).catch(() => void 0),
|
|
47615
49397
|
readJson(nb, dir, FILES.gazetteer)
|
|
47616
49398
|
]);
|
|
47617
49399
|
return validate({
|
|
@@ -47623,7 +49405,8 @@ function loadMapData() {
|
|
|
47623
49405
|
...rivers && { rivers },
|
|
47624
49406
|
...mountainRanges && { mountainRanges },
|
|
47625
49407
|
...naLand && { naLand },
|
|
47626
|
-
...naLakes && { naLakes }
|
|
49408
|
+
...naLakes && { naLakes },
|
|
49409
|
+
...waterBodies && { waterBodies }
|
|
47627
49410
|
});
|
|
47628
49411
|
})().catch((e) => {
|
|
47629
49412
|
cache = void 0;
|
|
@@ -47644,6 +49427,7 @@ var init_load_data = __esm({
|
|
|
47644
49427
|
mountainRanges: "mountain-ranges.json",
|
|
47645
49428
|
naLand: "na-land.json",
|
|
47646
49429
|
naLakes: "na-lakes.json",
|
|
49430
|
+
waterBodies: "water-bodies.json",
|
|
47647
49431
|
gazetteer: "gazetteer.json"
|
|
47648
49432
|
};
|
|
47649
49433
|
CANDIDATE_DIRS = [
|
|
@@ -49657,8 +51441,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
49657
51441
|
const lines = splitParticipantLabel(p.label, LABEL_MAX_CHARS);
|
|
49658
51442
|
if (lines.length === 0) continue;
|
|
49659
51443
|
const widest = Math.max(...lines.map((l) => l.length));
|
|
49660
|
-
const
|
|
49661
|
-
uniformBoxWidth = Math.max(uniformBoxWidth,
|
|
51444
|
+
const labelWidth2 = widest * LABEL_CHAR_WIDTH + 10;
|
|
51445
|
+
uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
|
|
49662
51446
|
}
|
|
49663
51447
|
uniformBoxWidth = Math.min(MAX_BOX_WIDTH, uniformBoxWidth);
|
|
49664
51448
|
const effectiveGap = Math.max(PARTICIPANT_GAP, uniformBoxWidth + 30);
|
|
@@ -52357,15 +54141,15 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
52357
54141
|
textColor,
|
|
52358
54142
|
onClickItem
|
|
52359
54143
|
);
|
|
52360
|
-
const
|
|
52361
|
-
for (const node of nodes)
|
|
54144
|
+
const neighbors2 = /* @__PURE__ */ new Map();
|
|
54145
|
+
for (const node of nodes) neighbors2.set(node, /* @__PURE__ */ new Set());
|
|
52362
54146
|
for (const link of links) {
|
|
52363
|
-
|
|
52364
|
-
|
|
54147
|
+
neighbors2.get(link.source).add(link.target);
|
|
54148
|
+
neighbors2.get(link.target).add(link.source);
|
|
52365
54149
|
}
|
|
52366
54150
|
const FADE_OPACITY3 = 0.1;
|
|
52367
54151
|
function handleMouseEnter(hovered) {
|
|
52368
|
-
const connected =
|
|
54152
|
+
const connected = neighbors2.get(hovered);
|
|
52369
54153
|
g.selectAll(".arc-link").each(function() {
|
|
52370
54154
|
const el = d3Selection23.select(this);
|
|
52371
54155
|
const src = el.attr("data-source");
|
|
@@ -53273,10 +55057,12 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
|
|
|
53273
55057
|
const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
|
|
53274
55058
|
const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
|
|
53275
55059
|
const innerWidth = width - margin.left - margin.right;
|
|
53276
|
-
const
|
|
53277
|
-
const rowH = Math.min(ctx.structural(28),
|
|
55060
|
+
const availInnerHeight = height - margin.top - margin.bottom;
|
|
55061
|
+
const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
|
|
55062
|
+
const innerHeight = rowH * sorted.length;
|
|
55063
|
+
const usedHeight = margin.top + innerHeight + margin.bottom;
|
|
53278
55064
|
const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
53279
|
-
const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height",
|
|
55065
|
+
const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height", usedHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
|
|
53280
55066
|
if (ctx.isBelowFloor) {
|
|
53281
55067
|
svg.attr("width", "100%");
|
|
53282
55068
|
}
|
|
@@ -54300,7 +56086,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54300
56086
|
8,
|
|
54301
56087
|
Math.floor(OVERLAP_WRAP_TARGET_W / OVERLAP_CH_W)
|
|
54302
56088
|
);
|
|
54303
|
-
function
|
|
56089
|
+
function wrapLabel3(text, maxChars) {
|
|
54304
56090
|
const words = text.split(/\s+/).filter(Boolean);
|
|
54305
56091
|
const lines = [];
|
|
54306
56092
|
let cur = "";
|
|
@@ -54346,7 +56132,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54346
56132
|
if (!ov.label) continue;
|
|
54347
56133
|
const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
|
|
54348
56134
|
if (idxs.some((idx) => idx < 0)) continue;
|
|
54349
|
-
const lines =
|
|
56135
|
+
const lines = wrapLabel3(ov.label, MAX_WRAP_CHARS);
|
|
54350
56136
|
wrappedOverlapLabels.set(ov, lines);
|
|
54351
56137
|
const dir = predictOverlapDirRaw(idxs);
|
|
54352
56138
|
const longest = lines.reduce((m, l) => Math.max(m, l.length), 0);
|
|
@@ -55784,6 +57570,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55784
57570
|
const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
|
|
55785
57571
|
const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
|
|
55786
57572
|
const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
|
|
57573
|
+
const { mapExportDimensions: mapExportDimensions2 } = await Promise.resolve().then(() => (init_dimensions(), dimensions_exports));
|
|
55787
57574
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
55788
57575
|
const mapParsed = parseMap2(content);
|
|
55789
57576
|
let mapData = options?.mapData;
|
|
@@ -55796,14 +57583,15 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55796
57583
|
}
|
|
55797
57584
|
}
|
|
55798
57585
|
const mapResolved = resolveMap2(mapParsed, mapData);
|
|
55799
|
-
const
|
|
57586
|
+
const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
|
|
57587
|
+
const container2 = createExportContainer(dims2.width, dims2.height);
|
|
55800
57588
|
renderMapForExport2(
|
|
55801
57589
|
container2,
|
|
55802
57590
|
mapResolved,
|
|
55803
57591
|
mapData,
|
|
55804
57592
|
effectivePalette2,
|
|
55805
57593
|
theme === "dark",
|
|
55806
|
-
|
|
57594
|
+
dims2
|
|
55807
57595
|
);
|
|
55808
57596
|
return finalizeSvgExport(container2, theme, effectivePalette2);
|
|
55809
57597
|
}
|
|
@@ -56645,7 +58433,8 @@ async function render(content, options) {
|
|
|
56645
58433
|
...options?.c4Container !== void 0 && {
|
|
56646
58434
|
c4Container: options.c4Container
|
|
56647
58435
|
},
|
|
56648
|
-
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup }
|
|
58436
|
+
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
|
|
58437
|
+
...options?.mapData !== void 0 && { mapData: options.mapData }
|
|
56649
58438
|
});
|
|
56650
58439
|
if (chartType === "map") {
|
|
56651
58440
|
try {
|
|
@@ -56656,7 +58445,7 @@ async function render(content, options) {
|
|
|
56656
58445
|
Promise.resolve().then(() => (init_load_data(), load_data_exports))
|
|
56657
58446
|
]
|
|
56658
58447
|
);
|
|
56659
|
-
const data = await loadMapData2();
|
|
58448
|
+
const data = options?.mapData ?? await loadMapData2();
|
|
56660
58449
|
diagnostics = [...resolveMap2(parseMap2(content), data).diagnostics];
|
|
56661
58450
|
} catch {
|
|
56662
58451
|
}
|