@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/auto.cjs
CHANGED
|
@@ -93,18 +93,18 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
93
93
|
const results = [];
|
|
94
94
|
for (let i = 0; i < points.length; i++) {
|
|
95
95
|
const pt = points[i];
|
|
96
|
-
const
|
|
96
|
+
const labelWidth2 = pt.label.length * fontSize * CHAR_WIDTH_RATIO + 8;
|
|
97
97
|
let best = null;
|
|
98
98
|
const directions = [
|
|
99
99
|
{
|
|
100
100
|
// Above
|
|
101
101
|
gen: (offset) => {
|
|
102
|
-
const lx = pt.cx -
|
|
102
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
103
103
|
const ly = pt.cy - offset - labelHeight;
|
|
104
|
-
if (ly < chartBounds.top || lx < chartBounds.left || lx +
|
|
104
|
+
if (ly < chartBounds.top || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
|
|
105
105
|
return null;
|
|
106
106
|
return {
|
|
107
|
-
rect: { x: lx, y: ly, w:
|
|
107
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
108
108
|
textX: pt.cx,
|
|
109
109
|
textY: ly + labelHeight / 2,
|
|
110
110
|
anchor: "middle"
|
|
@@ -114,12 +114,12 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
114
114
|
{
|
|
115
115
|
// Below
|
|
116
116
|
gen: (offset) => {
|
|
117
|
-
const lx = pt.cx -
|
|
117
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
118
118
|
const ly = pt.cy + offset;
|
|
119
|
-
if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx +
|
|
119
|
+
if (ly + labelHeight > chartBounds.bottom || lx < chartBounds.left || lx + labelWidth2 > chartBounds.right)
|
|
120
120
|
return null;
|
|
121
121
|
return {
|
|
122
|
-
rect: { x: lx, y: ly, w:
|
|
122
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
123
123
|
textX: pt.cx,
|
|
124
124
|
textY: ly + labelHeight / 2,
|
|
125
125
|
anchor: "middle"
|
|
@@ -131,10 +131,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
131
131
|
gen: (offset) => {
|
|
132
132
|
const lx = pt.cx + offset;
|
|
133
133
|
const ly = pt.cy - labelHeight / 2;
|
|
134
|
-
if (lx +
|
|
134
|
+
if (lx + labelWidth2 > chartBounds.right || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
|
|
135
135
|
return null;
|
|
136
136
|
return {
|
|
137
|
-
rect: { x: lx, y: ly, w:
|
|
137
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
138
138
|
textX: lx,
|
|
139
139
|
textY: pt.cy,
|
|
140
140
|
anchor: "start"
|
|
@@ -144,13 +144,13 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
144
144
|
{
|
|
145
145
|
// Left
|
|
146
146
|
gen: (offset) => {
|
|
147
|
-
const lx = pt.cx - offset -
|
|
147
|
+
const lx = pt.cx - offset - labelWidth2;
|
|
148
148
|
const ly = pt.cy - labelHeight / 2;
|
|
149
149
|
if (lx < chartBounds.left || ly < chartBounds.top || ly + labelHeight > chartBounds.bottom)
|
|
150
150
|
return null;
|
|
151
151
|
return {
|
|
152
|
-
rect: { x: lx, y: ly, w:
|
|
153
|
-
textX: lx +
|
|
152
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
153
|
+
textX: lx + labelWidth2,
|
|
154
154
|
textY: pt.cy,
|
|
155
155
|
anchor: "end"
|
|
156
156
|
};
|
|
@@ -200,10 +200,10 @@ function computeQuadrantPointLabels(points, chartBounds, obstacles, pointRadius,
|
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
if (!best) {
|
|
203
|
-
const lx = pt.cx -
|
|
203
|
+
const lx = pt.cx - labelWidth2 / 2;
|
|
204
204
|
const ly = pt.cy - minGap - labelHeight;
|
|
205
205
|
best = {
|
|
206
|
-
rect: { x: lx, y: ly, w:
|
|
206
|
+
rect: { x: lx, y: ly, w: labelWidth2, h: labelHeight },
|
|
207
207
|
textX: pt.cx,
|
|
208
208
|
textY: ly + labelHeight / 2,
|
|
209
209
|
anchor: "middle",
|
|
@@ -763,6 +763,9 @@ var init_reserved_key_registry = __esm({
|
|
|
763
763
|
"value",
|
|
764
764
|
"label",
|
|
765
765
|
"style"
|
|
766
|
+
// `surface:` was removed in the 2026-06-02 defaults-on review — it is no longer
|
|
767
|
+
// a recognized metadata key (the route/edge surface feature was cut; §24B.7).
|
|
768
|
+
// A stray `surface: water` is no longer captured as a reserved key.
|
|
766
769
|
]);
|
|
767
770
|
ORG_REGISTRY = staticRegistry([
|
|
768
771
|
"color",
|
|
@@ -817,9 +820,7 @@ var init_reserved_key_registry = __esm({
|
|
|
817
820
|
BOXES_AND_LINES_REGISTRY = staticRegistry([
|
|
818
821
|
"color",
|
|
819
822
|
"description",
|
|
820
|
-
"
|
|
821
|
-
"split",
|
|
822
|
-
"fanout"
|
|
823
|
+
"value"
|
|
823
824
|
]);
|
|
824
825
|
TIMELINE_REGISTRY = staticRegistry([
|
|
825
826
|
"color",
|
|
@@ -1825,77 +1826,266 @@ function getSegmentColors(palette, count) {
|
|
|
1825
1826
|
(_, i) => hslToHex(Math.round((startHue + i * step) % 360), avgS, avgL)
|
|
1826
1827
|
);
|
|
1827
1828
|
}
|
|
1829
|
+
function politicalTints(palette, count, isDark) {
|
|
1830
|
+
if (count <= 0) return [];
|
|
1831
|
+
const base = isDark ? palette.surface : palette.bg;
|
|
1832
|
+
const c = palette.colors;
|
|
1833
|
+
const swatches = [
|
|
1834
|
+
.../* @__PURE__ */ new Set([
|
|
1835
|
+
c.green,
|
|
1836
|
+
c.yellow,
|
|
1837
|
+
c.orange,
|
|
1838
|
+
c.purple,
|
|
1839
|
+
c.red,
|
|
1840
|
+
c.teal,
|
|
1841
|
+
c.cyan,
|
|
1842
|
+
c.blue
|
|
1843
|
+
])
|
|
1844
|
+
];
|
|
1845
|
+
const bands = isDark ? POLITICAL_TINT_BANDS.dark : POLITICAL_TINT_BANDS.light;
|
|
1846
|
+
const out = [];
|
|
1847
|
+
for (const pct of bands) {
|
|
1848
|
+
if (out.length >= count) break;
|
|
1849
|
+
for (const s of swatches) out.push(mix(s, base, pct));
|
|
1850
|
+
}
|
|
1851
|
+
return out.slice(0, count);
|
|
1852
|
+
}
|
|
1853
|
+
var POLITICAL_TINT_BANDS;
|
|
1828
1854
|
var init_color_utils = __esm({
|
|
1829
1855
|
"src/palettes/color-utils.ts"() {
|
|
1830
1856
|
"use strict";
|
|
1857
|
+
POLITICAL_TINT_BANDS = {
|
|
1858
|
+
light: [32, 48, 64, 80],
|
|
1859
|
+
dark: [44, 58, 72, 86]
|
|
1860
|
+
};
|
|
1831
1861
|
}
|
|
1832
1862
|
});
|
|
1833
1863
|
|
|
1834
|
-
// src/palettes/
|
|
1835
|
-
var
|
|
1836
|
-
var
|
|
1837
|
-
"src/palettes/
|
|
1864
|
+
// src/palettes/atlas.ts
|
|
1865
|
+
var atlasPalette;
|
|
1866
|
+
var init_atlas = __esm({
|
|
1867
|
+
"src/palettes/atlas.ts"() {
|
|
1838
1868
|
"use strict";
|
|
1839
1869
|
init_registry();
|
|
1840
|
-
|
|
1841
|
-
id: "
|
|
1842
|
-
name: "
|
|
1870
|
+
atlasPalette = {
|
|
1871
|
+
id: "atlas",
|
|
1872
|
+
name: "Atlas",
|
|
1843
1873
|
light: {
|
|
1844
|
-
bg: "#
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1874
|
+
bg: "#f3ead3",
|
|
1875
|
+
// warm manila / parchment
|
|
1876
|
+
surface: "#ece0c0",
|
|
1877
|
+
// deeper paper (cards, panels)
|
|
1878
|
+
overlay: "#e8dab8",
|
|
1879
|
+
// popovers, dropdowns
|
|
1880
|
+
border: "#bcaa86",
|
|
1881
|
+
// muted sepia rule line
|
|
1882
|
+
text: "#463a26",
|
|
1883
|
+
// aged sepia-brown ink
|
|
1884
|
+
textMuted: "#7a6a4f",
|
|
1885
|
+
// faded annotation ink
|
|
1886
|
+
textOnFillLight: "#f7f1de",
|
|
1887
|
+
// parchment (light text on dark fills)
|
|
1888
|
+
textOnFillDark: "#3a2e1c",
|
|
1889
|
+
// deep ink (dark text on light fills)
|
|
1890
|
+
primary: "#5b7a99",
|
|
1891
|
+
// pull-down map ocean (steel-blue)
|
|
1892
|
+
secondary: "#7e9a6f",
|
|
1893
|
+
// lowland sage / celadon
|
|
1894
|
+
accent: "#b07f7c",
|
|
1895
|
+
// dusty rose
|
|
1896
|
+
destructive: "#b25a45",
|
|
1897
|
+
// brick / terracotta
|
|
1856
1898
|
colors: {
|
|
1857
|
-
red: "#
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1899
|
+
red: "#bf6a52",
|
|
1900
|
+
// terracotta brick
|
|
1901
|
+
orange: "#cf9a5c",
|
|
1902
|
+
// map tan / ochre
|
|
1903
|
+
yellow: "#cdb35e",
|
|
1904
|
+
// straw / muted lemon
|
|
1905
|
+
green: "#7e9a6f",
|
|
1906
|
+
// sage / celadon lowland
|
|
1907
|
+
blue: "#5b7a99",
|
|
1908
|
+
// steel-blue ocean
|
|
1909
|
+
purple: "#9a7fa6",
|
|
1910
|
+
// dusty lilac / mauve
|
|
1911
|
+
teal: "#6fa094",
|
|
1912
|
+
// muted seafoam
|
|
1913
|
+
cyan: "#79a7b5",
|
|
1914
|
+
// shallow-water blue
|
|
1915
|
+
gray: "#8a7d68",
|
|
1916
|
+
// warm taupe
|
|
1917
|
+
black: "#463a26",
|
|
1918
|
+
// ink
|
|
1919
|
+
white: "#ece0c0"
|
|
1920
|
+
// paper
|
|
1868
1921
|
}
|
|
1869
1922
|
},
|
|
1870
1923
|
dark: {
|
|
1871
|
-
bg: "#
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1924
|
+
bg: "#1e2a33",
|
|
1925
|
+
// deep map ocean (night globe)
|
|
1926
|
+
surface: "#27353f",
|
|
1927
|
+
// raised ocean
|
|
1928
|
+
overlay: "#2e3d48",
|
|
1929
|
+
// popovers, dropdowns
|
|
1930
|
+
border: "#3d4f5c",
|
|
1931
|
+
// depth-contour line
|
|
1932
|
+
text: "#e8dcc0",
|
|
1933
|
+
// parchment ink, inverted
|
|
1934
|
+
textMuted: "#a89a7d",
|
|
1935
|
+
// faded label
|
|
1936
|
+
textOnFillLight: "#f7f1de",
|
|
1937
|
+
// parchment
|
|
1938
|
+
textOnFillDark: "#1a242c",
|
|
1939
|
+
// deep ocean ink
|
|
1940
|
+
primary: "#7ba0bf",
|
|
1941
|
+
// brighter ocean
|
|
1942
|
+
secondary: "#9bb588",
|
|
1943
|
+
// sage, lifted
|
|
1944
|
+
accent: "#cf9a96",
|
|
1945
|
+
// dusty rose, lifted
|
|
1946
|
+
destructive: "#c9745c",
|
|
1947
|
+
// brick, lifted
|
|
1883
1948
|
colors: {
|
|
1884
|
-
red: "#
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1949
|
+
red: "#cf7a60",
|
|
1950
|
+
// terracotta
|
|
1951
|
+
orange: "#d9a96a",
|
|
1952
|
+
// tan / ochre
|
|
1953
|
+
yellow: "#d8c074",
|
|
1954
|
+
// straw
|
|
1955
|
+
green: "#9bb588",
|
|
1956
|
+
// sage lowland
|
|
1957
|
+
blue: "#7ba0bf",
|
|
1958
|
+
// ocean
|
|
1959
|
+
purple: "#b59ac0",
|
|
1960
|
+
// lilac / mauve
|
|
1961
|
+
teal: "#85b3a6",
|
|
1962
|
+
// seafoam
|
|
1963
|
+
cyan: "#92bccb",
|
|
1964
|
+
// shallow-water blue
|
|
1965
|
+
gray: "#9a8d76",
|
|
1966
|
+
// warm taupe
|
|
1967
|
+
black: "#27353f",
|
|
1968
|
+
// raised ocean
|
|
1969
|
+
white: "#e8dcc0"
|
|
1970
|
+
// parchment
|
|
1895
1971
|
}
|
|
1896
1972
|
}
|
|
1897
1973
|
};
|
|
1898
|
-
registerPalette(
|
|
1974
|
+
registerPalette(atlasPalette);
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
// src/palettes/blueprint.ts
|
|
1979
|
+
var blueprintPalette;
|
|
1980
|
+
var init_blueprint = __esm({
|
|
1981
|
+
"src/palettes/blueprint.ts"() {
|
|
1982
|
+
"use strict";
|
|
1983
|
+
init_registry();
|
|
1984
|
+
blueprintPalette = {
|
|
1985
|
+
id: "blueprint",
|
|
1986
|
+
name: "Blueprint",
|
|
1987
|
+
light: {
|
|
1988
|
+
bg: "#f4f8fb",
|
|
1989
|
+
// pale drafting white (faint cyan)
|
|
1990
|
+
surface: "#e6eef4",
|
|
1991
|
+
// drafting panel
|
|
1992
|
+
overlay: "#dde9f1",
|
|
1993
|
+
// popovers, dropdowns
|
|
1994
|
+
border: "#aac3d6",
|
|
1995
|
+
// pale blue grid line
|
|
1996
|
+
text: "#123a5e",
|
|
1997
|
+
// blueprint navy ink
|
|
1998
|
+
textMuted: "#4f7390",
|
|
1999
|
+
// faint draft note
|
|
2000
|
+
textOnFillLight: "#f4f8fb",
|
|
2001
|
+
// drafting white
|
|
2002
|
+
textOnFillDark: "#0c2f4d",
|
|
2003
|
+
// deep blueprint navy
|
|
2004
|
+
primary: "#1f5e8c",
|
|
2005
|
+
// blueprint blue
|
|
2006
|
+
secondary: "#5b7d96",
|
|
2007
|
+
// steel
|
|
2008
|
+
accent: "#b08a3e",
|
|
2009
|
+
// draftsman's ochre highlight
|
|
2010
|
+
destructive: "#c0504d",
|
|
2011
|
+
// correction red
|
|
2012
|
+
colors: {
|
|
2013
|
+
red: "#c25a4e",
|
|
2014
|
+
// correction red
|
|
2015
|
+
orange: "#c2823e",
|
|
2016
|
+
// ochre
|
|
2017
|
+
yellow: "#c2a843",
|
|
2018
|
+
// pencil gold
|
|
2019
|
+
green: "#4f8a6b",
|
|
2020
|
+
// drafting green
|
|
2021
|
+
blue: "#1f5e8c",
|
|
2022
|
+
// blueprint blue
|
|
2023
|
+
purple: "#6f5e96",
|
|
2024
|
+
// indigo pencil
|
|
2025
|
+
teal: "#3a8a8a",
|
|
2026
|
+
// teal
|
|
2027
|
+
cyan: "#3f8fb5",
|
|
2028
|
+
// cyan
|
|
2029
|
+
gray: "#7e8e98",
|
|
2030
|
+
// graphite
|
|
2031
|
+
black: "#123a5e",
|
|
2032
|
+
// navy ink
|
|
2033
|
+
white: "#e6eef4"
|
|
2034
|
+
// panel
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
dark: {
|
|
2038
|
+
bg: "#103a5e",
|
|
2039
|
+
// deep blueprint blue (cyanotype ground)
|
|
2040
|
+
surface: "#16466e",
|
|
2041
|
+
// raised sheet
|
|
2042
|
+
overlay: "#1c5180",
|
|
2043
|
+
// popovers, dropdowns
|
|
2044
|
+
border: "#3a6f96",
|
|
2045
|
+
// grid line
|
|
2046
|
+
text: "#eaf2f8",
|
|
2047
|
+
// chalk white
|
|
2048
|
+
textMuted: "#9fc0d6",
|
|
2049
|
+
// faint chalk note
|
|
2050
|
+
textOnFillLight: "#eaf2f8",
|
|
2051
|
+
// chalk white
|
|
2052
|
+
textOnFillDark: "#0c2f4d",
|
|
2053
|
+
// deep blueprint navy
|
|
2054
|
+
primary: "#7fb8d8",
|
|
2055
|
+
// chalk cyan
|
|
2056
|
+
secondary: "#9fb8c8",
|
|
2057
|
+
// pale steel
|
|
2058
|
+
accent: "#d8c27a",
|
|
2059
|
+
// chalk amber
|
|
2060
|
+
destructive: "#e08a7a",
|
|
2061
|
+
// chalk correction red
|
|
2062
|
+
colors: {
|
|
2063
|
+
red: "#e0907e",
|
|
2064
|
+
// chalk red
|
|
2065
|
+
orange: "#e0ab78",
|
|
2066
|
+
// chalk amber
|
|
2067
|
+
yellow: "#e3d089",
|
|
2068
|
+
// chalk gold
|
|
2069
|
+
green: "#93c79e",
|
|
2070
|
+
// chalk green
|
|
2071
|
+
blue: "#8ec3e0",
|
|
2072
|
+
// chalk cyan-blue
|
|
2073
|
+
purple: "#b6a6d8",
|
|
2074
|
+
// chalk indigo
|
|
2075
|
+
teal: "#84c7c2",
|
|
2076
|
+
// chalk teal
|
|
2077
|
+
cyan: "#9fd6e0",
|
|
2078
|
+
// chalk cyan
|
|
2079
|
+
gray: "#aebecb",
|
|
2080
|
+
// chalk graphite
|
|
2081
|
+
black: "#16466e",
|
|
2082
|
+
// raised sheet
|
|
2083
|
+
white: "#eaf2f8"
|
|
2084
|
+
// chalk white
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
registerPalette(blueprintPalette);
|
|
1899
2089
|
}
|
|
1900
2090
|
});
|
|
1901
2091
|
|
|
@@ -2392,6 +2582,120 @@ var init_rose_pine = __esm({
|
|
|
2392
2582
|
}
|
|
2393
2583
|
});
|
|
2394
2584
|
|
|
2585
|
+
// src/palettes/slate.ts
|
|
2586
|
+
var slatePalette;
|
|
2587
|
+
var init_slate = __esm({
|
|
2588
|
+
"src/palettes/slate.ts"() {
|
|
2589
|
+
"use strict";
|
|
2590
|
+
init_registry();
|
|
2591
|
+
slatePalette = {
|
|
2592
|
+
id: "slate",
|
|
2593
|
+
name: "Slate",
|
|
2594
|
+
light: {
|
|
2595
|
+
bg: "#ffffff",
|
|
2596
|
+
// clean slide white
|
|
2597
|
+
surface: "#f3f5f8",
|
|
2598
|
+
// light cool-gray panel
|
|
2599
|
+
overlay: "#eaeef3",
|
|
2600
|
+
// popovers, dropdowns
|
|
2601
|
+
border: "#d4dae1",
|
|
2602
|
+
// hairline rule
|
|
2603
|
+
text: "#1f2933",
|
|
2604
|
+
// near-black slate (softer than pure black)
|
|
2605
|
+
textMuted: "#5b6672",
|
|
2606
|
+
// secondary label
|
|
2607
|
+
textOnFillLight: "#ffffff",
|
|
2608
|
+
// light text on dark fills
|
|
2609
|
+
textOnFillDark: "#1f2933",
|
|
2610
|
+
// dark text on light fills
|
|
2611
|
+
primary: "#3b6ea5",
|
|
2612
|
+
// confident corporate blue
|
|
2613
|
+
secondary: "#5b6672",
|
|
2614
|
+
// slate gray
|
|
2615
|
+
accent: "#3a9188",
|
|
2616
|
+
// muted teal accent
|
|
2617
|
+
destructive: "#c0504d",
|
|
2618
|
+
// brick red
|
|
2619
|
+
colors: {
|
|
2620
|
+
red: "#c0504d",
|
|
2621
|
+
// brick
|
|
2622
|
+
orange: "#cc7a33",
|
|
2623
|
+
// muted amber
|
|
2624
|
+
yellow: "#c9a227",
|
|
2625
|
+
// gold (not neon)
|
|
2626
|
+
green: "#5b9357",
|
|
2627
|
+
// forest / sage
|
|
2628
|
+
blue: "#3b6ea5",
|
|
2629
|
+
// corporate blue
|
|
2630
|
+
purple: "#7d5ba6",
|
|
2631
|
+
// muted violet
|
|
2632
|
+
teal: "#3a9188",
|
|
2633
|
+
// teal
|
|
2634
|
+
cyan: "#4f96c4",
|
|
2635
|
+
// steel cyan
|
|
2636
|
+
gray: "#7e8a97",
|
|
2637
|
+
// cool gray
|
|
2638
|
+
black: "#1f2933",
|
|
2639
|
+
// slate ink
|
|
2640
|
+
white: "#f3f5f8"
|
|
2641
|
+
// panel
|
|
2642
|
+
}
|
|
2643
|
+
},
|
|
2644
|
+
dark: {
|
|
2645
|
+
bg: "#161b22",
|
|
2646
|
+
// deep slate (keynote dark)
|
|
2647
|
+
surface: "#202833",
|
|
2648
|
+
// raised panel
|
|
2649
|
+
overlay: "#29323e",
|
|
2650
|
+
// popovers, dropdowns
|
|
2651
|
+
border: "#38424f",
|
|
2652
|
+
// divider
|
|
2653
|
+
text: "#e6eaef",
|
|
2654
|
+
// off-white
|
|
2655
|
+
textMuted: "#9aa5b1",
|
|
2656
|
+
// secondary label
|
|
2657
|
+
textOnFillLight: "#ffffff",
|
|
2658
|
+
// light text on dark fills
|
|
2659
|
+
textOnFillDark: "#161b22",
|
|
2660
|
+
// dark text on light fills
|
|
2661
|
+
primary: "#5b9bd5",
|
|
2662
|
+
// lifted corporate blue
|
|
2663
|
+
secondary: "#8593a3",
|
|
2664
|
+
// slate gray, lifted
|
|
2665
|
+
accent: "#45b3a3",
|
|
2666
|
+
// teal, lifted
|
|
2667
|
+
destructive: "#e07b6e",
|
|
2668
|
+
// brick, lifted
|
|
2669
|
+
colors: {
|
|
2670
|
+
red: "#e07b6e",
|
|
2671
|
+
// brick
|
|
2672
|
+
orange: "#e0975a",
|
|
2673
|
+
// amber
|
|
2674
|
+
yellow: "#d9bd5a",
|
|
2675
|
+
// gold
|
|
2676
|
+
green: "#74b56e",
|
|
2677
|
+
// forest / sage
|
|
2678
|
+
blue: "#5b9bd5",
|
|
2679
|
+
// corporate blue
|
|
2680
|
+
purple: "#a585c9",
|
|
2681
|
+
// violet
|
|
2682
|
+
teal: "#45b3a3",
|
|
2683
|
+
// teal
|
|
2684
|
+
cyan: "#62b0d9",
|
|
2685
|
+
// steel cyan
|
|
2686
|
+
gray: "#95a1ae",
|
|
2687
|
+
// cool gray
|
|
2688
|
+
black: "#202833",
|
|
2689
|
+
// raised panel
|
|
2690
|
+
white: "#e6eaef"
|
|
2691
|
+
// off-white
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
registerPalette(slatePalette);
|
|
2696
|
+
}
|
|
2697
|
+
});
|
|
2698
|
+
|
|
2395
2699
|
// src/palettes/solarized.ts
|
|
2396
2700
|
var solarizedPalette;
|
|
2397
2701
|
var init_solarized = __esm({
|
|
@@ -2487,6 +2791,120 @@ var init_solarized = __esm({
|
|
|
2487
2791
|
}
|
|
2488
2792
|
});
|
|
2489
2793
|
|
|
2794
|
+
// src/palettes/tidewater.ts
|
|
2795
|
+
var tidewaterPalette;
|
|
2796
|
+
var init_tidewater = __esm({
|
|
2797
|
+
"src/palettes/tidewater.ts"() {
|
|
2798
|
+
"use strict";
|
|
2799
|
+
init_registry();
|
|
2800
|
+
tidewaterPalette = {
|
|
2801
|
+
id: "tidewater",
|
|
2802
|
+
name: "Tidewater",
|
|
2803
|
+
light: {
|
|
2804
|
+
bg: "#eceff0",
|
|
2805
|
+
// weathered sea-mist paper
|
|
2806
|
+
surface: "#e0e4e3",
|
|
2807
|
+
// worn deck panel
|
|
2808
|
+
overlay: "#dadfdf",
|
|
2809
|
+
// popovers, dropdowns
|
|
2810
|
+
border: "#a9b2b3",
|
|
2811
|
+
// muted slate rule
|
|
2812
|
+
text: "#18313f",
|
|
2813
|
+
// ship's-log navy ink
|
|
2814
|
+
textMuted: "#51636b",
|
|
2815
|
+
// faded log entry
|
|
2816
|
+
textOnFillLight: "#f3f5f3",
|
|
2817
|
+
// weathered white
|
|
2818
|
+
textOnFillDark: "#162c38",
|
|
2819
|
+
// deep navy
|
|
2820
|
+
primary: "#1f4e6b",
|
|
2821
|
+
// deep-sea navy
|
|
2822
|
+
secondary: "#b08a4f",
|
|
2823
|
+
// rope / manila tan
|
|
2824
|
+
accent: "#c69a3e",
|
|
2825
|
+
// brass
|
|
2826
|
+
destructive: "#c1433a",
|
|
2827
|
+
// signal-flag red
|
|
2828
|
+
colors: {
|
|
2829
|
+
red: "#c1433a",
|
|
2830
|
+
// signal-flag red
|
|
2831
|
+
orange: "#cc7a38",
|
|
2832
|
+
// weathered amber
|
|
2833
|
+
yellow: "#d6bf5a",
|
|
2834
|
+
// brass gold
|
|
2835
|
+
green: "#4f8a6b",
|
|
2836
|
+
// sea-glass green
|
|
2837
|
+
blue: "#1f4e6b",
|
|
2838
|
+
// deep-sea navy
|
|
2839
|
+
purple: "#6a5a8c",
|
|
2840
|
+
// twilight harbor
|
|
2841
|
+
teal: "#3d8c8c",
|
|
2842
|
+
// sea-glass teal
|
|
2843
|
+
cyan: "#4f9bb5",
|
|
2844
|
+
// shallow water
|
|
2845
|
+
gray: "#8a8d86",
|
|
2846
|
+
// driftwood gray
|
|
2847
|
+
black: "#18313f",
|
|
2848
|
+
// navy ink
|
|
2849
|
+
white: "#e0e4e3"
|
|
2850
|
+
// deck panel
|
|
2851
|
+
}
|
|
2852
|
+
},
|
|
2853
|
+
dark: {
|
|
2854
|
+
bg: "#0f2230",
|
|
2855
|
+
// night-harbor deep sea
|
|
2856
|
+
surface: "#16303f",
|
|
2857
|
+
// raised hull
|
|
2858
|
+
overlay: "#1d3a4a",
|
|
2859
|
+
// popovers, dropdowns
|
|
2860
|
+
border: "#2c4856",
|
|
2861
|
+
// rigging line
|
|
2862
|
+
text: "#e6ebe8",
|
|
2863
|
+
// weathered white
|
|
2864
|
+
textMuted: "#9aaab0",
|
|
2865
|
+
// faded label
|
|
2866
|
+
textOnFillLight: "#f3f5f3",
|
|
2867
|
+
// weathered white
|
|
2868
|
+
textOnFillDark: "#0f2230",
|
|
2869
|
+
// deep sea
|
|
2870
|
+
primary: "#4f9bc4",
|
|
2871
|
+
// lifted sea blue
|
|
2872
|
+
secondary: "#c9a46a",
|
|
2873
|
+
// rope tan, lifted
|
|
2874
|
+
accent: "#d9b25a",
|
|
2875
|
+
// brass, lifted
|
|
2876
|
+
destructive: "#e06a5e",
|
|
2877
|
+
// signal red, lifted
|
|
2878
|
+
colors: {
|
|
2879
|
+
red: "#e06a5e",
|
|
2880
|
+
// signal-flag red
|
|
2881
|
+
orange: "#df9a52",
|
|
2882
|
+
// amber
|
|
2883
|
+
yellow: "#e0c662",
|
|
2884
|
+
// brass gold
|
|
2885
|
+
green: "#6fb58c",
|
|
2886
|
+
// sea-glass green
|
|
2887
|
+
blue: "#4f9bc4",
|
|
2888
|
+
// sea blue
|
|
2889
|
+
purple: "#9486bf",
|
|
2890
|
+
// twilight harbor
|
|
2891
|
+
teal: "#5cb0ac",
|
|
2892
|
+
// sea-glass teal
|
|
2893
|
+
cyan: "#62b4cf",
|
|
2894
|
+
// shallow water
|
|
2895
|
+
gray: "#9aa39c",
|
|
2896
|
+
// driftwood gray
|
|
2897
|
+
black: "#16303f",
|
|
2898
|
+
// raised hull
|
|
2899
|
+
white: "#e6ebe8"
|
|
2900
|
+
// weathered white
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
registerPalette(tidewaterPalette);
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2490
2908
|
// src/palettes/tokyo-night.ts
|
|
2491
2909
|
var tokyoNightPalette;
|
|
2492
2910
|
var init_tokyo_night = __esm({
|
|
@@ -2762,7 +3180,8 @@ var init_monokai = __esm({
|
|
|
2762
3180
|
// src/palettes/index.ts
|
|
2763
3181
|
var palettes_exports = {};
|
|
2764
3182
|
__export(palettes_exports, {
|
|
2765
|
-
|
|
3183
|
+
atlasPalette: () => atlasPalette,
|
|
3184
|
+
blueprintPalette: () => blueprintPalette,
|
|
2766
3185
|
catppuccinPalette: () => catppuccinPalette,
|
|
2767
3186
|
contrastText: () => contrastText,
|
|
2768
3187
|
draculaPalette: () => draculaPalette,
|
|
@@ -2783,7 +3202,9 @@ __export(palettes_exports, {
|
|
|
2783
3202
|
rosePinePalette: () => rosePinePalette,
|
|
2784
3203
|
shade: () => shade,
|
|
2785
3204
|
shapeFill: () => shapeFill,
|
|
3205
|
+
slatePalette: () => slatePalette,
|
|
2786
3206
|
solarizedPalette: () => solarizedPalette,
|
|
3207
|
+
tidewaterPalette: () => tidewaterPalette,
|
|
2787
3208
|
tint: () => tint,
|
|
2788
3209
|
tokyoNightPalette: () => tokyoNightPalette
|
|
2789
3210
|
});
|
|
@@ -2793,17 +3214,21 @@ var init_palettes = __esm({
|
|
|
2793
3214
|
"use strict";
|
|
2794
3215
|
init_registry();
|
|
2795
3216
|
init_color_utils();
|
|
2796
|
-
|
|
3217
|
+
init_atlas();
|
|
3218
|
+
init_blueprint();
|
|
2797
3219
|
init_catppuccin();
|
|
2798
3220
|
init_gruvbox();
|
|
2799
3221
|
init_nord();
|
|
2800
3222
|
init_one_dark();
|
|
2801
3223
|
init_rose_pine();
|
|
3224
|
+
init_slate();
|
|
2802
3225
|
init_solarized();
|
|
3226
|
+
init_tidewater();
|
|
2803
3227
|
init_tokyo_night();
|
|
2804
3228
|
init_dracula();
|
|
2805
3229
|
init_monokai();
|
|
2806
|
-
|
|
3230
|
+
init_atlas();
|
|
3231
|
+
init_blueprint();
|
|
2807
3232
|
init_catppuccin();
|
|
2808
3233
|
init_dracula();
|
|
2809
3234
|
init_gruvbox();
|
|
@@ -2811,9 +3236,15 @@ var init_palettes = __esm({
|
|
|
2811
3236
|
init_nord();
|
|
2812
3237
|
init_one_dark();
|
|
2813
3238
|
init_rose_pine();
|
|
3239
|
+
init_slate();
|
|
2814
3240
|
init_solarized();
|
|
3241
|
+
init_tidewater();
|
|
2815
3242
|
init_tokyo_night();
|
|
2816
3243
|
palettes = {
|
|
3244
|
+
atlas: atlasPalette,
|
|
3245
|
+
blueprint: blueprintPalette,
|
|
3246
|
+
slate: slatePalette,
|
|
3247
|
+
tidewater: tidewaterPalette,
|
|
2817
3248
|
nord: nordPalette,
|
|
2818
3249
|
catppuccin: catppuccinPalette,
|
|
2819
3250
|
solarized: solarizedPalette,
|
|
@@ -2822,8 +3253,7 @@ var init_palettes = __esm({
|
|
|
2822
3253
|
oneDark: oneDarkPalette,
|
|
2823
3254
|
rosePine: rosePinePalette,
|
|
2824
3255
|
dracula: draculaPalette,
|
|
2825
|
-
monokai: monokaiPalette
|
|
2826
|
-
bold: boldPalette
|
|
3256
|
+
monokai: monokaiPalette
|
|
2827
3257
|
};
|
|
2828
3258
|
}
|
|
2829
3259
|
});
|
|
@@ -3333,6 +3763,9 @@ function controlsGroupCapsuleWidth(toggles) {
|
|
|
3333
3763
|
}
|
|
3334
3764
|
return w;
|
|
3335
3765
|
}
|
|
3766
|
+
function isAppHostedControls(config, isExport) {
|
|
3767
|
+
return !isExport && config.controlsHost === "app" && !!config.controlsGroup && config.controlsGroup.toggles.length > 0;
|
|
3768
|
+
}
|
|
3336
3769
|
function buildControlsGroupLayout(config, state) {
|
|
3337
3770
|
const cg = config.controlsGroup;
|
|
3338
3771
|
if (!cg || cg.toggles.length === 0) return void 0;
|
|
@@ -3386,6 +3819,7 @@ function buildControlsGroupLayout(config, state) {
|
|
|
3386
3819
|
function computeLegendLayout(config, state, containerWidth) {
|
|
3387
3820
|
const { groups, controls: configControls, mode } = config;
|
|
3388
3821
|
const isExport = mode === "export";
|
|
3822
|
+
const gated = isAppHostedControls(config, isExport);
|
|
3389
3823
|
const activeGroupName = state.activeGroup?.toLowerCase() ?? null;
|
|
3390
3824
|
if (isExport && !activeGroupName) {
|
|
3391
3825
|
return {
|
|
@@ -3396,7 +3830,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3396
3830
|
pills: []
|
|
3397
3831
|
};
|
|
3398
3832
|
}
|
|
3399
|
-
const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
|
|
3833
|
+
const controlsGroupLayout = isExport || gated ? void 0 : buildControlsGroupLayout(config, state);
|
|
3400
3834
|
const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
|
|
3401
3835
|
if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
|
|
3402
3836
|
return {
|
|
@@ -8276,8 +8710,8 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8276
8710
|
const pt = points[i];
|
|
8277
8711
|
const ptSize = pt.size ?? symbolSize;
|
|
8278
8712
|
const minGap = ptSize / 2 + 4;
|
|
8279
|
-
const
|
|
8280
|
-
const labelX = pt.px -
|
|
8713
|
+
const labelWidth2 = pt.name.length * fontSize * 0.6 + 8;
|
|
8714
|
+
const labelX = pt.px - labelWidth2 / 2;
|
|
8281
8715
|
let bestLabelY = 0;
|
|
8282
8716
|
let bestOffset = Infinity;
|
|
8283
8717
|
let placed = false;
|
|
@@ -8289,7 +8723,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8289
8723
|
const candidate = {
|
|
8290
8724
|
x: labelX,
|
|
8291
8725
|
y: labelY,
|
|
8292
|
-
w:
|
|
8726
|
+
w: labelWidth2,
|
|
8293
8727
|
h: labelHeight
|
|
8294
8728
|
};
|
|
8295
8729
|
let collision = false;
|
|
@@ -8331,7 +8765,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8331
8765
|
const labelRect = {
|
|
8332
8766
|
x: labelX,
|
|
8333
8767
|
y: bestLabelY,
|
|
8334
|
-
w:
|
|
8768
|
+
w: labelWidth2,
|
|
8335
8769
|
h: labelHeight
|
|
8336
8770
|
};
|
|
8337
8771
|
placedLabels.push(labelRect);
|
|
@@ -8367,7 +8801,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8367
8801
|
shape: {
|
|
8368
8802
|
x: labelX - bgPad,
|
|
8369
8803
|
y: bestLabelY - bgPad,
|
|
8370
|
-
width:
|
|
8804
|
+
width: labelWidth2 + bgPad * 2,
|
|
8371
8805
|
height: labelHeight + bgPad * 2
|
|
8372
8806
|
},
|
|
8373
8807
|
style: { fill: bg },
|
|
@@ -15807,10 +16241,6 @@ function parseMap(content) {
|
|
|
15807
16241
|
handleTag(trimmed, lineNumber);
|
|
15808
16242
|
continue;
|
|
15809
16243
|
}
|
|
15810
|
-
if ((firstWord === "muted" || firstWord === "natural") && trimmed === firstWord) {
|
|
15811
|
-
handleDirective(firstWord, "", lineNumber);
|
|
15812
|
-
continue;
|
|
15813
|
-
}
|
|
15814
16244
|
if (DIRECTIVE_SET.has(firstWord) && !trimmed.slice(firstWord.length).trimStart().startsWith(":")) {
|
|
15815
16245
|
handleDirective(
|
|
15816
16246
|
firstWord,
|
|
@@ -15857,24 +16287,6 @@ function parseMap(content) {
|
|
|
15857
16287
|
pushWarning(line12, `Duplicate directive "${key}" \u2014 last value wins.`);
|
|
15858
16288
|
};
|
|
15859
16289
|
switch (key) {
|
|
15860
|
-
case "region":
|
|
15861
|
-
dup(d.region);
|
|
15862
|
-
d.region = value;
|
|
15863
|
-
break;
|
|
15864
|
-
case "projection":
|
|
15865
|
-
dup(d.projection);
|
|
15866
|
-
if (value && ![
|
|
15867
|
-
"equirectangular",
|
|
15868
|
-
"natural-earth",
|
|
15869
|
-
"albers-usa",
|
|
15870
|
-
"mercator"
|
|
15871
|
-
].includes(value))
|
|
15872
|
-
pushWarning(
|
|
15873
|
-
line12,
|
|
15874
|
-
`Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
|
|
15875
|
-
);
|
|
15876
|
-
d.projection = value;
|
|
15877
|
-
break;
|
|
15878
16290
|
case "region-metric": {
|
|
15879
16291
|
dup(d.regionMetric);
|
|
15880
16292
|
const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
|
|
@@ -15890,91 +16302,43 @@ function parseMap(content) {
|
|
|
15890
16302
|
dup(d.flowMetric);
|
|
15891
16303
|
d.flowMetric = value;
|
|
15892
16304
|
break;
|
|
15893
|
-
case "
|
|
15894
|
-
dup(d.
|
|
15895
|
-
|
|
15896
|
-
const s = parseScale(value, line12);
|
|
15897
|
-
if (s) d.scale = s;
|
|
15898
|
-
}
|
|
15899
|
-
break;
|
|
15900
|
-
case "region-labels":
|
|
15901
|
-
dup(d.regionLabels);
|
|
15902
|
-
if (value && !["full", "abbrev", "off"].includes(value))
|
|
15903
|
-
pushWarning(
|
|
15904
|
-
line12,
|
|
15905
|
-
`Unknown region-labels "${value}" (expected full | abbrev | off).`
|
|
15906
|
-
);
|
|
15907
|
-
d.regionLabels = value;
|
|
15908
|
-
break;
|
|
15909
|
-
case "poi-labels":
|
|
15910
|
-
dup(d.poiLabels);
|
|
15911
|
-
if (value && !["off", "auto", "all"].includes(value))
|
|
15912
|
-
pushWarning(
|
|
15913
|
-
line12,
|
|
15914
|
-
`Unknown poi-labels "${value}" (expected off | auto | all).`
|
|
15915
|
-
);
|
|
15916
|
-
d.poiLabels = value;
|
|
15917
|
-
break;
|
|
15918
|
-
case "default-country":
|
|
15919
|
-
dup(d.defaultCountry);
|
|
15920
|
-
d.defaultCountry = value;
|
|
15921
|
-
break;
|
|
15922
|
-
case "default-state":
|
|
15923
|
-
dup(d.defaultState);
|
|
15924
|
-
d.defaultState = value;
|
|
16305
|
+
case "locale":
|
|
16306
|
+
dup(d.locale);
|
|
16307
|
+
d.locale = value;
|
|
15925
16308
|
break;
|
|
15926
16309
|
case "active-tag":
|
|
15927
16310
|
dup(d.activeTag);
|
|
15928
16311
|
d.activeTag = value;
|
|
15929
16312
|
break;
|
|
16313
|
+
case "caption":
|
|
16314
|
+
dup(d.caption);
|
|
16315
|
+
d.caption = value;
|
|
16316
|
+
break;
|
|
16317
|
+
// ── Cosmetic `no-*` opt-outs: bare flags, idempotent (mirror `no-legend`,
|
|
16318
|
+
// no dup warning); each defaults the feature ON when absent. ──
|
|
15930
16319
|
case "no-legend":
|
|
15931
16320
|
d.noLegend = true;
|
|
15932
16321
|
break;
|
|
15933
|
-
case "no-
|
|
15934
|
-
d.
|
|
16322
|
+
case "no-coastline":
|
|
16323
|
+
d.noCoastline = true;
|
|
15935
16324
|
break;
|
|
15936
|
-
case "relief":
|
|
15937
|
-
d.
|
|
16325
|
+
case "no-relief":
|
|
16326
|
+
d.noRelief = true;
|
|
15938
16327
|
break;
|
|
15939
|
-
case "
|
|
15940
|
-
|
|
15941
|
-
if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
|
|
15942
|
-
pushWarning(
|
|
15943
|
-
line12,
|
|
15944
|
-
`Conflicting basemap dress \u2014 "${d.basemapStyle}" then "${key}"; last wins.`
|
|
15945
|
-
);
|
|
15946
|
-
d.basemapStyle = key;
|
|
16328
|
+
case "no-context-labels":
|
|
16329
|
+
d.noContextLabels = true;
|
|
15947
16330
|
break;
|
|
15948
|
-
case "
|
|
15949
|
-
|
|
15950
|
-
d.subtitle = value;
|
|
16331
|
+
case "no-region-labels":
|
|
16332
|
+
d.noRegionLabels = true;
|
|
15951
16333
|
break;
|
|
15952
|
-
case "
|
|
15953
|
-
|
|
15954
|
-
|
|
16334
|
+
case "no-poi-labels":
|
|
16335
|
+
d.noPoiLabels = true;
|
|
16336
|
+
break;
|
|
16337
|
+
case "no-colorize":
|
|
16338
|
+
d.noColorize = true;
|
|
15955
16339
|
break;
|
|
15956
16340
|
}
|
|
15957
16341
|
}
|
|
15958
|
-
function parseScale(value, line12) {
|
|
15959
|
-
const toks = value.split(/\s+/).filter(Boolean);
|
|
15960
|
-
const min = Number(toks[0]);
|
|
15961
|
-
const max = Number(toks[1]);
|
|
15962
|
-
if (!Number.isFinite(min) || !Number.isFinite(max)) {
|
|
15963
|
-
pushError(line12, `scale requires numeric <min> <max> (got "${value}").`);
|
|
15964
|
-
return null;
|
|
15965
|
-
}
|
|
15966
|
-
const scale = { min, max };
|
|
15967
|
-
if (toks[2] === "center") {
|
|
15968
|
-
const c = Number(toks[3]);
|
|
15969
|
-
if (Number.isFinite(c)) scale.center = c;
|
|
15970
|
-
else
|
|
15971
|
-
pushError(
|
|
15972
|
-
line12,
|
|
15973
|
-
`scale center requires a number (got "${toks[3] ?? ""}").`
|
|
15974
|
-
);
|
|
15975
|
-
}
|
|
15976
|
-
return scale;
|
|
15977
|
-
}
|
|
15978
16342
|
function handleTag(trimmed, line12) {
|
|
15979
16343
|
const m = matchTagBlockHeading(trimmed);
|
|
15980
16344
|
if (!m) {
|
|
@@ -16174,13 +16538,15 @@ function parseMap(content) {
|
|
|
16174
16538
|
pushError(line12, `Edge has an empty endpoint: "${trimmed}".`);
|
|
16175
16539
|
continue;
|
|
16176
16540
|
}
|
|
16177
|
-
const
|
|
16541
|
+
const isLast = k === links.length - 1;
|
|
16542
|
+
const meta = isLast ? lastSplit.meta : {};
|
|
16543
|
+
const style = links[k].style === "arc" ? "arc" : "straight";
|
|
16178
16544
|
edges.push({
|
|
16179
16545
|
from,
|
|
16180
16546
|
to,
|
|
16181
16547
|
...links[k].label !== void 0 && { label: links[k].label },
|
|
16182
16548
|
directed: links[k].directed,
|
|
16183
|
-
style
|
|
16549
|
+
style,
|
|
16184
16550
|
meta,
|
|
16185
16551
|
lineNumber: line12
|
|
16186
16552
|
});
|
|
@@ -16266,22 +16632,19 @@ var init_parser12 = __esm({
|
|
|
16266
16632
|
LEG_ARROW_RE = /^(-[^>]*?->|->|~[^>]*?~>|~>|--)\s+(.+)$/;
|
|
16267
16633
|
AT_RE = /(^|[\s,])at\s*:/i;
|
|
16268
16634
|
DIRECTIVE_SET = /* @__PURE__ */ new Set([
|
|
16269
|
-
"region",
|
|
16270
|
-
"projection",
|
|
16271
16635
|
"region-metric",
|
|
16272
16636
|
"poi-metric",
|
|
16273
16637
|
"flow-metric",
|
|
16274
|
-
"
|
|
16275
|
-
"region-labels",
|
|
16276
|
-
"poi-labels",
|
|
16277
|
-
"default-country",
|
|
16278
|
-
"default-state",
|
|
16638
|
+
"locale",
|
|
16279
16639
|
"active-tag",
|
|
16640
|
+
"caption",
|
|
16280
16641
|
"no-legend",
|
|
16281
|
-
"no-
|
|
16282
|
-
"relief",
|
|
16283
|
-
"
|
|
16284
|
-
"
|
|
16642
|
+
"no-coastline",
|
|
16643
|
+
"no-relief",
|
|
16644
|
+
"no-context-labels",
|
|
16645
|
+
"no-region-labels",
|
|
16646
|
+
"no-poi-labels",
|
|
16647
|
+
"no-colorize"
|
|
16285
16648
|
]);
|
|
16286
16649
|
}
|
|
16287
16650
|
});
|
|
@@ -16459,6 +16822,21 @@ function parseBoxesAndLines(content) {
|
|
|
16459
16822
|
}
|
|
16460
16823
|
continue;
|
|
16461
16824
|
}
|
|
16825
|
+
if (!contentStarted) {
|
|
16826
|
+
const metricMatch = trimmed.match(/^box-metric\s+(.+)$/i);
|
|
16827
|
+
if (metricMatch) {
|
|
16828
|
+
const { label, colorName } = peelTrailingColorName(
|
|
16829
|
+
metricMatch[1].trim()
|
|
16830
|
+
);
|
|
16831
|
+
result.boxMetric = label;
|
|
16832
|
+
if (colorName !== void 0) result.boxMetricColor = colorName;
|
|
16833
|
+
continue;
|
|
16834
|
+
}
|
|
16835
|
+
if (/^show-values$/i.test(trimmed)) {
|
|
16836
|
+
result.showValues = true;
|
|
16837
|
+
continue;
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16462
16840
|
if (!contentStarted) {
|
|
16463
16841
|
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
16464
16842
|
if (optMatch) {
|
|
@@ -16837,6 +17215,19 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16837
17215
|
description = [metadata["description"]];
|
|
16838
17216
|
delete metadata["description"];
|
|
16839
17217
|
}
|
|
17218
|
+
let value;
|
|
17219
|
+
if (metadata["value"] !== void 0) {
|
|
17220
|
+
const raw = metadata["value"];
|
|
17221
|
+
const num = Number(raw);
|
|
17222
|
+
if (Number.isFinite(num)) {
|
|
17223
|
+
value = num;
|
|
17224
|
+
} else {
|
|
17225
|
+
diagnostics.push(
|
|
17226
|
+
makeDgmoError(lineNum, `value must be a number (got "${raw}")`, "error")
|
|
17227
|
+
);
|
|
17228
|
+
}
|
|
17229
|
+
delete metadata["value"];
|
|
17230
|
+
}
|
|
16840
17231
|
if (split.alias) {
|
|
16841
17232
|
nameAliasMap?.set(normalizeName(split.alias), label);
|
|
16842
17233
|
}
|
|
@@ -16845,7 +17236,8 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16845
17236
|
label,
|
|
16846
17237
|
lineNumber: lineNum,
|
|
16847
17238
|
metadata,
|
|
16848
|
-
...description !== void 0 && { description }
|
|
17239
|
+
...description !== void 0 && { description },
|
|
17240
|
+
...value !== void 0 && { value }
|
|
16849
17241
|
};
|
|
16850
17242
|
}
|
|
16851
17243
|
function splitTargetAndMeta(rest, metaAliasMap) {
|
|
@@ -24202,8 +24594,8 @@ function renderKanban(container, parsed, palette, isDark, options) {
|
|
|
24202
24594
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24203
24595
|
for (const meta of tagMeta) {
|
|
24204
24596
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(`${meta.label}: `);
|
|
24205
|
-
const
|
|
24206
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
24597
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
24598
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24207
24599
|
metaY += sCardMetaLineHeight;
|
|
24208
24600
|
}
|
|
24209
24601
|
for (const detail of card.details) {
|
|
@@ -24547,8 +24939,8 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
|
|
|
24547
24939
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24548
24940
|
for (const meta of tagMeta) {
|
|
24549
24941
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", palette.textMuted).text(`${meta.label}: `);
|
|
24550
|
-
const
|
|
24551
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
24942
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
24943
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24552
24944
|
metaY += sCardMetaLineHeight;
|
|
24553
24945
|
}
|
|
24554
24946
|
for (const detail of card.details) {
|
|
@@ -25383,8 +25775,8 @@ function classifyEREntities(tables, relationships) {
|
|
|
25383
25775
|
}
|
|
25384
25776
|
}
|
|
25385
25777
|
const mmParticipants = /* @__PURE__ */ new Set();
|
|
25386
|
-
for (const [id,
|
|
25387
|
-
if (
|
|
25778
|
+
for (const [id, neighbors2] of tableStarNeighbors) {
|
|
25779
|
+
if (neighbors2.size >= 2) mmParticipants.add(id);
|
|
25388
25780
|
}
|
|
25389
25781
|
const indegreeValues = Object.values(indegreeMap);
|
|
25390
25782
|
const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
|
|
@@ -25965,7 +26357,18 @@ function fitLabelToHeader(label, nodeWidth, maxLines) {
|
|
|
25965
26357
|
const truncated = label.length > maxChars ? label.slice(0, maxChars - 1) + "\u2026" : label;
|
|
25966
26358
|
return { lines: [truncated], fontSize: MIN_NODE_FONT_SIZE };
|
|
25967
26359
|
}
|
|
25968
|
-
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, solid) {
|
|
26360
|
+
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, value, solid) {
|
|
26361
|
+
const neutralFill = mix(palette.bg, palette.text, isDark ? 90 : 95);
|
|
26362
|
+
if (value.active) {
|
|
26363
|
+
const fill3 = node.value !== void 0 ? value.fillForValue(node.value) : neutralFill;
|
|
26364
|
+
const stroke3 = value.hue;
|
|
26365
|
+
const text2 = contrastText(
|
|
26366
|
+
fill3,
|
|
26367
|
+
palette.textOnFillLight,
|
|
26368
|
+
palette.textOnFillDark
|
|
26369
|
+
);
|
|
26370
|
+
return { fill: fill3, stroke: stroke3, text: text2 };
|
|
26371
|
+
}
|
|
25969
26372
|
const tagColor = resolveTagColor(
|
|
25970
26373
|
node.metadata,
|
|
25971
26374
|
[...tagGroups],
|
|
@@ -26055,7 +26458,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26055
26458
|
controlsExpanded,
|
|
26056
26459
|
onToggleDescriptions,
|
|
26057
26460
|
onToggleControlsExpand,
|
|
26058
|
-
exportMode = false
|
|
26461
|
+
exportMode = false,
|
|
26462
|
+
controlsHost
|
|
26059
26463
|
} = options ?? {};
|
|
26060
26464
|
d3Selection6.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
26061
26465
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -26073,21 +26477,65 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26073
26477
|
const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
|
|
26074
26478
|
const sTitleFontSize = sctx.text(TITLE_FONT_SIZE);
|
|
26075
26479
|
const sTitleY = sctx.structural(TITLE_Y);
|
|
26076
|
-
const
|
|
26480
|
+
const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
|
|
26481
|
+
const hasRamp = nodeValues.length > 0;
|
|
26482
|
+
const allNonNegative = hasRamp && nodeValues.every((v) => v >= 0);
|
|
26483
|
+
const rampMin = allNonNegative ? 0 : Math.min(...nodeValues);
|
|
26484
|
+
const rampMax = Math.max(...nodeValues);
|
|
26485
|
+
const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
|
|
26486
|
+
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
26487
|
+
const fillForValue = (v) => {
|
|
26488
|
+
const t = rampMax > rampMin ? (v - rampMin) / (rampMax - rampMin) : 1;
|
|
26489
|
+
const pct = RAMP_FLOOR + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR);
|
|
26490
|
+
return mix(rampHue, rampBase, pct);
|
|
26491
|
+
};
|
|
26492
|
+
const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
|
|
26493
|
+
const matchColorGroup = (v) => {
|
|
26494
|
+
const lv = v.trim().toLowerCase();
|
|
26495
|
+
if (lv === "none") return null;
|
|
26496
|
+
const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
|
|
26497
|
+
if (tg) return tg.name;
|
|
26498
|
+
if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
|
|
26499
|
+
return v;
|
|
26500
|
+
};
|
|
26501
|
+
const override = activeTagGroup;
|
|
26502
|
+
let activeGroup;
|
|
26503
|
+
if (override !== void 0) {
|
|
26504
|
+
activeGroup = override === null ? null : matchColorGroup(override);
|
|
26505
|
+
} else if (parsed.options["active-tag"] !== void 0) {
|
|
26506
|
+
activeGroup = matchColorGroup(parsed.options["active-tag"]);
|
|
26507
|
+
} else {
|
|
26508
|
+
activeGroup = VALUE_NAME ?? (parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null);
|
|
26509
|
+
}
|
|
26510
|
+
const activeIsValue = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
26511
|
+
const valueGroup = VALUE_NAME !== null ? {
|
|
26512
|
+
name: VALUE_NAME,
|
|
26513
|
+
entries: [],
|
|
26514
|
+
gradient: {
|
|
26515
|
+
min: rampMin,
|
|
26516
|
+
max: rampMax,
|
|
26517
|
+
hue: rampHue,
|
|
26518
|
+
base: rampBase
|
|
26519
|
+
}
|
|
26520
|
+
} : null;
|
|
26521
|
+
const legendGroups = [
|
|
26522
|
+
...valueGroup ? [valueGroup] : [],
|
|
26523
|
+
...parsed.tagGroups
|
|
26524
|
+
];
|
|
26525
|
+
const reserveHasDescriptions = parsed.nodes.some(
|
|
26526
|
+
(n) => n.description && n.description.length > 0
|
|
26527
|
+
);
|
|
26528
|
+
const willRenderLegend = legendGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app";
|
|
26529
|
+
const sLegendHeight = willRenderLegend ? sctx.structural(
|
|
26077
26530
|
getMaxLegendReservedHeight(
|
|
26078
26531
|
{
|
|
26079
|
-
groups:
|
|
26532
|
+
groups: legendGroups,
|
|
26080
26533
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26081
26534
|
mode: exportMode ? "export" : "preview"
|
|
26082
26535
|
},
|
|
26083
26536
|
width
|
|
26084
26537
|
)
|
|
26085
|
-
);
|
|
26086
|
-
const activeGroup = resolveActiveTagGroup(
|
|
26087
|
-
parsed.tagGroups,
|
|
26088
|
-
parsed.options["active-tag"],
|
|
26089
|
-
activeTagGroup
|
|
26090
|
-
);
|
|
26538
|
+
) : 0;
|
|
26091
26539
|
const hidden = hiddenTagValues ?? parsed.initialHiddenTagValues;
|
|
26092
26540
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
26093
26541
|
for (const node of parsed.nodes) nodeMap.set(node.label, node);
|
|
@@ -26098,7 +26546,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26098
26546
|
const hasAnyDescriptions = parsed.nodes.some(
|
|
26099
26547
|
(n) => n.description && n.description.length > 0
|
|
26100
26548
|
);
|
|
26101
|
-
const needsLegend =
|
|
26549
|
+
const needsLegend = legendGroups.length > 0 || hasAnyDescriptions && onToggleDescriptions;
|
|
26102
26550
|
const legendH = needsLegend ? sLegendHeight + 8 : 0;
|
|
26103
26551
|
const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
|
|
26104
26552
|
let labelZoneExtension = 0;
|
|
@@ -26304,12 +26752,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26304
26752
|
activeGroup,
|
|
26305
26753
|
palette,
|
|
26306
26754
|
isDark,
|
|
26755
|
+
{ active: activeIsValue, hue: rampHue, fillForValue },
|
|
26307
26756
|
parsed.options["solid-fill"] === "on"
|
|
26308
26757
|
);
|
|
26309
26758
|
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);
|
|
26310
26759
|
for (const [key, val] of Object.entries(node.metadata)) {
|
|
26311
26760
|
nodeG.attr(`data-tag-${key.toLowerCase()}`, val.toLowerCase());
|
|
26312
26761
|
}
|
|
26762
|
+
if (node.value !== void 0) {
|
|
26763
|
+
nodeG.attr("data-value", node.value);
|
|
26764
|
+
}
|
|
26313
26765
|
if (onClickItem) {
|
|
26314
26766
|
nodeG.on("click", (event) => {
|
|
26315
26767
|
const target = event.target;
|
|
@@ -26393,14 +26845,30 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26393
26845
|
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]);
|
|
26394
26846
|
}
|
|
26395
26847
|
}
|
|
26848
|
+
if (parsed.showValues && node.value !== void 0) {
|
|
26849
|
+
const valueText = String(node.value);
|
|
26850
|
+
const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
|
|
26851
|
+
if (descShown) {
|
|
26852
|
+
const padX = 6;
|
|
26853
|
+
const padY = 5;
|
|
26854
|
+
const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
|
|
26855
|
+
const bh = VALUE_FONT_SIZE + 4;
|
|
26856
|
+
const bx = ln.width / 2 - bw - 4;
|
|
26857
|
+
const by = -ln.height / 2 + 4;
|
|
26858
|
+
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);
|
|
26859
|
+
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);
|
|
26860
|
+
} else {
|
|
26861
|
+
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);
|
|
26862
|
+
}
|
|
26863
|
+
}
|
|
26396
26864
|
}
|
|
26397
26865
|
const hasDescriptions = parsed.nodes.some(
|
|
26398
26866
|
(n) => n.description && n.description.length > 0
|
|
26399
26867
|
);
|
|
26400
|
-
const hasLegend =
|
|
26868
|
+
const hasLegend = legendGroups.length > 0 || hasDescriptions && controlsHost !== "app";
|
|
26401
26869
|
if (hasLegend) {
|
|
26402
26870
|
let controlsGroup;
|
|
26403
|
-
if (hasDescriptions && onToggleDescriptions) {
|
|
26871
|
+
if (hasDescriptions && (onToggleDescriptions || controlsHost === "app")) {
|
|
26404
26872
|
controlsGroup = {
|
|
26405
26873
|
toggles: [
|
|
26406
26874
|
{
|
|
@@ -26415,10 +26883,17 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26415
26883
|
};
|
|
26416
26884
|
}
|
|
26417
26885
|
const legendConfig = {
|
|
26418
|
-
groups:
|
|
26886
|
+
groups: legendGroups,
|
|
26419
26887
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26420
26888
|
mode: exportMode ? "export" : "preview",
|
|
26421
|
-
|
|
26889
|
+
// Keep inactive sibling tag groups visible as collapsed pills so the user
|
|
26890
|
+
// can click one to flip the active colouring dimension (preview only —
|
|
26891
|
+
// export shows just the active group). Without this, declaring a second
|
|
26892
|
+
// tag group (e.g. Team) leaves it invisible whenever another group is
|
|
26893
|
+
// active. The app's BoxesAndLinesPreview already wires pill clicks.
|
|
26894
|
+
showInactivePills: true,
|
|
26895
|
+
...controlsGroup !== void 0 && { controlsGroup },
|
|
26896
|
+
...controlsHost !== void 0 && { controlsHost }
|
|
26422
26897
|
};
|
|
26423
26898
|
const legendState = {
|
|
26424
26899
|
activeGroup,
|
|
@@ -26463,7 +26938,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
|
|
|
26463
26938
|
}
|
|
26464
26939
|
});
|
|
26465
26940
|
}
|
|
26466
|
-
var d3Selection6, d3Shape4, 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;
|
|
26941
|
+
var d3Selection6, d3Shape4, 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;
|
|
26467
26942
|
var init_renderer6 = __esm({
|
|
26468
26943
|
"src/boxes-and-lines/renderer.ts"() {
|
|
26469
26944
|
"use strict";
|
|
@@ -26474,6 +26949,7 @@ var init_renderer6 = __esm({
|
|
|
26474
26949
|
init_legend_layout();
|
|
26475
26950
|
init_title_constants();
|
|
26476
26951
|
init_color_utils();
|
|
26952
|
+
init_colors();
|
|
26477
26953
|
init_tag_groups();
|
|
26478
26954
|
init_inline_markdown();
|
|
26479
26955
|
init_wrapped_desc();
|
|
@@ -26496,6 +26972,8 @@ var init_renderer6 = __esm({
|
|
|
26496
26972
|
GROUP_RX = 8;
|
|
26497
26973
|
GROUP_LABEL_FONT_SIZE = 14;
|
|
26498
26974
|
GROUP_LABEL_ZONE = 32;
|
|
26975
|
+
RAMP_FLOOR = 15;
|
|
26976
|
+
VALUE_FONT_SIZE = 11;
|
|
26499
26977
|
lineGeneratorLR = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26500
26978
|
lineGeneratorTB = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26501
26979
|
}
|
|
@@ -27666,8 +28144,9 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27666
28144
|
const containerHeight = exportDims?.height ?? (container.getBoundingClientRect().height || 600);
|
|
27667
28145
|
d3Selection7.select(container).selectAll("*").remove();
|
|
27668
28146
|
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);
|
|
28147
|
+
const appHosted = options?.controlsHost === "app";
|
|
27669
28148
|
const hasControls = !!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
|
|
27670
|
-
const hasLegend = parsed.tagGroups.length > 0 || hasControls;
|
|
28149
|
+
const hasLegend = parsed.tagGroups.length > 0 || hasControls && !appHosted;
|
|
27671
28150
|
const fixedLegend = !isExport && hasLegend;
|
|
27672
28151
|
const legendReserve = fixedLegend ? getMaxLegendReservedHeight(
|
|
27673
28152
|
{
|
|
@@ -27761,7 +28240,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27761
28240
|
}),
|
|
27762
28241
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
27763
28242
|
mode: options?.exportMode ? "export" : "preview",
|
|
27764
|
-
...controlsToggles !== void 0 && { controlsGroup: controlsToggles }
|
|
28243
|
+
...controlsToggles !== void 0 && { controlsGroup: controlsToggles },
|
|
28244
|
+
...options?.controlsHost !== void 0 && {
|
|
28245
|
+
controlsHost: options.controlsHost
|
|
28246
|
+
}
|
|
27765
28247
|
};
|
|
27766
28248
|
const legendState = {
|
|
27767
28249
|
activeGroup: options?.colorByDepth ? null : activeTagGroup !== void 0 ? activeTagGroup : parsed.options["active-tag"] ?? null,
|
|
@@ -28205,8 +28687,8 @@ function computeFieldAlignX(children) {
|
|
|
28205
28687
|
for (const child of children) {
|
|
28206
28688
|
if (child.metadata["_labelField"] === "true" && child.children.length >= 2) {
|
|
28207
28689
|
const labelEl = child.children[0];
|
|
28208
|
-
const
|
|
28209
|
-
maxLabelWidth = Math.max(maxLabelWidth,
|
|
28690
|
+
const labelWidth2 = labelEl.label.length * CHAR_WIDTH5;
|
|
28691
|
+
maxLabelWidth = Math.max(maxLabelWidth, labelWidth2);
|
|
28210
28692
|
labelFieldCount++;
|
|
28211
28693
|
}
|
|
28212
28694
|
}
|
|
@@ -33170,7 +33652,7 @@ function hasRoles(node) {
|
|
|
33170
33652
|
function computeNodeWidth2(node, expanded, options) {
|
|
33171
33653
|
const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
|
|
33172
33654
|
const badgeLen = badgeVal > 0 ? `${badgeVal}x`.length + 2 : 0;
|
|
33173
|
-
const
|
|
33655
|
+
const labelWidth2 = (node.label.length + badgeLen) * CHAR_WIDTH7 + PADDING_X3;
|
|
33174
33656
|
const allKeys = [];
|
|
33175
33657
|
if (node.computedRps > 0) allKeys.push("RPS");
|
|
33176
33658
|
if (expanded) {
|
|
@@ -33214,7 +33696,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33214
33696
|
allKeys.push("overflow");
|
|
33215
33697
|
}
|
|
33216
33698
|
}
|
|
33217
|
-
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2,
|
|
33699
|
+
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
|
|
33218
33700
|
const maxKeyLen = Math.max(...allKeys.map((k) => k.length));
|
|
33219
33701
|
let maxRowWidth = 0;
|
|
33220
33702
|
if (node.computedRps > 0) {
|
|
@@ -33302,7 +33784,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33302
33784
|
truncated.length * META_CHAR_WIDTH3 + PADDING_X3
|
|
33303
33785
|
);
|
|
33304
33786
|
}
|
|
33305
|
-
return Math.max(MIN_NODE_WIDTH2,
|
|
33787
|
+
return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
|
|
33306
33788
|
}
|
|
33307
33789
|
function computeNodeHeight2(node, expanded, options) {
|
|
33308
33790
|
const propCount = countDisplayProps(node, expanded, options);
|
|
@@ -34851,8 +35333,9 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
34851
35333
|
}
|
|
34852
35334
|
return groups;
|
|
34853
35335
|
}
|
|
34854
|
-
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false) {
|
|
35336
|
+
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost) {
|
|
34855
35337
|
if (legendGroups.length === 0 && !playback) return;
|
|
35338
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
34856
35339
|
const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
|
|
34857
35340
|
if (activeGroup) {
|
|
34858
35341
|
legendG.attr("data-legend-active", activeGroup.toLowerCase());
|
|
@@ -34861,14 +35344,29 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
34861
35344
|
name: g.name,
|
|
34862
35345
|
entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
|
|
34863
35346
|
}));
|
|
34864
|
-
if (playback) {
|
|
35347
|
+
if (playback && !appHostedPlayback) {
|
|
34865
35348
|
allGroups.push({ name: "Playback", entries: [] });
|
|
34866
35349
|
}
|
|
34867
35350
|
const legendConfig = {
|
|
34868
35351
|
groups: allGroups,
|
|
34869
35352
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
34870
35353
|
mode: exportMode ? "export" : "preview",
|
|
34871
|
-
showEmptyGroups: true
|
|
35354
|
+
showEmptyGroups: true,
|
|
35355
|
+
...appHostedPlayback && {
|
|
35356
|
+
controlsHost: "app",
|
|
35357
|
+
controlsGroup: {
|
|
35358
|
+
toggles: [
|
|
35359
|
+
{
|
|
35360
|
+
id: "playback",
|
|
35361
|
+
type: "toggle",
|
|
35362
|
+
label: "Playback",
|
|
35363
|
+
active: true,
|
|
35364
|
+
onToggle: () => {
|
|
35365
|
+
}
|
|
35366
|
+
}
|
|
35367
|
+
]
|
|
35368
|
+
}
|
|
35369
|
+
}
|
|
34872
35370
|
};
|
|
34873
35371
|
const legendState = { activeGroup };
|
|
34874
35372
|
renderLegendD3(
|
|
@@ -34919,8 +35417,9 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
34919
35417
|
}
|
|
34920
35418
|
}
|
|
34921
35419
|
}
|
|
34922
|
-
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
35420
|
+
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes, controlsHost) {
|
|
34923
35421
|
d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
35422
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
34924
35423
|
const ctx = ScaleContext.identity();
|
|
34925
35424
|
const sc = buildScaledConstants(ctx);
|
|
34926
35425
|
const legendGroups = computeInfraLegendGroups(
|
|
@@ -34929,7 +35428,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
34929
35428
|
palette,
|
|
34930
35429
|
layout.edges
|
|
34931
35430
|
);
|
|
34932
|
-
const hasLegend = legendGroups.length > 0 || !!playback;
|
|
35431
|
+
const hasLegend = legendGroups.length > 0 || !!playback && !appHostedPlayback;
|
|
34933
35432
|
const fixedLegend = !exportMode && hasLegend;
|
|
34934
35433
|
const legendDynamicH = hasLegend ? getMaxLegendReservedHeight(
|
|
34935
35434
|
{
|
|
@@ -35073,7 +35572,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35073
35572
|
isDark,
|
|
35074
35573
|
activeGroup ?? null,
|
|
35075
35574
|
playback ?? void 0,
|
|
35076
|
-
exportMode
|
|
35575
|
+
exportMode,
|
|
35576
|
+
controlsHost
|
|
35077
35577
|
);
|
|
35078
35578
|
legendSvg.selectAll(".infra-legend-group").style("pointer-events", "auto");
|
|
35079
35579
|
} else {
|
|
@@ -35086,7 +35586,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35086
35586
|
isDark,
|
|
35087
35587
|
activeGroup ?? null,
|
|
35088
35588
|
playback ?? void 0,
|
|
35089
|
-
exportMode
|
|
35589
|
+
exportMode,
|
|
35590
|
+
controlsHost
|
|
35090
35591
|
);
|
|
35091
35592
|
}
|
|
35092
35593
|
}
|
|
@@ -42721,6 +43222,9 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
|
|
|
42721
43222
|
onToggle: (active) => options.onToggleListing(active)
|
|
42722
43223
|
}
|
|
42723
43224
|
]
|
|
43225
|
+
},
|
|
43226
|
+
...options.controlsHost !== void 0 && {
|
|
43227
|
+
controlsHost: options.controlsHost
|
|
42724
43228
|
}
|
|
42725
43229
|
};
|
|
42726
43230
|
const legendState = {
|
|
@@ -44544,7 +45048,7 @@ function computeCycleLayout(parsed, options) {
|
|
|
44544
45048
|
const circleNodes = parsed.options["circle-nodes"] === "true";
|
|
44545
45049
|
const nodeDims = parsed.nodes.map((node) => {
|
|
44546
45050
|
const hasDesc = !hideDescriptions && node.description.length > 0;
|
|
44547
|
-
const
|
|
45051
|
+
const labelWidth2 = Math.max(
|
|
44548
45052
|
MIN_NODE_WIDTH4,
|
|
44549
45053
|
node.label.length * LABEL_CHAR_W + NODE_PAD_X * 2
|
|
44550
45054
|
);
|
|
@@ -44553,12 +45057,12 @@ function computeCycleLayout(parsed, options) {
|
|
|
44553
45057
|
}
|
|
44554
45058
|
if (!hasDesc) {
|
|
44555
45059
|
return {
|
|
44556
|
-
width: Math.min(MAX_NODE_WIDTH3,
|
|
45060
|
+
width: Math.min(MAX_NODE_WIDTH3, labelWidth2),
|
|
44557
45061
|
height: PLAIN_NODE_HEIGHT,
|
|
44558
45062
|
wrappedDesc: []
|
|
44559
45063
|
};
|
|
44560
45064
|
}
|
|
44561
|
-
return chooseDescribedRectDims(node.description,
|
|
45065
|
+
return chooseDescribedRectDims(node.description, labelWidth2);
|
|
44562
45066
|
});
|
|
44563
45067
|
if (circleNodes) {
|
|
44564
45068
|
const maxDiam = Math.max(...nodeDims.map((d) => d.width));
|
|
@@ -44754,10 +45258,10 @@ function computeCycleLayout(parsed, options) {
|
|
|
44754
45258
|
scale
|
|
44755
45259
|
};
|
|
44756
45260
|
}
|
|
44757
|
-
function chooseDescribedRectDims(description,
|
|
45261
|
+
function chooseDescribedRectDims(description, labelWidth2) {
|
|
44758
45262
|
const minW = Math.min(
|
|
44759
45263
|
MAX_NODE_WIDTH3,
|
|
44760
|
-
Math.max(MIN_NODE_WIDTH4,
|
|
45264
|
+
Math.max(MIN_NODE_WIDTH4, labelWidth2, DESC_MIN_WIDTH)
|
|
44761
45265
|
);
|
|
44762
45266
|
let best = null;
|
|
44763
45267
|
let bestScore = Infinity;
|
|
@@ -45185,7 +45689,8 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45185
45689
|
const hideDescriptions = (renderOptions?.hideDescriptions ?? false) || parsed.options["no-descriptions"] === "true" || viewState?.hd === true;
|
|
45186
45690
|
const showDescriptions = !hideDescriptions;
|
|
45187
45691
|
const hasDescriptions = parsed.nodes.some((n) => n.description.length > 0) || parsed.edges.some((e) => e.description.length > 0);
|
|
45188
|
-
const
|
|
45692
|
+
const appHostedControls = renderOptions?.controlsHost === "app";
|
|
45693
|
+
const hasLegend = !appHostedControls && hasDescriptions && !!renderOptions?.onToggleDescriptions;
|
|
45189
45694
|
const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
|
|
45190
45695
|
const legendOffset = hasLegend ? sLegendHeight : 0;
|
|
45191
45696
|
const layoutHeight = height - (showTitle ? sTitleAreaHeight : 0) - legendOffset;
|
|
@@ -45222,7 +45727,10 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45222
45727
|
groups: [],
|
|
45223
45728
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
45224
45729
|
mode: renderOptions?.exportMode ? "export" : "preview",
|
|
45225
|
-
controlsGroup
|
|
45730
|
+
controlsGroup,
|
|
45731
|
+
...renderOptions?.controlsHost !== void 0 && {
|
|
45732
|
+
controlsHost: renderOptions.controlsHost
|
|
45733
|
+
}
|
|
45226
45734
|
};
|
|
45227
45735
|
const legendState = {
|
|
45228
45736
|
activeGroup: null,
|
|
@@ -45493,6 +46001,107 @@ function featureIndex(topo) {
|
|
|
45493
46001
|
}
|
|
45494
46002
|
return idx;
|
|
45495
46003
|
}
|
|
46004
|
+
function buildAdjacency(topo) {
|
|
46005
|
+
const cached = adjacencyCache.get(topo);
|
|
46006
|
+
if (cached) return cached;
|
|
46007
|
+
const geometries = geomObject(topo).geometries;
|
|
46008
|
+
const nb = (0, import_topojson_client.neighbors)(geometries);
|
|
46009
|
+
const sets = /* @__PURE__ */ new Map();
|
|
46010
|
+
geometries.forEach((g, i) => {
|
|
46011
|
+
if (!g.type || g.type === "null") return;
|
|
46012
|
+
let set = sets.get(g.id);
|
|
46013
|
+
if (!set) {
|
|
46014
|
+
set = /* @__PURE__ */ new Set();
|
|
46015
|
+
sets.set(g.id, set);
|
|
46016
|
+
}
|
|
46017
|
+
for (const j of nb[i] ?? []) {
|
|
46018
|
+
const nid = geometries[j]?.id;
|
|
46019
|
+
if (nid && nid !== g.id) set.add(nid);
|
|
46020
|
+
}
|
|
46021
|
+
});
|
|
46022
|
+
const out = /* @__PURE__ */ new Map();
|
|
46023
|
+
for (const [iso, set] of sets) out.set(iso, [...set].sort());
|
|
46024
|
+
adjacencyCache.set(topo, out);
|
|
46025
|
+
return out;
|
|
46026
|
+
}
|
|
46027
|
+
function decodeFeatures(topo) {
|
|
46028
|
+
return geomObject(topo).geometries.map((g) => {
|
|
46029
|
+
const f = (0, import_topojson_client.feature)(topo, g);
|
|
46030
|
+
return {
|
|
46031
|
+
type: "Feature",
|
|
46032
|
+
id: g.id,
|
|
46033
|
+
properties: g.properties,
|
|
46034
|
+
geometry: f.geometry
|
|
46035
|
+
};
|
|
46036
|
+
});
|
|
46037
|
+
}
|
|
46038
|
+
function pointInRing(lon, lat, ring) {
|
|
46039
|
+
let inside = false;
|
|
46040
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46041
|
+
const xi = ring[i][0];
|
|
46042
|
+
const yi = ring[i][1];
|
|
46043
|
+
const xj = ring[j][0];
|
|
46044
|
+
const yj = ring[j][1];
|
|
46045
|
+
const intersect = yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi;
|
|
46046
|
+
if (intersect) inside = !inside;
|
|
46047
|
+
}
|
|
46048
|
+
return inside;
|
|
46049
|
+
}
|
|
46050
|
+
function pointOnRingEdge(lon, lat, ring) {
|
|
46051
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46052
|
+
const xi = ring[i][0];
|
|
46053
|
+
const yi = ring[i][1];
|
|
46054
|
+
const xj = ring[j][0];
|
|
46055
|
+
const yj = ring[j][1];
|
|
46056
|
+
if (lon < Math.min(xi, xj) - EDGE_EPS || lon > Math.max(xi, xj) + EDGE_EPS)
|
|
46057
|
+
continue;
|
|
46058
|
+
if (lat < Math.min(yi, yj) - EDGE_EPS || lat > Math.max(yi, yj) + EDGE_EPS)
|
|
46059
|
+
continue;
|
|
46060
|
+
const cross = (xj - xi) * (lat - yi) - (yj - yi) * (lon - xi);
|
|
46061
|
+
if (Math.abs(cross) <= EDGE_EPS) return true;
|
|
46062
|
+
}
|
|
46063
|
+
return false;
|
|
46064
|
+
}
|
|
46065
|
+
function pointInGeometry(geometry, lon, lat) {
|
|
46066
|
+
const g = geometry;
|
|
46067
|
+
if (!g) return false;
|
|
46068
|
+
const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
|
|
46069
|
+
for (const rings of polys) {
|
|
46070
|
+
if (!rings.length) continue;
|
|
46071
|
+
if (pointOnRingEdge(lon, lat, rings[0])) return true;
|
|
46072
|
+
if (!pointInRing(lon, lat, rings[0])) continue;
|
|
46073
|
+
let inHole = false;
|
|
46074
|
+
for (let h = 1; h < rings.length; h++) {
|
|
46075
|
+
if (pointInRing(lon, lat, rings[h]) && !pointOnRingEdge(lon, lat, rings[h])) {
|
|
46076
|
+
inHole = true;
|
|
46077
|
+
break;
|
|
46078
|
+
}
|
|
46079
|
+
}
|
|
46080
|
+
if (!inHole) return true;
|
|
46081
|
+
}
|
|
46082
|
+
return false;
|
|
46083
|
+
}
|
|
46084
|
+
function regionAt(lonLat, countries, states) {
|
|
46085
|
+
const lon = lonLat[0];
|
|
46086
|
+
const lat = lonLat[1];
|
|
46087
|
+
let country = null;
|
|
46088
|
+
for (const f of countries) {
|
|
46089
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46090
|
+
country = { iso: f.id, name: f.properties.name };
|
|
46091
|
+
break;
|
|
46092
|
+
}
|
|
46093
|
+
}
|
|
46094
|
+
let state = null;
|
|
46095
|
+
if (country?.iso === "US" && states) {
|
|
46096
|
+
for (const f of states) {
|
|
46097
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46098
|
+
state = { iso: f.id, name: f.properties.name };
|
|
46099
|
+
break;
|
|
46100
|
+
}
|
|
46101
|
+
}
|
|
46102
|
+
}
|
|
46103
|
+
return { country, state };
|
|
46104
|
+
}
|
|
45496
46105
|
function featureBbox(topo, geomId) {
|
|
45497
46106
|
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45498
46107
|
if (!geom) return null;
|
|
@@ -45610,13 +46219,15 @@ function unionLongitudes(lons) {
|
|
|
45610
46219
|
}
|
|
45611
46220
|
return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
|
|
45612
46221
|
}
|
|
45613
|
-
var import_topojson_client, import_d3_geo, fold, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
46222
|
+
var import_topojson_client, import_d3_geo, fold, adjacencyCache, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
45614
46223
|
var init_geo = __esm({
|
|
45615
46224
|
"src/map/geo.ts"() {
|
|
45616
46225
|
"use strict";
|
|
45617
46226
|
import_topojson_client = require("topojson-client");
|
|
45618
46227
|
import_d3_geo = require("d3-geo");
|
|
45619
46228
|
fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
46229
|
+
adjacencyCache = /* @__PURE__ */ new WeakMap();
|
|
46230
|
+
EDGE_EPS = 1e-9;
|
|
45620
46231
|
DETACH_GAP_DEG = 10;
|
|
45621
46232
|
DETACH_AREA_FRAC = 0.25;
|
|
45622
46233
|
}
|
|
@@ -45636,6 +46247,12 @@ function looksUS(lat, lon) {
|
|
|
45636
46247
|
if (lat < 15 || lat > 72) return false;
|
|
45637
46248
|
return lon >= -180 && lon <= -64 || lon >= 172;
|
|
45638
46249
|
}
|
|
46250
|
+
function looksNorthAmericaNeighbor(lat, lon) {
|
|
46251
|
+
return lat >= 14 && lat <= 72 && lon >= -141 && lon <= -52;
|
|
46252
|
+
}
|
|
46253
|
+
function isWholeSphere(bb) {
|
|
46254
|
+
return bb[0][0] <= -179 && bb[1][0] >= 179 && bb[0][1] <= -89 && bb[1][1] >= 89;
|
|
46255
|
+
}
|
|
45639
46256
|
function resolveMap(parsed, data) {
|
|
45640
46257
|
const diagnostics = [...parsed.diagnostics];
|
|
45641
46258
|
const err = (line12, message, code) => {
|
|
@@ -45646,9 +46263,6 @@ function resolveMap(parsed, data) {
|
|
|
45646
46263
|
};
|
|
45647
46264
|
const result = {
|
|
45648
46265
|
title: parsed.title,
|
|
45649
|
-
...parsed.directives.subtitle !== void 0 && {
|
|
45650
|
-
subtitle: parsed.directives.subtitle
|
|
45651
|
-
},
|
|
45652
46266
|
...parsed.directives.caption !== void 0 && {
|
|
45653
46267
|
caption: parsed.directives.caption
|
|
45654
46268
|
},
|
|
@@ -45658,7 +46272,7 @@ function resolveMap(parsed, data) {
|
|
|
45658
46272
|
// renderer's job (step 4) — the resolver only carries `tags` + `tagGroups`
|
|
45659
46273
|
// through; it never resolves a tag value to a palette color (#10).
|
|
45660
46274
|
directives: { ...parsed.directives },
|
|
45661
|
-
basemaps: { world: "
|
|
46275
|
+
basemaps: { world: "detail", subdivisions: [] },
|
|
45662
46276
|
regions: [],
|
|
45663
46277
|
pois: [],
|
|
45664
46278
|
edges: [],
|
|
@@ -45667,7 +46281,8 @@ function resolveMap(parsed, data) {
|
|
|
45667
46281
|
[-180, -85],
|
|
45668
46282
|
[180, 85]
|
|
45669
46283
|
],
|
|
45670
|
-
projection: "
|
|
46284
|
+
projection: "equirectangular",
|
|
46285
|
+
poiFrameContainers: [],
|
|
45671
46286
|
diagnostics,
|
|
45672
46287
|
error: parsed.error
|
|
45673
46288
|
};
|
|
@@ -45677,7 +46292,10 @@ function resolveMap(parsed, data) {
|
|
|
45677
46292
|
...[...countryIndex.values()].map((v) => v.name),
|
|
45678
46293
|
...[...usStateIndex.values()].map((v) => v.name)
|
|
45679
46294
|
];
|
|
45680
|
-
const
|
|
46295
|
+
const localeRaw = parsed.directives.locale?.toUpperCase();
|
|
46296
|
+
const localeCountry = localeRaw ? localeRaw.split("-")[0] : void 0;
|
|
46297
|
+
const localeSubdivision = localeRaw && /^[A-Z]{2}-/.test(localeRaw) ? localeRaw : void 0;
|
|
46298
|
+
const usScoped = localeCountry === "US" || parsed.regions.some((r) => {
|
|
45681
46299
|
const f = fold(r.name);
|
|
45682
46300
|
return usStateIndex.has(f) && !countryIndex.has(f);
|
|
45683
46301
|
}) || parsed.regions.some(
|
|
@@ -45828,7 +46446,7 @@ function resolveMap(parsed, data) {
|
|
|
45828
46446
|
if (!scope)
|
|
45829
46447
|
warn2(
|
|
45830
46448
|
line12,
|
|
45831
|
-
`"${name}" is ambiguous \u2014 resolved to the most-populous match.`,
|
|
46449
|
+
`"${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.`,
|
|
45832
46450
|
"W_MAP_AMBIGUOUS_NAME"
|
|
45833
46451
|
);
|
|
45834
46452
|
}
|
|
@@ -45841,17 +46459,21 @@ function resolveMap(parsed, data) {
|
|
|
45841
46459
|
return fold(pos.name);
|
|
45842
46460
|
};
|
|
45843
46461
|
const poiCountries = [];
|
|
45844
|
-
let
|
|
46462
|
+
let anyUsPoi = false;
|
|
46463
|
+
let anyNonNaPoi = false;
|
|
45845
46464
|
const noteCountry = (iso) => {
|
|
45846
46465
|
if (iso) {
|
|
45847
46466
|
poiCountries.push(iso);
|
|
45848
|
-
if (iso
|
|
46467
|
+
if (iso === "US") anyUsPoi = true;
|
|
46468
|
+
if (iso !== "US" && iso !== "CA" && iso !== "MX") anyNonNaPoi = true;
|
|
45849
46469
|
}
|
|
45850
46470
|
};
|
|
45851
46471
|
const deferred = [];
|
|
45852
46472
|
for (const p of parsed.pois) {
|
|
45853
46473
|
if (p.pos.kind === "coords") {
|
|
45854
|
-
if (
|
|
46474
|
+
if (looksUS(p.pos.lat, p.pos.lon)) anyUsPoi = true;
|
|
46475
|
+
else if (!looksNorthAmericaNeighbor(p.pos.lat, p.pos.lon))
|
|
46476
|
+
anyNonNaPoi = true;
|
|
45855
46477
|
addResolvedPoi(p.pos.lat, p.pos.lon, p);
|
|
45856
46478
|
continue;
|
|
45857
46479
|
}
|
|
@@ -45869,14 +46491,15 @@ function resolveMap(parsed, data) {
|
|
|
45869
46491
|
deferred.push(p);
|
|
45870
46492
|
}
|
|
45871
46493
|
}
|
|
45872
|
-
const inferredCountry =
|
|
46494
|
+
const inferredCountry = localeCountry ?? mostCommonCountry(regions, poiCountries) ?? void 0;
|
|
46495
|
+
const inferredScope = localeSubdivision ?? inferredCountry;
|
|
45873
46496
|
for (const p of deferred) {
|
|
45874
46497
|
if (p.pos.kind !== "name") continue;
|
|
45875
46498
|
const got = lookupName(
|
|
45876
46499
|
p.pos.name,
|
|
45877
46500
|
p.pos.scope,
|
|
45878
46501
|
p.lineNumber,
|
|
45879
|
-
|
|
46502
|
+
inferredScope,
|
|
45880
46503
|
true
|
|
45881
46504
|
);
|
|
45882
46505
|
if (got.kind === "ok") {
|
|
@@ -45946,7 +46569,8 @@ function resolveMap(parsed, data) {
|
|
|
45946
46569
|
const meta = sizeValue !== void 0 ? { value: sizeValue } : {};
|
|
45947
46570
|
if (pos.kind === "coords") {
|
|
45948
46571
|
const id = alias ? fold(alias) : `@${pos.lat},${pos.lon}`;
|
|
45949
|
-
if (
|
|
46572
|
+
if (looksUS(pos.lat, pos.lon)) anyUsPoi = true;
|
|
46573
|
+
else if (!looksNorthAmericaNeighbor(pos.lat, pos.lon)) anyNonNaPoi = true;
|
|
45950
46574
|
if (!registry.has(id)) {
|
|
45951
46575
|
registerPoi(
|
|
45952
46576
|
id,
|
|
@@ -45969,7 +46593,7 @@ function resolveMap(parsed, data) {
|
|
|
45969
46593
|
if (registry.has(f)) return f;
|
|
45970
46594
|
const aliased = declaredByName.get(f);
|
|
45971
46595
|
if (aliased) return aliased;
|
|
45972
|
-
const got = lookupName(pos.name, pos.scope, line12,
|
|
46596
|
+
const got = lookupName(pos.name, pos.scope, line12, inferredScope, true);
|
|
45973
46597
|
if (got.kind !== "ok") return null;
|
|
45974
46598
|
noteCountry(got.iso);
|
|
45975
46599
|
registerPoi(
|
|
@@ -46026,9 +46650,12 @@ function resolveMap(parsed, data) {
|
|
|
46026
46650
|
}
|
|
46027
46651
|
routes.push({ stopIds, legs, lineNumber: rt.lineNumber });
|
|
46028
46652
|
}
|
|
46653
|
+
const hasUsContent = usSubdivisionReferenced || anyUsPoi || localeCountry === "US";
|
|
46654
|
+
const usOriented = !anyNonNaPoi && !regions.some(
|
|
46655
|
+
(r) => r.layer === "country" && !["US", "CA", "MX"].includes(r.iso)
|
|
46656
|
+
) && hasUsContent;
|
|
46029
46657
|
const subdivisions = [];
|
|
46030
|
-
if (usSubdivisionReferenced ||
|
|
46031
|
-
subdivisions.push("us-states");
|
|
46658
|
+
if (usSubdivisionReferenced || usOriented) subdivisions.push("us-states");
|
|
46032
46659
|
const regionBoxes = [];
|
|
46033
46660
|
for (const ref of referencedRegionIds) {
|
|
46034
46661
|
const bb = featureBbox(data.usStates, ref.id);
|
|
@@ -46046,17 +46673,51 @@ function resolveMap(parsed, data) {
|
|
|
46046
46673
|
[-180, -85],
|
|
46047
46674
|
[180, 85]
|
|
46048
46675
|
];
|
|
46049
|
-
|
|
46676
|
+
const basePad = regions.length > 0 ? REGION_PAD_FRACTION : PAD_FRACTION;
|
|
46677
|
+
let extent2 = unioned ? pad(unioned, basePad) : DEFAULT_EXTENT;
|
|
46678
|
+
const isPoiOnly = pois.length > 0 && regions.length === 0;
|
|
46679
|
+
const containerRegionIds = [];
|
|
46680
|
+
if (isPoiOnly) {
|
|
46681
|
+
const countries = decodeFeatures(data.worldDetail);
|
|
46682
|
+
const states = decodeFeatures(data.usStates);
|
|
46683
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46684
|
+
const containerBoxes = [];
|
|
46685
|
+
for (const p of pois) {
|
|
46686
|
+
const { country, state } = regionAt([p.lon, p.lat], countries, states);
|
|
46687
|
+
const id = state?.iso ?? country?.iso;
|
|
46688
|
+
if (!id || seen.has(id)) continue;
|
|
46689
|
+
seen.add(id);
|
|
46690
|
+
containerRegionIds.push(id);
|
|
46691
|
+
const bb = state ? featureBbox(data.usStates, id) : featureBboxPrimary(data.worldCoarse, id);
|
|
46692
|
+
if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
|
|
46693
|
+
}
|
|
46694
|
+
const containerUnion = unionExtent(containerBoxes, points);
|
|
46695
|
+
if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
|
|
46696
|
+
}
|
|
46697
|
+
if (isPoiOnly) {
|
|
46698
|
+
const cx = (extent2[0][0] + extent2[1][0]) / 2;
|
|
46699
|
+
const cy = (extent2[0][1] + extent2[1][1]) / 2;
|
|
46700
|
+
const lon = extent2[1][0] - extent2[0][0];
|
|
46701
|
+
const lat = extent2[1][1] - extent2[0][1];
|
|
46702
|
+
const longer = Math.max(lon, lat);
|
|
46703
|
+
if (longer > 0 && longer < POI_ZOOM_FLOOR_DEG) {
|
|
46704
|
+
const k = POI_ZOOM_FLOOR_DEG / longer;
|
|
46705
|
+
const halfLon = lon * k / 2;
|
|
46706
|
+
const halfLat = lat * k / 2;
|
|
46707
|
+
extent2 = [
|
|
46708
|
+
[cx - halfLon, cy - halfLat],
|
|
46709
|
+
[cx + halfLon, cy + halfLat]
|
|
46710
|
+
];
|
|
46711
|
+
}
|
|
46712
|
+
}
|
|
46050
46713
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
46051
46714
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
46052
46715
|
const span = Math.max(lonSpan, latSpan);
|
|
46053
46716
|
const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
|
|
46054
|
-
const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
46055
46717
|
let projection;
|
|
46056
|
-
|
|
46057
|
-
|
|
46058
|
-
|
|
46059
|
-
} else if (usDominant) {
|
|
46718
|
+
if (isPoiOnly && usOriented && lonSpan < US_NATIONAL_LON_SPAN) {
|
|
46719
|
+
projection = "mercator";
|
|
46720
|
+
} else if (usOriented) {
|
|
46060
46721
|
projection = "albers-usa";
|
|
46061
46722
|
} else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
|
|
46062
46723
|
projection = "equirectangular";
|
|
@@ -46074,11 +46735,20 @@ function resolveMap(parsed, data) {
|
|
|
46074
46735
|
result.edges = edges;
|
|
46075
46736
|
result.routes = routes;
|
|
46076
46737
|
result.basemaps = {
|
|
46077
|
-
|
|
46738
|
+
// Tier is intentionally pinned to detail (50m) at ALL scales. Diagrammo maps
|
|
46739
|
+
// are presentational (palette tints, relief hachures, POI hubs), not
|
|
46740
|
+
// survey-grade — recognizability > generalization: 110m coarse drops the
|
|
46741
|
+
// Italian boot to a stump at world scale. `WORLD_SPAN` lives on only for the
|
|
46742
|
+
// projection decision (the `usOriented`/`span > WORLD_SPAN` chain above); it
|
|
46743
|
+
// no longer gates basemap resolution.
|
|
46744
|
+
// `worldCoarse` is still loaded — it's the authoritative name/bbox index
|
|
46745
|
+
// (featureIndex, featureBboxPrimary), not dead code.
|
|
46746
|
+
world: "detail",
|
|
46078
46747
|
subdivisions
|
|
46079
46748
|
};
|
|
46080
46749
|
result.extent = extent2;
|
|
46081
46750
|
result.projection = projection;
|
|
46751
|
+
result.poiFrameContainers = containerRegionIds;
|
|
46082
46752
|
result.error = parsed.error ?? firstError(diagnostics);
|
|
46083
46753
|
return result;
|
|
46084
46754
|
}
|
|
@@ -46115,7 +46785,7 @@ function firstError(diags) {
|
|
|
46115
46785
|
const e = diags.find((d) => d.severity === "error");
|
|
46116
46786
|
return e ? formatDgmoError(e) : null;
|
|
46117
46787
|
}
|
|
46118
|
-
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
|
|
46788
|
+
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;
|
|
46119
46789
|
var init_resolver2 = __esm({
|
|
46120
46790
|
"src/map/resolver.ts"() {
|
|
46121
46791
|
"use strict";
|
|
@@ -46124,8 +46794,11 @@ var init_resolver2 = __esm({
|
|
|
46124
46794
|
WORLD_SPAN = 90;
|
|
46125
46795
|
MERCATOR_MAX_LAT = 80;
|
|
46126
46796
|
PAD_FRACTION = 0.05;
|
|
46797
|
+
REGION_PAD_FRACTION = 0.12;
|
|
46127
46798
|
WORLD_LAT_SOUTH = -58;
|
|
46128
46799
|
WORLD_LAT_NORTH = 78;
|
|
46800
|
+
POI_ZOOM_FLOOR_DEG = 7;
|
|
46801
|
+
US_NATIONAL_LON_SPAN = 48;
|
|
46129
46802
|
REGION_ALIASES = {
|
|
46130
46803
|
// Common everyday names → the Natural-Earth display name actually shipped.
|
|
46131
46804
|
"united states": "united states of america",
|
|
@@ -46203,17 +46876,305 @@ var init_resolver2 = __esm({
|
|
|
46203
46876
|
}
|
|
46204
46877
|
});
|
|
46205
46878
|
|
|
46879
|
+
// src/map/colorize.ts
|
|
46880
|
+
function assignColors(isos, adjacency) {
|
|
46881
|
+
const sorted = [...isos].sort();
|
|
46882
|
+
const byIso = /* @__PURE__ */ new Map();
|
|
46883
|
+
let maxIndex = -1;
|
|
46884
|
+
for (const iso of sorted) {
|
|
46885
|
+
const taken = /* @__PURE__ */ new Set();
|
|
46886
|
+
for (const n of adjacency.get(iso) ?? []) {
|
|
46887
|
+
const c = byIso.get(n);
|
|
46888
|
+
if (c !== void 0) taken.add(c);
|
|
46889
|
+
}
|
|
46890
|
+
let h = 0;
|
|
46891
|
+
while (taken.has(h)) h++;
|
|
46892
|
+
byIso.set(iso, h);
|
|
46893
|
+
if (h > maxIndex) maxIndex = h;
|
|
46894
|
+
}
|
|
46895
|
+
return { byIso, huesNeeded: maxIndex + 1 };
|
|
46896
|
+
}
|
|
46897
|
+
var init_colorize = __esm({
|
|
46898
|
+
"src/map/colorize.ts"() {
|
|
46899
|
+
"use strict";
|
|
46900
|
+
}
|
|
46901
|
+
});
|
|
46902
|
+
|
|
46903
|
+
// src/map/context-labels.ts
|
|
46904
|
+
function tierBand(maxSpanDeg) {
|
|
46905
|
+
if (maxSpanDeg >= 90) return "world";
|
|
46906
|
+
if (maxSpanDeg >= 20) return "continental";
|
|
46907
|
+
if (maxSpanDeg >= 5) return "regional";
|
|
46908
|
+
return "local";
|
|
46909
|
+
}
|
|
46910
|
+
function labelBudget(width, height, band) {
|
|
46911
|
+
const bandCap = {
|
|
46912
|
+
world: 6,
|
|
46913
|
+
continental: 5,
|
|
46914
|
+
regional: 4,
|
|
46915
|
+
local: 3
|
|
46916
|
+
};
|
|
46917
|
+
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
46918
|
+
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
46919
|
+
}
|
|
46920
|
+
function waterEligible(tier, kind, band) {
|
|
46921
|
+
switch (band) {
|
|
46922
|
+
case "world":
|
|
46923
|
+
return tier <= 1 && (kind === "ocean" || kind === "sea");
|
|
46924
|
+
case "continental":
|
|
46925
|
+
return tier <= 2;
|
|
46926
|
+
case "regional":
|
|
46927
|
+
return tier <= 3;
|
|
46928
|
+
case "local":
|
|
46929
|
+
return tier <= 4;
|
|
46930
|
+
}
|
|
46931
|
+
}
|
|
46932
|
+
function insideViewport(p, width, height) {
|
|
46933
|
+
return !!p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && p[0] >= 0 && p[0] <= width && p[1] >= 0 && p[1] <= height;
|
|
46934
|
+
}
|
|
46935
|
+
function labelWidth(text, letterSpacing) {
|
|
46936
|
+
const spacing = letterSpacing > 0 ? Math.max(0, text.length - 1) * letterSpacing : 0;
|
|
46937
|
+
return measureLegendText(text, FONT) + spacing + 2 * PADX;
|
|
46938
|
+
}
|
|
46939
|
+
function wrapLabel2(text, letterSpacing) {
|
|
46940
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
46941
|
+
if (words.length <= 1) return [text];
|
|
46942
|
+
const maxLines = words.length >= 4 ? 3 : 2;
|
|
46943
|
+
const n = words.length;
|
|
46944
|
+
let best = null;
|
|
46945
|
+
for (let mask = 0; mask < 1 << n - 1; mask++) {
|
|
46946
|
+
const lines = [];
|
|
46947
|
+
let cur = [words[0]];
|
|
46948
|
+
for (let i = 1; i < n; i++) {
|
|
46949
|
+
if (mask & 1 << i - 1) {
|
|
46950
|
+
lines.push(cur.join(" "));
|
|
46951
|
+
cur = [words[i]];
|
|
46952
|
+
} else cur.push(words[i]);
|
|
46953
|
+
}
|
|
46954
|
+
lines.push(cur.join(" "));
|
|
46955
|
+
if (lines.length > maxLines) continue;
|
|
46956
|
+
const cost = Math.round(
|
|
46957
|
+
Math.max(...lines.map((l) => labelWidth(l, letterSpacing)))
|
|
46958
|
+
);
|
|
46959
|
+
const head = labelWidth(lines[0], letterSpacing);
|
|
46960
|
+
if (!best || cost < best.cost || cost === best.cost && lines.length < best.lines.length || cost === best.cost && lines.length === best.lines.length && head > best.head)
|
|
46961
|
+
best = { lines, cost, head };
|
|
46962
|
+
}
|
|
46963
|
+
return best?.lines ?? [text];
|
|
46964
|
+
}
|
|
46965
|
+
function rectAround(cx, cy, lines, letterSpacing) {
|
|
46966
|
+
const w = Math.max(...lines.map((l) => labelWidth(l, letterSpacing)));
|
|
46967
|
+
const h = (lines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY;
|
|
46968
|
+
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
46969
|
+
}
|
|
46970
|
+
function rectFits(r, width, height) {
|
|
46971
|
+
return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
|
|
46972
|
+
}
|
|
46973
|
+
function overlapsPadded(a, b, pad2) {
|
|
46974
|
+
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;
|
|
46975
|
+
}
|
|
46976
|
+
function placeContextLabels(args) {
|
|
46977
|
+
const {
|
|
46978
|
+
projection,
|
|
46979
|
+
dLonSpan,
|
|
46980
|
+
dLatSpan,
|
|
46981
|
+
width,
|
|
46982
|
+
height,
|
|
46983
|
+
waterBodies,
|
|
46984
|
+
countries,
|
|
46985
|
+
palette,
|
|
46986
|
+
project,
|
|
46987
|
+
collides,
|
|
46988
|
+
overLand
|
|
46989
|
+
} = args;
|
|
46990
|
+
void projection;
|
|
46991
|
+
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
46992
|
+
const budget = labelBudget(width, height, band);
|
|
46993
|
+
if (budget <= 0) return [];
|
|
46994
|
+
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
46995
|
+
const countryColor = palette.textMuted;
|
|
46996
|
+
const haloColor = palette.bg;
|
|
46997
|
+
const candidates = [];
|
|
46998
|
+
const center = [width / 2, height / 2];
|
|
46999
|
+
for (const e of waterBodies?.entries ?? []) {
|
|
47000
|
+
const [lat, lon, name, tier, kind, alt] = e;
|
|
47001
|
+
if (!waterEligible(tier, kind, band)) continue;
|
|
47002
|
+
const wlines = wrapLabel2(name, WATER_LETTER_SPACING);
|
|
47003
|
+
const anchorsLngLat = [[lon, lat]];
|
|
47004
|
+
for (const a of alt ?? []) anchorsLngLat.push([a[1], a[0]]);
|
|
47005
|
+
let best = null;
|
|
47006
|
+
let bestD = Infinity;
|
|
47007
|
+
let nearestProj = null;
|
|
47008
|
+
let nearestProjD = Infinity;
|
|
47009
|
+
for (const [aLon, aLat] of anchorsLngLat) {
|
|
47010
|
+
const p = project(aLon, aLat);
|
|
47011
|
+
if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
|
|
47012
|
+
const d = (p[0] - center[0]) ** 2 + (p[1] - center[1]) ** 2;
|
|
47013
|
+
if (d < nearestProjD) {
|
|
47014
|
+
nearestProjD = d;
|
|
47015
|
+
nearestProj = p;
|
|
47016
|
+
}
|
|
47017
|
+
if (!insideViewport(p, width, height)) continue;
|
|
47018
|
+
if (d < bestD) {
|
|
47019
|
+
bestD = d;
|
|
47020
|
+
best = p;
|
|
47021
|
+
}
|
|
47022
|
+
}
|
|
47023
|
+
if (!best && tier === 0 && nearestProj) {
|
|
47024
|
+
const overX = Math.max(0, -nearestProj[0], nearestProj[0] - width);
|
|
47025
|
+
const overY = Math.max(0, -nearestProj[1], nearestProj[1] - height);
|
|
47026
|
+
if (overX <= width * EDGE_CLAMP_OVERSHOOT && overY <= height * EDGE_CLAMP_OVERSHOOT) {
|
|
47027
|
+
const halfW = Math.max(...wlines.map((l) => labelWidth(l, WATER_LETTER_SPACING))) / 2;
|
|
47028
|
+
const halfH = ((wlines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY) / 2;
|
|
47029
|
+
const m = EDGE_CLAMP_MARGIN;
|
|
47030
|
+
best = [
|
|
47031
|
+
Math.min(Math.max(nearestProj[0], halfW + m), width - halfW - m),
|
|
47032
|
+
Math.min(Math.max(nearestProj[1], halfH + m), height - halfH - m)
|
|
47033
|
+
];
|
|
47034
|
+
}
|
|
47035
|
+
}
|
|
47036
|
+
if (!best) continue;
|
|
47037
|
+
candidates.push({
|
|
47038
|
+
text: name,
|
|
47039
|
+
lines: wlines,
|
|
47040
|
+
cx: best[0],
|
|
47041
|
+
cy: best[1],
|
|
47042
|
+
italic: true,
|
|
47043
|
+
letterSpacing: WATER_LETTER_SPACING,
|
|
47044
|
+
color: waterColor,
|
|
47045
|
+
// Water before any country (×1000), then by tier, then kind, then name.
|
|
47046
|
+
sort: tier * 10 + KIND_ORDER[kind]
|
|
47047
|
+
});
|
|
47048
|
+
}
|
|
47049
|
+
const ranked = countries.map((c) => {
|
|
47050
|
+
const [x0, y0, x1, y1] = c.bbox;
|
|
47051
|
+
const w = x1 - x0;
|
|
47052
|
+
const h = y1 - y0;
|
|
47053
|
+
return { c, w, h, area: w * h };
|
|
47054
|
+
}).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
|
|
47055
|
+
let ci = 0;
|
|
47056
|
+
for (const r of ranked) {
|
|
47057
|
+
const { c, w, h } = r;
|
|
47058
|
+
if (w > width * 0.66 || h > height * 0.66) continue;
|
|
47059
|
+
if (!insideViewport(c.anchor, width, height)) continue;
|
|
47060
|
+
const text = c.name;
|
|
47061
|
+
const tw = labelWidth(text, 0);
|
|
47062
|
+
if (tw > w || FONT + 2 * PADY > h) continue;
|
|
47063
|
+
candidates.push({
|
|
47064
|
+
text,
|
|
47065
|
+
lines: [text],
|
|
47066
|
+
cx: c.anchor[0],
|
|
47067
|
+
cy: c.anchor[1],
|
|
47068
|
+
italic: false,
|
|
47069
|
+
letterSpacing: 0,
|
|
47070
|
+
color: countryColor,
|
|
47071
|
+
// Always after every water body (+1e6); larger area = earlier.
|
|
47072
|
+
sort: 1e6 + ci++
|
|
47073
|
+
});
|
|
47074
|
+
}
|
|
47075
|
+
candidates.sort((a, b) => a.sort - b.sort);
|
|
47076
|
+
const placed = [];
|
|
47077
|
+
const placedRects = [];
|
|
47078
|
+
for (const cand of candidates) {
|
|
47079
|
+
if (placed.length >= budget) break;
|
|
47080
|
+
const rect = rectAround(cand.cx, cand.cy, cand.lines, cand.letterSpacing);
|
|
47081
|
+
if (!rectFits(rect, width, height)) continue;
|
|
47082
|
+
if (cand.italic && overLand) {
|
|
47083
|
+
const inset = 2;
|
|
47084
|
+
const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
|
|
47085
|
+
const touchesLand = cand.lines.some((line12, li) => {
|
|
47086
|
+
const lw = labelWidth(line12, cand.letterSpacing);
|
|
47087
|
+
const x0 = cand.cx - lw / 2 + inset;
|
|
47088
|
+
const x1 = cand.cx + lw / 2 - inset;
|
|
47089
|
+
const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
|
|
47090
|
+
const base = top + li * LINE_HEIGHT;
|
|
47091
|
+
return [base, base - FONT * 0.4, base - FONT * 0.8].some(
|
|
47092
|
+
(y) => xs.some((x) => overLand(x, y))
|
|
47093
|
+
);
|
|
47094
|
+
});
|
|
47095
|
+
if (touchesLand) continue;
|
|
47096
|
+
}
|
|
47097
|
+
if (collides(rect)) continue;
|
|
47098
|
+
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
47099
|
+
placedRects.push(rect);
|
|
47100
|
+
placed.push({
|
|
47101
|
+
x: cand.cx,
|
|
47102
|
+
y: cand.cy,
|
|
47103
|
+
text: cand.text,
|
|
47104
|
+
anchor: "middle",
|
|
47105
|
+
color: cand.color,
|
|
47106
|
+
// No halo: the bg-coloured outline reads as a ghost box behind the text
|
|
47107
|
+
// over the tinted water/land. Context labels are muted enough to sit
|
|
47108
|
+
// cleanly on the basemap without one.
|
|
47109
|
+
halo: false,
|
|
47110
|
+
haloColor,
|
|
47111
|
+
italic: cand.italic,
|
|
47112
|
+
letterSpacing: cand.letterSpacing,
|
|
47113
|
+
...cand.lines.length > 1 ? { lines: cand.lines } : {},
|
|
47114
|
+
lineNumber: 0
|
|
47115
|
+
});
|
|
47116
|
+
}
|
|
47117
|
+
return placed;
|
|
47118
|
+
}
|
|
47119
|
+
var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, KIND_ORDER;
|
|
47120
|
+
var init_context_labels = __esm({
|
|
47121
|
+
"src/map/context-labels.ts"() {
|
|
47122
|
+
"use strict";
|
|
47123
|
+
init_color_utils();
|
|
47124
|
+
init_legend_constants();
|
|
47125
|
+
FONT = 11;
|
|
47126
|
+
LINE_HEIGHT = FONT + 2;
|
|
47127
|
+
PADX = 4;
|
|
47128
|
+
PADY = 3;
|
|
47129
|
+
WATER_LETTER_SPACING = 1.5;
|
|
47130
|
+
CONTEXT_PAD = 4;
|
|
47131
|
+
EDGE_CLAMP_MARGIN = 8;
|
|
47132
|
+
EDGE_CLAMP_OVERSHOOT = 0.35;
|
|
47133
|
+
KIND_ORDER = {
|
|
47134
|
+
ocean: 0,
|
|
47135
|
+
sea: 1,
|
|
47136
|
+
gulf: 2,
|
|
47137
|
+
bay: 3,
|
|
47138
|
+
strait: 4,
|
|
47139
|
+
channel: 5,
|
|
47140
|
+
sound: 6
|
|
47141
|
+
};
|
|
47142
|
+
}
|
|
47143
|
+
});
|
|
47144
|
+
|
|
46206
47145
|
// src/map/layout.ts
|
|
46207
47146
|
function geomObject2(topo) {
|
|
46208
47147
|
const key = Object.keys(topo.objects)[0];
|
|
46209
47148
|
return topo.objects[key];
|
|
46210
47149
|
}
|
|
47150
|
+
function mergeFeatures(a, b) {
|
|
47151
|
+
const polysOf = (f) => {
|
|
47152
|
+
const g = f.geometry;
|
|
47153
|
+
if (!g) return null;
|
|
47154
|
+
if (g.type === "Polygon") return [g.coordinates];
|
|
47155
|
+
if (g.type === "MultiPolygon") return g.coordinates;
|
|
47156
|
+
return null;
|
|
47157
|
+
};
|
|
47158
|
+
const pa = polysOf(a);
|
|
47159
|
+
const pb = polysOf(b);
|
|
47160
|
+
if (!pa || !pb) return a;
|
|
47161
|
+
return {
|
|
47162
|
+
...a,
|
|
47163
|
+
geometry: { type: "MultiPolygon", coordinates: [...pa, ...pb] }
|
|
47164
|
+
};
|
|
47165
|
+
}
|
|
46211
47166
|
function decodeLayer(topo) {
|
|
47167
|
+
const cached = decodeCache.get(topo);
|
|
47168
|
+
if (cached) return cached;
|
|
46212
47169
|
const out = /* @__PURE__ */ new Map();
|
|
46213
47170
|
for (const g of geomObject2(topo).geometries) {
|
|
46214
47171
|
const f = (0, import_topojson_client2.feature)(topo, g);
|
|
46215
|
-
|
|
47172
|
+
if (!f.geometry) continue;
|
|
47173
|
+
const tagged = { ...f, id: g.id };
|
|
47174
|
+
const existing = out.get(g.id);
|
|
47175
|
+
out.set(g.id, existing ? mergeFeatures(existing, tagged) : tagged);
|
|
46216
47176
|
}
|
|
47177
|
+
decodeCache.set(topo, out);
|
|
46217
47178
|
return out;
|
|
46218
47179
|
}
|
|
46219
47180
|
function projectionFor(family) {
|
|
@@ -46222,9 +47183,12 @@ function projectionFor(family) {
|
|
|
46222
47183
|
return usConusProjection();
|
|
46223
47184
|
case "mercator":
|
|
46224
47185
|
return (0, import_d3_geo2.geoMercator)();
|
|
47186
|
+
case "equal-earth":
|
|
47187
|
+
return (0, import_d3_geo2.geoEqualEarth)();
|
|
47188
|
+
case "equirectangular":
|
|
47189
|
+
return (0, import_d3_geo2.geoEquirectangular)();
|
|
46225
47190
|
case "natural-earth":
|
|
46226
47191
|
return (0, import_d3_geo2.geoNaturalEarth1)();
|
|
46227
|
-
case "equirectangular":
|
|
46228
47192
|
default:
|
|
46229
47193
|
return (0, import_d3_geo2.geoEquirectangular)();
|
|
46230
47194
|
}
|
|
@@ -46243,13 +47207,11 @@ function mapNeutralLandColor(palette, isDark, _dataActive = false) {
|
|
|
46243
47207
|
isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT
|
|
46244
47208
|
);
|
|
46245
47209
|
}
|
|
46246
|
-
function
|
|
46247
|
-
const { palette, isDark } = opts;
|
|
46248
|
-
const { width, height } = size;
|
|
47210
|
+
function buildMapProjection(resolved, data) {
|
|
46249
47211
|
const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
|
|
46250
|
-
const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
|
|
47212
|
+
const usCrisp = (resolved.projection === "albers-usa" || resolved.projection === "mercator") && wantsUsStates && !!data.naLand;
|
|
46251
47213
|
const worldTopo = usCrisp ? data.worldDetail : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
|
|
46252
|
-
const worldLayer = decodeLayer(worldTopo);
|
|
47214
|
+
const worldLayer = new Map(decodeLayer(worldTopo));
|
|
46253
47215
|
if (usCrisp && data.naLand) {
|
|
46254
47216
|
const [nbW, nbS, nbE, nbN] = [-140, 10, -52, 66];
|
|
46255
47217
|
const crisp = decodeLayer(data.naLand);
|
|
@@ -46258,16 +47220,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46258
47220
|
if (!base) continue;
|
|
46259
47221
|
const [[bw, bs], [be, bn]] = (0, import_d3_geo2.geoBounds)(base);
|
|
46260
47222
|
if (bw >= nbW && be <= nbE && bs >= nbS && bn <= nbN)
|
|
46261
|
-
worldLayer.set(iso, cf);
|
|
47223
|
+
worldLayer.set(iso, { ...cf, properties: base.properties });
|
|
46262
47224
|
}
|
|
46263
47225
|
}
|
|
46264
47226
|
const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
|
|
47227
|
+
const extentOutline = () => {
|
|
47228
|
+
const [[w, s], [e, n]] = resolved.extent;
|
|
47229
|
+
const N = 16;
|
|
47230
|
+
const coords = [];
|
|
47231
|
+
for (let i = 0; i <= N; i++) {
|
|
47232
|
+
const t = i / N;
|
|
47233
|
+
const lon = w + (e - w) * t;
|
|
47234
|
+
const lat = s + (n - s) * t;
|
|
47235
|
+
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
47236
|
+
}
|
|
47237
|
+
return {
|
|
47238
|
+
type: "Feature",
|
|
47239
|
+
properties: {},
|
|
47240
|
+
geometry: { type: "MultiPoint", coordinates: coords }
|
|
47241
|
+
};
|
|
47242
|
+
};
|
|
47243
|
+
let fitFeatures;
|
|
47244
|
+
if (resolved.projection === "albers-usa" && usLayer) {
|
|
47245
|
+
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
47246
|
+
const neighborPoints = resolved.pois.filter((p) => !inAlaska(p.lon, p.lat) && !inHawaii(p.lon, p.lat)).map((p) => [p.lon, p.lat]);
|
|
47247
|
+
if (neighborPoints.length > 0) {
|
|
47248
|
+
fitFeatures.push({
|
|
47249
|
+
type: "Feature",
|
|
47250
|
+
properties: {},
|
|
47251
|
+
geometry: { type: "MultiPoint", coordinates: neighborPoints }
|
|
47252
|
+
});
|
|
47253
|
+
}
|
|
47254
|
+
for (const r of resolved.regions) {
|
|
47255
|
+
if (r.layer === "country" && (r.iso === "CA" || r.iso === "MX")) {
|
|
47256
|
+
const cf = worldLayer.get(r.iso);
|
|
47257
|
+
if (cf) fitFeatures.push(cf);
|
|
47258
|
+
}
|
|
47259
|
+
}
|
|
47260
|
+
} else {
|
|
47261
|
+
fitFeatures = [extentOutline()];
|
|
47262
|
+
}
|
|
47263
|
+
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
47264
|
+
const projection = projectionFor(resolved.projection);
|
|
47265
|
+
if (resolved.projection !== "albers-usa") {
|
|
47266
|
+
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
47267
|
+
if (centerLon > 180) centerLon -= 360;
|
|
47268
|
+
projection.rotate([-centerLon, 0]);
|
|
47269
|
+
}
|
|
47270
|
+
const fitGB = (0, import_d3_geo2.geoBounds)(fitTarget);
|
|
47271
|
+
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
47272
|
+
return {
|
|
47273
|
+
projection,
|
|
47274
|
+
fitTarget,
|
|
47275
|
+
fitIsGlobal,
|
|
47276
|
+
worldLayer,
|
|
47277
|
+
usLayer,
|
|
47278
|
+
usCrisp,
|
|
47279
|
+
wantsUsStates,
|
|
47280
|
+
worldTopo
|
|
47281
|
+
};
|
|
47282
|
+
}
|
|
47283
|
+
function parsePathRings(d) {
|
|
47284
|
+
const rings = [];
|
|
47285
|
+
let cur = [];
|
|
47286
|
+
const re = /([MLZ])([^MLZ]*)/g;
|
|
47287
|
+
let m;
|
|
47288
|
+
while (m = re.exec(d)) {
|
|
47289
|
+
if (m[1] === "Z") {
|
|
47290
|
+
if (cur.length) rings.push(cur);
|
|
47291
|
+
cur = [];
|
|
47292
|
+
continue;
|
|
47293
|
+
}
|
|
47294
|
+
if (m[1] === "M" && cur.length) {
|
|
47295
|
+
rings.push(cur);
|
|
47296
|
+
cur = [];
|
|
47297
|
+
}
|
|
47298
|
+
const nums = m[2].split(/[ ,]+/).map(Number);
|
|
47299
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
47300
|
+
const x = nums[i];
|
|
47301
|
+
const y = nums[i + 1];
|
|
47302
|
+
if (Number.isFinite(x) && Number.isFinite(y)) cur.push([x, y]);
|
|
47303
|
+
}
|
|
47304
|
+
}
|
|
47305
|
+
if (cur.length) rings.push(cur);
|
|
47306
|
+
return rings;
|
|
47307
|
+
}
|
|
47308
|
+
function dropAntimeridianWrapSlivers(d, width, height) {
|
|
47309
|
+
const rings = parsePathRings(d);
|
|
47310
|
+
if (rings.length <= 1) return d;
|
|
47311
|
+
const eps = 0.75;
|
|
47312
|
+
const minArea = 3e-3 * width * height;
|
|
47313
|
+
const ringArea = (r) => {
|
|
47314
|
+
let s = 0;
|
|
47315
|
+
for (let i = 0; i < r.length; i++) {
|
|
47316
|
+
const a = r[i];
|
|
47317
|
+
const b = r[(i + 1) % r.length];
|
|
47318
|
+
s += a[0] * b[1] - b[0] * a[1];
|
|
47319
|
+
}
|
|
47320
|
+
return Math.abs(s) / 2;
|
|
47321
|
+
};
|
|
47322
|
+
const areas = rings.map(ringArea);
|
|
47323
|
+
const maxArea = Math.max(...areas);
|
|
47324
|
+
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;
|
|
47325
|
+
let dropped = false;
|
|
47326
|
+
const kept = rings.filter((r, idx) => {
|
|
47327
|
+
if (areas[idx] >= maxArea || areas[idx] >= minArea) return true;
|
|
47328
|
+
const touches = r.some((p, i) => onVEdge(p, r[(i + 1) % r.length]));
|
|
47329
|
+
if (touches) {
|
|
47330
|
+
dropped = true;
|
|
47331
|
+
return false;
|
|
47332
|
+
}
|
|
47333
|
+
return true;
|
|
47334
|
+
});
|
|
47335
|
+
if (!dropped) return d;
|
|
47336
|
+
return kept.map(
|
|
47337
|
+
(r) => r.map((p, i) => (i ? "L" : "M") + p[0] + "," + p[1]).join("") + "Z"
|
|
47338
|
+
).join("");
|
|
47339
|
+
}
|
|
47340
|
+
function layoutMap(resolved, data, size, opts) {
|
|
47341
|
+
const { palette, isDark } = opts;
|
|
47342
|
+
const { width, height } = size;
|
|
47343
|
+
const {
|
|
47344
|
+
projection,
|
|
47345
|
+
fitTarget,
|
|
47346
|
+
fitIsGlobal,
|
|
47347
|
+
worldLayer,
|
|
47348
|
+
usLayer,
|
|
47349
|
+
usCrisp,
|
|
47350
|
+
worldTopo
|
|
47351
|
+
} = buildMapProjection(resolved, data);
|
|
46265
47352
|
const usContext = usLayer !== null;
|
|
46266
47353
|
const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
|
|
46267
47354
|
const values = resolved.regions.filter((r) => r.value !== void 0).map((r) => r.value);
|
|
46268
|
-
const
|
|
46269
|
-
const rampMin =
|
|
46270
|
-
const rampMax =
|
|
47355
|
+
const allNonNegative = values.length > 0 && values.every((v) => v >= 0);
|
|
47356
|
+
const rampMin = allNonNegative ? 0 : Math.min(...values);
|
|
47357
|
+
const rampMax = Math.max(...values);
|
|
46271
47358
|
const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
|
|
46272
47359
|
const hasRamp = values.length > 0;
|
|
46273
47360
|
const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
|
|
@@ -46288,7 +47375,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46288
47375
|
activeGroup = VALUE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
|
|
46289
47376
|
}
|
|
46290
47377
|
const activeIsScore = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
46291
|
-
const mutedBasemap =
|
|
47378
|
+
const mutedBasemap = activeGroup !== null;
|
|
46292
47379
|
const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
|
|
46293
47380
|
const water = mapBackgroundColor(palette, isDark, mutedBasemap);
|
|
46294
47381
|
const lakeStroke = mix(regionStroke, water, 45);
|
|
@@ -46297,10 +47384,43 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46297
47384
|
palette.bg,
|
|
46298
47385
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
46299
47386
|
);
|
|
47387
|
+
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && resolved.tagGroups.length === 0;
|
|
47388
|
+
const colorByIso = /* @__PURE__ */ new Map();
|
|
47389
|
+
if (colorizeActive) {
|
|
47390
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
47391
|
+
const addEdges = (src) => {
|
|
47392
|
+
for (const [iso, ns] of src) {
|
|
47393
|
+
const cur = adjacency.get(iso);
|
|
47394
|
+
if (cur) cur.push(...ns);
|
|
47395
|
+
else adjacency.set(iso, [...ns]);
|
|
47396
|
+
}
|
|
47397
|
+
};
|
|
47398
|
+
addEdges(buildAdjacency(worldTopo));
|
|
47399
|
+
if (usLayer) {
|
|
47400
|
+
addEdges(buildAdjacency(data.usStates));
|
|
47401
|
+
for (const [country, states] of Object.entries(FOREIGN_BORDER)) {
|
|
47402
|
+
const cn = adjacency.get(country);
|
|
47403
|
+
if (!cn) continue;
|
|
47404
|
+
for (const st of states) {
|
|
47405
|
+
const sn = adjacency.get(st);
|
|
47406
|
+
if (!sn) continue;
|
|
47407
|
+
cn.push(st);
|
|
47408
|
+
sn.push(country);
|
|
47409
|
+
}
|
|
47410
|
+
}
|
|
47411
|
+
}
|
|
47412
|
+
const { byIso, huesNeeded } = assignColors(
|
|
47413
|
+
[...adjacency.keys()],
|
|
47414
|
+
adjacency
|
|
47415
|
+
);
|
|
47416
|
+
const tints = politicalTints(palette, huesNeeded, isDark);
|
|
47417
|
+
for (const [iso, idx] of byIso) colorByIso.set(iso, tints[idx]);
|
|
47418
|
+
}
|
|
47419
|
+
const colorizeStroke = (fill2) => mix(fill2, palette.text, 35);
|
|
46300
47420
|
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
46301
47421
|
const fillForValue = (s) => {
|
|
46302
47422
|
const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
|
|
46303
|
-
const pct =
|
|
47423
|
+
const pct = RAMP_FLOOR2 + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR2);
|
|
46304
47424
|
return mix(rampHue, rampBase, pct);
|
|
46305
47425
|
};
|
|
46306
47426
|
const tagFill = (tags, groupName) => {
|
|
@@ -46332,43 +47452,15 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46332
47452
|
if (activeIsScore) {
|
|
46333
47453
|
return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
|
|
46334
47454
|
}
|
|
47455
|
+
if (colorizeActive) return (r.iso && colorByIso.get(r.iso)) ?? neutralFill;
|
|
46335
47456
|
return tagFill(r.tags, activeGroup) ?? neutralFill;
|
|
46336
47457
|
};
|
|
46337
47458
|
const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
|
|
46338
|
-
const
|
|
46339
|
-
const [[w, s], [e, n]] = resolved.extent;
|
|
46340
|
-
const N = 16;
|
|
46341
|
-
const coords = [];
|
|
46342
|
-
for (let i = 0; i <= N; i++) {
|
|
46343
|
-
const t = i / N;
|
|
46344
|
-
const lon = w + (e - w) * t;
|
|
46345
|
-
const lat = s + (n - s) * t;
|
|
46346
|
-
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
46347
|
-
}
|
|
46348
|
-
return {
|
|
46349
|
-
type: "Feature",
|
|
46350
|
-
properties: {},
|
|
46351
|
-
geometry: { type: "MultiPoint", coordinates: coords }
|
|
46352
|
-
};
|
|
46353
|
-
};
|
|
46354
|
-
let fitFeatures;
|
|
46355
|
-
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46356
|
-
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
46357
|
-
} else {
|
|
46358
|
-
fitFeatures = [extentOutline()];
|
|
46359
|
-
}
|
|
46360
|
-
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
46361
|
-
const projection = projectionFor(resolved.projection);
|
|
46362
|
-
if (resolved.projection !== "albers-usa") {
|
|
46363
|
-
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
46364
|
-
if (centerLon > 180) centerLon -= 360;
|
|
46365
|
-
projection.rotate([-centerLon, 0]);
|
|
46366
|
-
}
|
|
46367
|
-
const TITLE_GAP = 16;
|
|
47459
|
+
const TITLE_GAP2 = 16;
|
|
46368
47460
|
let topPad = FIT_PAD;
|
|
46369
47461
|
if (resolved.title && resolved.pois.length > 0) {
|
|
46370
47462
|
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
46371
|
-
topPad = Math.max(FIT_PAD, bannerBottom +
|
|
47463
|
+
topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
|
|
46372
47464
|
}
|
|
46373
47465
|
const fitBox = [
|
|
46374
47466
|
[FIT_PAD, topPad],
|
|
@@ -46378,21 +47470,20 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46378
47470
|
]
|
|
46379
47471
|
];
|
|
46380
47472
|
projection.fitExtent(fitBox, fitTarget);
|
|
46381
|
-
const fitGB = (0, import_d3_geo2.geoBounds)(fitTarget);
|
|
46382
|
-
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46383
47473
|
let path;
|
|
46384
47474
|
let project;
|
|
46385
47475
|
let stretchParams = null;
|
|
46386
|
-
if (fitIsGlobal) {
|
|
47476
|
+
if (fitIsGlobal && !opts.preferContain) {
|
|
46387
47477
|
const cb = (0, import_d3_geo2.geoPath)(projection).bounds(fitTarget);
|
|
46388
47478
|
const bx0 = cb[0][0];
|
|
46389
47479
|
const by0 = cb[0][1];
|
|
46390
47480
|
const cw = cb[1][0] - bx0;
|
|
46391
47481
|
const ch = cb[1][1] - by0;
|
|
46392
|
-
const
|
|
46393
|
-
const
|
|
46394
|
-
const
|
|
46395
|
-
const
|
|
47482
|
+
const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
|
|
47483
|
+
const ox = 0;
|
|
47484
|
+
const oy = topReserve;
|
|
47485
|
+
const sx = cw > 0 ? width / cw : 1;
|
|
47486
|
+
const sy = ch > 0 ? (height - topReserve) / ch : 1;
|
|
46396
47487
|
stretchParams = { sx, sy, ox, oy, bx0, by0 };
|
|
46397
47488
|
const stretch = (x, y) => [
|
|
46398
47489
|
ox + (x - bx0) * sx,
|
|
@@ -46425,7 +47516,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46425
47516
|
const insets = [];
|
|
46426
47517
|
const insetRegions = [];
|
|
46427
47518
|
const insetLabelSeeds = [];
|
|
46428
|
-
|
|
47519
|
+
const akRef = resolved.regions.some((r) => r.iso === "US-AK") || resolved.pois.some((p) => inAlaska(p.lon, p.lat));
|
|
47520
|
+
const hiRef = resolved.regions.some((r) => r.iso === "US-HI") || resolved.pois.some((p) => inHawaii(p.lon, p.lat));
|
|
47521
|
+
if (resolved.projection === "albers-usa" && usLayer && (akRef || hiRef)) {
|
|
46429
47522
|
const PAD = 8;
|
|
46430
47523
|
const GAP = 12;
|
|
46431
47524
|
const yB = height - FIT_PAD;
|
|
@@ -46490,8 +47583,18 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46490
47583
|
);
|
|
46491
47584
|
const d = (0, import_d3_geo2.geoPath)(proj)(f) ?? "";
|
|
46492
47585
|
if (!d) return xr;
|
|
47586
|
+
let contextLand;
|
|
47587
|
+
if (iso === "US-AK") {
|
|
47588
|
+
const can = worldLayer.get("CA");
|
|
47589
|
+
const cd = can ? (0, import_d3_geo2.geoPath)(proj)(can) ?? "" : "";
|
|
47590
|
+
if (cd)
|
|
47591
|
+
contextLand = {
|
|
47592
|
+
d: cd,
|
|
47593
|
+
fill: colorizeActive ? colorByIso.get("CA") ?? foreignFill : foreignFill
|
|
47594
|
+
};
|
|
47595
|
+
}
|
|
46493
47596
|
const r = regionById.get(iso);
|
|
46494
|
-
let fill2 = neutralFill;
|
|
47597
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? neutralFill : neutralFill;
|
|
46495
47598
|
let lineNumber = -1;
|
|
46496
47599
|
if (r?.layer === "us-state") {
|
|
46497
47600
|
fill2 = regionFill(r);
|
|
@@ -46510,13 +47613,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46510
47613
|
],
|
|
46511
47614
|
// The FITTED inset projection (just fit to this box) — captured so the
|
|
46512
47615
|
// geo-query can invert pixels inside the frame back to AK/HI coords.
|
|
46513
|
-
projection: proj
|
|
47616
|
+
projection: proj,
|
|
47617
|
+
...contextLand && { contextLand }
|
|
46514
47618
|
});
|
|
46515
47619
|
insetRegions.push({
|
|
46516
47620
|
id: iso,
|
|
46517
47621
|
d,
|
|
46518
47622
|
fill: fill2,
|
|
46519
|
-
stroke: regionStroke,
|
|
47623
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46520
47624
|
lineNumber,
|
|
46521
47625
|
layer: "us-state",
|
|
46522
47626
|
...r?.value !== void 0 && { value: r.value },
|
|
@@ -46529,13 +47633,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46529
47633
|
}
|
|
46530
47634
|
return xr;
|
|
46531
47635
|
};
|
|
46532
|
-
|
|
46533
|
-
|
|
46534
|
-
alaskaProjection(),
|
|
46535
|
-
|
|
46536
|
-
|
|
46537
|
-
|
|
46538
|
-
|
|
47636
|
+
let akRight = FIT_PAD;
|
|
47637
|
+
if (akRef)
|
|
47638
|
+
akRight = placeInset("US-AK", alaskaProjection(), FIT_PAD, width * 0.15);
|
|
47639
|
+
if (hiRef)
|
|
47640
|
+
placeInset(
|
|
47641
|
+
"US-HI",
|
|
47642
|
+
hawaiiProjection(),
|
|
47643
|
+
akRef ? akRight + 24 : FIT_PAD,
|
|
47644
|
+
width * 0.1
|
|
47645
|
+
);
|
|
46539
47646
|
}
|
|
46540
47647
|
const conusFit = resolved.projection === "albers-usa" && !!usLayer;
|
|
46541
47648
|
const classifyExtent = conusFit ? (0, import_d3_geo2.geoBounds)(fitTarget) : resolved.extent;
|
|
@@ -46551,15 +47658,24 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46551
47658
|
};
|
|
46552
47659
|
const ringOverlapsView = (ring) => {
|
|
46553
47660
|
let loMin = Infinity, loMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
|
|
47661
|
+
const lons = [];
|
|
46554
47662
|
for (const [rawLon] of ring) {
|
|
46555
47663
|
const lon = normLon(rawLon);
|
|
47664
|
+
lons.push(lon);
|
|
46556
47665
|
if (lon < loMin) loMin = lon;
|
|
46557
47666
|
if (lon > loMax) loMax = lon;
|
|
46558
47667
|
if (rawLon < rawMin) rawMin = rawLon;
|
|
46559
47668
|
if (rawLon > rawMax) rawMax = rawLon;
|
|
46560
47669
|
}
|
|
46561
|
-
|
|
46562
|
-
|
|
47670
|
+
lons.sort((a, b) => a - b);
|
|
47671
|
+
let maxGap = 0;
|
|
47672
|
+
for (let i = 1; i < lons.length; i++)
|
|
47673
|
+
maxGap = Math.max(maxGap, lons[i] - lons[i - 1]);
|
|
47674
|
+
if (lons.length > 1)
|
|
47675
|
+
maxGap = Math.max(maxGap, lons[0] + 360 - lons[lons.length - 1]);
|
|
47676
|
+
const occupiedArc = 360 - maxGap;
|
|
47677
|
+
if (occupiedArc > 270) return false;
|
|
47678
|
+
if (rawMax - rawMin > 180 && occupiedArc < 90) return false;
|
|
46563
47679
|
let px0 = Infinity, py0 = Infinity, px1 = -Infinity, py1 = -Infinity, anyFinite = false;
|
|
46564
47680
|
for (const [lon, lat] of ring) {
|
|
46565
47681
|
const p = project(lon, lat);
|
|
@@ -46632,7 +47748,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46632
47748
|
const regions = [];
|
|
46633
47749
|
const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
|
|
46634
47750
|
for (const [iso, f] of layerFeatures) {
|
|
46635
|
-
if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
|
|
47751
|
+
if (layerKind === "us-state" && usContext && resolved.projection === "albers-usa" && INSET_STATES.has(iso))
|
|
46636
47752
|
continue;
|
|
46637
47753
|
if (layerKind === "country" && usContext && iso === "US") continue;
|
|
46638
47754
|
if (layerKind === "country" && iso === "AQ" && !regionById.has("AQ"))
|
|
@@ -46640,11 +47756,13 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46640
47756
|
const r = regionById.get(iso);
|
|
46641
47757
|
const viewF = shouldCull ? cullFeatureToView(f) : dropFrameFillers(f);
|
|
46642
47758
|
if (!viewF) continue;
|
|
46643
|
-
const
|
|
47759
|
+
const raw = path(viewF) ?? "";
|
|
47760
|
+
const d = fitIsGlobal ? dropAntimeridianWrapSlivers(raw, width, height) : raw;
|
|
46644
47761
|
if (!d) continue;
|
|
46645
47762
|
const isThisLayer = r?.layer === layerKind;
|
|
46646
47763
|
const isForeign = layerKind === "country" && usContext && iso !== "US";
|
|
46647
|
-
|
|
47764
|
+
const baseFill = isForeign ? foreignFill : neutralFill;
|
|
47765
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? baseFill : baseFill;
|
|
46648
47766
|
let label;
|
|
46649
47767
|
let lineNumber = -1;
|
|
46650
47768
|
let layer = "base";
|
|
@@ -46653,15 +47771,21 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46653
47771
|
lineNumber = r.lineNumber;
|
|
46654
47772
|
layer = layerKind;
|
|
46655
47773
|
label = r.name;
|
|
47774
|
+
} else {
|
|
47775
|
+
label = f.properties?.name;
|
|
46656
47776
|
}
|
|
47777
|
+
const labelAnchor = WORLD_LABEL_ANCHORS[iso];
|
|
47778
|
+
const c = labelAnchor ? project(labelAnchor[0], labelAnchor[1]) : path.centroid(viewF);
|
|
47779
|
+
const hasCentroid = c != null && Number.isFinite(c[0]) && Number.isFinite(c[1]);
|
|
46657
47780
|
regions.push({
|
|
46658
47781
|
id: iso,
|
|
46659
47782
|
d,
|
|
46660
47783
|
fill: fill2,
|
|
46661
|
-
stroke: regionStroke,
|
|
47784
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46662
47785
|
lineNumber,
|
|
46663
47786
|
layer,
|
|
46664
47787
|
...label !== void 0 && { label },
|
|
47788
|
+
...hasCentroid && { labelX: c[0], labelY: c[1] },
|
|
46665
47789
|
...isThisLayer && r.value !== void 0 && { value: r.value },
|
|
46666
47790
|
...isThisLayer && Object.keys(r.tags).length > 0 && { tags: r.tags }
|
|
46667
47791
|
});
|
|
@@ -46686,9 +47810,41 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46686
47810
|
});
|
|
46687
47811
|
}
|
|
46688
47812
|
}
|
|
47813
|
+
const pointInRings = (px, py, rings) => {
|
|
47814
|
+
let inside = false;
|
|
47815
|
+
for (const ring of rings) {
|
|
47816
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
47817
|
+
const [xi, yi] = ring[i];
|
|
47818
|
+
const [xj, yj] = ring[j];
|
|
47819
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
47820
|
+
inside = !inside;
|
|
47821
|
+
}
|
|
47822
|
+
}
|
|
47823
|
+
return inside;
|
|
47824
|
+
};
|
|
47825
|
+
const fillHitTargets = [...regions, ...insetRegions].map((r) => ({
|
|
47826
|
+
fill: r.fill,
|
|
47827
|
+
rings: parsePathRings(r.d)
|
|
47828
|
+
}));
|
|
47829
|
+
const fillAt = (x, y) => {
|
|
47830
|
+
let hit = water;
|
|
47831
|
+
for (const t of fillHitTargets)
|
|
47832
|
+
if (pointInRings(x, y, t.rings)) hit = t.fill;
|
|
47833
|
+
return hit;
|
|
47834
|
+
};
|
|
47835
|
+
const labelOnFill = (fill2) => {
|
|
47836
|
+
const color = contrastRatio(fill2, palette.textOnFillDark) >= contrastRatio(fill2, palette.textOnFillLight) ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47837
|
+
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47838
|
+
return {
|
|
47839
|
+
color,
|
|
47840
|
+
halo: contrastRatio(fill2, color) < REGION_LABEL_HALO_RATIO,
|
|
47841
|
+
haloColor
|
|
47842
|
+
};
|
|
47843
|
+
};
|
|
47844
|
+
const reliefAllowed = resolved.directives.noRelief !== true;
|
|
46689
47845
|
const relief = [];
|
|
46690
47846
|
let reliefHatch = null;
|
|
46691
|
-
if (
|
|
47847
|
+
if (reliefAllowed && data.mountainRanges) {
|
|
46692
47848
|
for (const [, f] of decodeLayer(data.mountainRanges)) {
|
|
46693
47849
|
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
46694
47850
|
if (!viewF) continue;
|
|
@@ -46704,16 +47860,32 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46704
47860
|
if (relief.length) {
|
|
46705
47861
|
const darkTone = isDark ? palette.bg : palette.text;
|
|
46706
47862
|
const lightTone = isDark ? palette.text : palette.bg;
|
|
46707
|
-
const
|
|
47863
|
+
const reliefLandRef = colorizeActive ? isDark ? palette.surface : palette.bg : neutralFill;
|
|
47864
|
+
const landLum = relativeLuminance(reliefLandRef);
|
|
46708
47865
|
const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
|
|
46709
47866
|
reliefHatch = {
|
|
46710
|
-
color: mix(tone,
|
|
47867
|
+
color: mix(tone, reliefLandRef, RELIEF_HATCH_STRENGTH),
|
|
46711
47868
|
spacing: RELIEF_HATCH_SPACING,
|
|
46712
47869
|
width: RELIEF_HATCH_WIDTH
|
|
46713
47870
|
};
|
|
46714
47871
|
}
|
|
46715
47872
|
}
|
|
46716
|
-
|
|
47873
|
+
let coastlineStyle = null;
|
|
47874
|
+
if (resolved.directives.noCoastline !== true) {
|
|
47875
|
+
const minDim = Math.min(width, height);
|
|
47876
|
+
coastlineStyle = {
|
|
47877
|
+
color: mix(regionStroke, water, COASTLINE_STROKE_MIX),
|
|
47878
|
+
// N equal-width rings: distance steps outward by COASTLINE_STEP; opacity
|
|
47879
|
+
// fades linearly from NEAR (innermost) to FAR (outermost).
|
|
47880
|
+
lines: Array.from({ length: COASTLINE_RING_COUNT }, (_, k) => ({
|
|
47881
|
+
d: (COASTLINE_D0 + k * COASTLINE_STEP) * minDim,
|
|
47882
|
+
thickness: COASTLINE_THICKNESS * minDim,
|
|
47883
|
+
opacity: COASTLINE_OPACITY_NEAR + (COASTLINE_OPACITY_FAR - COASTLINE_OPACITY_NEAR) * k / (COASTLINE_RING_COUNT - 1)
|
|
47884
|
+
})),
|
|
47885
|
+
minExtent: (isGlobalView ? COASTLINE_MIN_EXTENT_GLOBAL : COASTLINE_MIN_EXTENT) * minDim
|
|
47886
|
+
};
|
|
47887
|
+
}
|
|
47888
|
+
const riverColor = mix(palette.colors.blue, water, 32);
|
|
46717
47889
|
const rivers = [];
|
|
46718
47890
|
if (data.rivers) {
|
|
46719
47891
|
for (const [, f] of decodeLayer(data.rivers)) {
|
|
@@ -46769,38 +47941,108 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46769
47941
|
const xy = project(p.lon, p.lat);
|
|
46770
47942
|
if (xy) projected.push({ p, xy });
|
|
46771
47943
|
}
|
|
46772
|
-
const
|
|
47944
|
+
const placePoi = (e, cx, cy, clusterId) => {
|
|
47945
|
+
const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
|
|
47946
|
+
poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
|
|
47947
|
+
const num = routeNumberById.get(e.p.id);
|
|
47948
|
+
pois.push({
|
|
47949
|
+
id: e.p.id,
|
|
47950
|
+
cx,
|
|
47951
|
+
cy,
|
|
47952
|
+
r: radiusFor(e.p),
|
|
47953
|
+
fill: fill2,
|
|
47954
|
+
stroke: stroke2,
|
|
47955
|
+
lineNumber: e.p.lineNumber,
|
|
47956
|
+
implicit: !!e.p.implicit,
|
|
47957
|
+
isOrigin: originIds.has(e.p.id),
|
|
47958
|
+
...num !== void 0 && { routeNumber: num },
|
|
47959
|
+
...Object.keys(e.p.tags).length > 0 && { tags: e.p.tags },
|
|
47960
|
+
...clusterId !== void 0 && { clusterId }
|
|
47961
|
+
});
|
|
47962
|
+
};
|
|
47963
|
+
const clusters = [];
|
|
47964
|
+
const connected = /* @__PURE__ */ new Set();
|
|
47965
|
+
for (const e of resolved.edges) {
|
|
47966
|
+
connected.add(e.fromId);
|
|
47967
|
+
connected.add(e.toId);
|
|
47968
|
+
}
|
|
47969
|
+
for (const rt of resolved.routes) {
|
|
47970
|
+
rt.stopIds.forEach((id) => connected.add(id));
|
|
47971
|
+
}
|
|
47972
|
+
const radiusOf = (e) => radiusFor(e.p);
|
|
46773
47973
|
for (const e of projected) {
|
|
46774
|
-
|
|
46775
|
-
|
|
46776
|
-
|
|
46777
|
-
|
|
46778
|
-
|
|
46779
|
-
|
|
46780
|
-
|
|
46781
|
-
|
|
46782
|
-
|
|
46783
|
-
|
|
46784
|
-
|
|
46785
|
-
|
|
46786
|
-
|
|
46787
|
-
|
|
46788
|
-
|
|
46789
|
-
|
|
46790
|
-
|
|
46791
|
-
|
|
46792
|
-
|
|
46793
|
-
|
|
46794
|
-
|
|
46795
|
-
|
|
46796
|
-
|
|
46797
|
-
|
|
46798
|
-
|
|
46799
|
-
|
|
46800
|
-
|
|
46801
|
-
|
|
46802
|
-
|
|
46803
|
-
|
|
47974
|
+
if (connected.has(e.p.id)) placePoi(e, e.xy[0], e.xy[1]);
|
|
47975
|
+
}
|
|
47976
|
+
const groups = [];
|
|
47977
|
+
for (const e of projected) {
|
|
47978
|
+
if (connected.has(e.p.id)) continue;
|
|
47979
|
+
const r = radiusOf(e);
|
|
47980
|
+
const near = groups.find(
|
|
47981
|
+
(g) => g.some(
|
|
47982
|
+
(q) => Math.hypot(q.xy[0] - e.xy[0], q.xy[1] - e.xy[1]) < (r + radiusOf(q)) * STACK_OVERLAP
|
|
47983
|
+
)
|
|
47984
|
+
);
|
|
47985
|
+
if (near) near.push(e);
|
|
47986
|
+
else groups.push([e]);
|
|
47987
|
+
}
|
|
47988
|
+
for (const g of groups) {
|
|
47989
|
+
if (g.length === 1) {
|
|
47990
|
+
placePoi(g[0], g[0].xy[0], g[0].xy[1]);
|
|
47991
|
+
continue;
|
|
47992
|
+
}
|
|
47993
|
+
const clusterId = g[0].p.id;
|
|
47994
|
+
const cx0 = g.reduce((s, e) => s + e.xy[0], 0) / g.length;
|
|
47995
|
+
const cy0 = g.reduce((s, e) => s + e.xy[1], 0) / g.length;
|
|
47996
|
+
const maxR = Math.max(...g.map(radiusOf));
|
|
47997
|
+
const sep = 2 * maxR + STACK_RING_GAP;
|
|
47998
|
+
const ringR = Math.max(
|
|
47999
|
+
COLO_R,
|
|
48000
|
+
sep / (2 * Math.sin(Math.PI / Math.max(g.length, 2)))
|
|
48001
|
+
);
|
|
48002
|
+
const positions = g.map((e, i) => {
|
|
48003
|
+
if (g.length <= STACK_RING_MAX) {
|
|
48004
|
+
const ang2 = -Math.PI / 2 + i * 2 * Math.PI / g.length;
|
|
48005
|
+
return {
|
|
48006
|
+
e,
|
|
48007
|
+
mx: cx0 + Math.cos(ang2) * ringR,
|
|
48008
|
+
my: cy0 + Math.sin(ang2) * ringR
|
|
48009
|
+
};
|
|
48010
|
+
}
|
|
48011
|
+
const ang = i * GOLDEN_ANGLE;
|
|
48012
|
+
const rr = ringR * Math.sqrt((i + 1) / g.length);
|
|
48013
|
+
return { e, mx: cx0 + Math.cos(ang) * rr, my: cy0 + Math.sin(ang) * rr };
|
|
48014
|
+
});
|
|
48015
|
+
let minX = cx0 - maxR;
|
|
48016
|
+
let maxX = cx0 + maxR;
|
|
48017
|
+
let minY = cy0 - maxR;
|
|
48018
|
+
let maxY = cy0 + maxR;
|
|
48019
|
+
for (const { mx, my, e } of positions) {
|
|
48020
|
+
const r = radiusOf(e);
|
|
48021
|
+
minX = Math.min(minX, mx - r);
|
|
48022
|
+
maxX = Math.max(maxX, mx + r);
|
|
48023
|
+
minY = Math.min(minY, my - r);
|
|
48024
|
+
maxY = Math.max(maxY, my + r);
|
|
48025
|
+
}
|
|
48026
|
+
let dx = 0;
|
|
48027
|
+
let dy = 0;
|
|
48028
|
+
if (minX + dx < 2) dx = 2 - minX;
|
|
48029
|
+
if (maxX + dx > width - 2) dx = width - 2 - maxX;
|
|
48030
|
+
if (minY + dy < 2) dy = 2 - minY;
|
|
48031
|
+
if (maxY + dy > height - 2) dy = height - 2 - maxY;
|
|
48032
|
+
const legsOut = [];
|
|
48033
|
+
for (const { e, mx, my } of positions) {
|
|
48034
|
+
const fx = mx + dx;
|
|
48035
|
+
const fy = my + dy;
|
|
48036
|
+
placePoi(e, fx, fy, clusterId);
|
|
48037
|
+
legsOut.push({ x2: fx, y2: fy, color: poiFill(e.p).fill });
|
|
48038
|
+
}
|
|
48039
|
+
clusters.push({
|
|
48040
|
+
id: clusterId,
|
|
48041
|
+
cx: cx0 + dx,
|
|
48042
|
+
cy: cy0 + dy,
|
|
48043
|
+
count: g.length,
|
|
48044
|
+
hitR: ringR + maxR + 6,
|
|
48045
|
+
legs: legsOut
|
|
46804
48046
|
});
|
|
46805
48047
|
}
|
|
46806
48048
|
const legs = [];
|
|
@@ -46850,16 +48092,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46850
48092
|
if (!a || !b) continue;
|
|
46851
48093
|
const mx = (a.cx + b.cx) / 2;
|
|
46852
48094
|
const my = (a.cy + b.cy) / 2;
|
|
48095
|
+
const bow = {
|
|
48096
|
+
curved: leg.style === "arc",
|
|
48097
|
+
offset: 0,
|
|
48098
|
+
labelX: mx,
|
|
48099
|
+
labelY: my - 4
|
|
48100
|
+
};
|
|
48101
|
+
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46853
48102
|
legs.push({
|
|
46854
|
-
d: legPath(a, b,
|
|
48103
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46855
48104
|
width: routeWidthFor(Number(leg.value)),
|
|
46856
48105
|
color: mix(palette.text, palette.bg, 72),
|
|
46857
48106
|
arrow: true,
|
|
46858
48107
|
lineNumber: leg.lineNumber,
|
|
46859
48108
|
...leg.label !== void 0 && {
|
|
46860
48109
|
label: leg.label,
|
|
46861
|
-
labelX:
|
|
46862
|
-
labelY:
|
|
48110
|
+
labelX: bow.labelX,
|
|
48111
|
+
labelY: bow.labelY,
|
|
48112
|
+
labelColor: routeLabelStyle.color,
|
|
48113
|
+
labelHalo: routeLabelStyle.halo,
|
|
48114
|
+
labelHaloColor: routeLabelStyle.haloColor
|
|
46863
48115
|
}
|
|
46864
48116
|
});
|
|
46865
48117
|
}
|
|
@@ -46887,20 +48139,29 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46887
48139
|
const a = poiScreen.get(e.fromId);
|
|
46888
48140
|
const b = poiScreen.get(e.toId);
|
|
46889
48141
|
if (!a || !b) return;
|
|
46890
|
-
const
|
|
46891
|
-
const offset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
48142
|
+
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
46892
48143
|
const mx = (a.cx + b.cx) / 2;
|
|
46893
48144
|
const my = (a.cy + b.cy) / 2;
|
|
48145
|
+
const bow = {
|
|
48146
|
+
curved: e.style === "arc" || n > 1,
|
|
48147
|
+
offset: fanOffset,
|
|
48148
|
+
labelX: mx,
|
|
48149
|
+
labelY: my - 4
|
|
48150
|
+
};
|
|
48151
|
+
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46894
48152
|
legs.push({
|
|
46895
|
-
d: legPath(a, b, curved, offset),
|
|
48153
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46896
48154
|
width: widthFor(e),
|
|
46897
48155
|
color: mix(palette.text, palette.bg, 66),
|
|
46898
48156
|
arrow: e.directed,
|
|
46899
48157
|
lineNumber: e.lineNumber,
|
|
46900
48158
|
...e.label !== void 0 && {
|
|
46901
48159
|
label: e.label,
|
|
46902
|
-
labelX:
|
|
46903
|
-
labelY:
|
|
48160
|
+
labelX: bow.labelX,
|
|
48161
|
+
labelY: bow.labelY,
|
|
48162
|
+
labelColor: edgeLabelStyle.color,
|
|
48163
|
+
labelHalo: edgeLabelStyle.halo,
|
|
48164
|
+
labelHaloColor: edgeLabelStyle.haloColor
|
|
46904
48165
|
}
|
|
46905
48166
|
});
|
|
46906
48167
|
});
|
|
@@ -46942,48 +48203,73 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46942
48203
|
}
|
|
46943
48204
|
}
|
|
46944
48205
|
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));
|
|
46945
|
-
const
|
|
48206
|
+
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
48207
|
+
const isCompact = width < COMPACT_WIDTH_PX;
|
|
46946
48208
|
const LABEL_PADX = 6;
|
|
46947
48209
|
const LABEL_PADY = 3;
|
|
46948
|
-
const labelW = (text) => measureLegendText(text,
|
|
46949
|
-
const labelH =
|
|
48210
|
+
const labelW = (text) => measureLegendText(text, FONT2) + 2 * LABEL_PADX;
|
|
48211
|
+
const labelH = FONT2 + 2 * LABEL_PADY;
|
|
46950
48212
|
const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
|
|
46951
|
-
const color =
|
|
46952
|
-
|
|
46953
|
-
|
|
46954
|
-
|
|
48213
|
+
const { color, haloColor } = labelOnFill(fill2);
|
|
48214
|
+
const halfW = measureLegendText(text, FONT2) / 2;
|
|
48215
|
+
const overflows = [y - FONT2 * 0.55, y - FONT2 * 0.1].some(
|
|
48216
|
+
(sy) => fillAt(x - halfW, sy) !== fill2 || fillAt(x + halfW, sy) !== fill2
|
|
46955
48217
|
);
|
|
46956
|
-
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
46957
48218
|
labels.push({
|
|
46958
48219
|
x,
|
|
46959
48220
|
y,
|
|
46960
48221
|
text,
|
|
46961
48222
|
anchor: "middle",
|
|
46962
48223
|
color,
|
|
46963
|
-
halo:
|
|
48224
|
+
halo: overflows,
|
|
46964
48225
|
haloColor,
|
|
46965
48226
|
lineNumber
|
|
46966
48227
|
});
|
|
46967
48228
|
};
|
|
46968
|
-
const
|
|
46969
|
-
|
|
46970
|
-
|
|
48229
|
+
const REGION_LABEL_GAP = 2;
|
|
48230
|
+
const regionLabelRect = (cx, cy, text) => {
|
|
48231
|
+
const w = measureLegendText(text, FONT2) + 2 * REGION_LABEL_GAP;
|
|
48232
|
+
return { x: cx - w / 2, y: cy - FONT2 / 2, w, h: FONT2 };
|
|
46971
48233
|
};
|
|
46972
|
-
if (
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
const
|
|
46976
|
-
if (!
|
|
48234
|
+
if (showRegionLabels) {
|
|
48235
|
+
const frameContainers = new Set(resolved.poiFrameContainers);
|
|
48236
|
+
const entries = regions.map((r) => {
|
|
48237
|
+
const isContainer = frameContainers.has(r.id);
|
|
48238
|
+
if (r.layer === "base" && !isContainer || r.label === void 0)
|
|
48239
|
+
return null;
|
|
48240
|
+
const isUsState = r.layer === "us-state" || r.id.startsWith("US-");
|
|
48241
|
+
const f = isUsState ? usLayer?.get(r.id) : worldLayer.get(r.id);
|
|
48242
|
+
if (!f) return null;
|
|
46977
48243
|
const [[x0, y0], [x1, y1]] = path.bounds(f);
|
|
46978
|
-
const
|
|
46979
|
-
|
|
46980
|
-
const
|
|
48244
|
+
const boxW = x1 - x0;
|
|
48245
|
+
const boxH = y1 - y0;
|
|
48246
|
+
const abbrev = isUsState ? r.id.replace(/^US-/, "") : void 0;
|
|
48247
|
+
const candidates = abbrev !== void 0 ? isCompact ? [abbrev, r.label] : [r.label, abbrev] : [r.label];
|
|
48248
|
+
const anchor = !isUsState ? WORLD_LABEL_ANCHORS[r.id] : void 0;
|
|
46981
48249
|
const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
|
|
46982
|
-
if (!c || !Number.isFinite(c[0]))
|
|
48250
|
+
if (!c || !Number.isFinite(c[0])) return null;
|
|
48251
|
+
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
48252
|
+
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
48253
|
+
const placedRegionRects = [];
|
|
48254
|
+
const POI_LABEL_PAD = 14;
|
|
48255
|
+
const poiObstacles = pois.map((p) => ({
|
|
48256
|
+
x: p.cx - p.r - POI_LABEL_PAD,
|
|
48257
|
+
y: p.cy - p.r - POI_LABEL_PAD,
|
|
48258
|
+
w: 2 * (p.r + POI_LABEL_PAD),
|
|
48259
|
+
h: 2 * (p.r + POI_LABEL_PAD)
|
|
48260
|
+
}));
|
|
48261
|
+
for (const { r, c, boxW, boxH, candidates } of entries) {
|
|
48262
|
+
const text = candidates.find((t) => {
|
|
48263
|
+
if (labelW(t) > boxW || labelH > boxH) return false;
|
|
48264
|
+
const rect = regionLabelRect(c[0], c[1], t);
|
|
48265
|
+
return !placedRegionRects.some((p) => rectsOverlap(rect, p)) && !poiObstacles.some((o) => rectsOverlap(rect, o));
|
|
48266
|
+
});
|
|
48267
|
+
if (text === void 0) continue;
|
|
48268
|
+
placedRegionRects.push(regionLabelRect(c[0], c[1], text));
|
|
46983
48269
|
pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
|
|
46984
48270
|
}
|
|
46985
48271
|
for (const seed of insetLabelSeeds) {
|
|
46986
|
-
const text =
|
|
48272
|
+
const text = isCompact ? seed.iso.replace(/^US-/, "") : seed.name;
|
|
46987
48273
|
const src = regionById.get(seed.iso);
|
|
46988
48274
|
pushRegionLabel(
|
|
46989
48275
|
seed.x,
|
|
@@ -46994,22 +48280,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46994
48280
|
);
|
|
46995
48281
|
}
|
|
46996
48282
|
}
|
|
46997
|
-
|
|
46998
|
-
|
|
46999
|
-
const ordered = [...pois].sort(
|
|
47000
|
-
(a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1)
|
|
47001
|
-
);
|
|
48283
|
+
if (resolved.directives.noPoiLabels !== true) {
|
|
48284
|
+
const ordered = [...pois].filter((p) => p.clusterId === void 0).sort((a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1));
|
|
47002
48285
|
const poiById = new Map(resolved.pois.map((q) => [q.id, q]));
|
|
47003
48286
|
const labelText = (p) => {
|
|
47004
48287
|
const src = poiById.get(p.id);
|
|
47005
48288
|
return src?.label ?? src?.name ?? p.id;
|
|
47006
48289
|
};
|
|
47007
|
-
const poiLabH =
|
|
48290
|
+
const poiLabH = FONT2 * 1.25;
|
|
47008
48291
|
const labelInfo = (p) => {
|
|
47009
48292
|
const text = labelText(p);
|
|
47010
|
-
return { text, w: measureLegendText(text,
|
|
48293
|
+
return { text, w: measureLegendText(text, FONT2) };
|
|
47011
48294
|
};
|
|
47012
48295
|
const GAP = 3;
|
|
48296
|
+
const clusterMembersById = /* @__PURE__ */ new Map();
|
|
48297
|
+
for (const p of pois) {
|
|
48298
|
+
if (p.clusterId === void 0) continue;
|
|
48299
|
+
const arr = clusterMembersById.get(p.clusterId);
|
|
48300
|
+
if (arr) arr.push(p);
|
|
48301
|
+
else clusterMembersById.set(p.clusterId, [p]);
|
|
48302
|
+
}
|
|
47013
48303
|
const inlineRect = (p, w, side) => {
|
|
47014
48304
|
switch (side) {
|
|
47015
48305
|
case "right":
|
|
@@ -47039,11 +48329,11 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47039
48329
|
const x = side === "right" ? rect.x : side === "left" ? rect.x + w : p.cx;
|
|
47040
48330
|
labels.push({
|
|
47041
48331
|
x,
|
|
47042
|
-
y: rect.y + poiLabH / 2 +
|
|
48332
|
+
y: rect.y + poiLabH / 2 + FONT2 / 3,
|
|
47043
48333
|
text,
|
|
47044
48334
|
anchor,
|
|
47045
48335
|
color: palette.text,
|
|
47046
|
-
halo:
|
|
48336
|
+
halo: false,
|
|
47047
48337
|
haloColor: palette.bg,
|
|
47048
48338
|
poiId: p.id,
|
|
47049
48339
|
lineNumber: p.lineNumber
|
|
@@ -47054,43 +48344,60 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47054
48344
|
return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
|
|
47055
48345
|
};
|
|
47056
48346
|
const GROUP_R = 30;
|
|
47057
|
-
const
|
|
48347
|
+
const groups2 = [];
|
|
47058
48348
|
for (const p of ordered) {
|
|
47059
|
-
const near =
|
|
48349
|
+
const near = groups2.find(
|
|
47060
48350
|
(g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
|
|
47061
48351
|
);
|
|
47062
48352
|
if (near) near.push(p);
|
|
47063
|
-
else
|
|
48353
|
+
else groups2.push([p]);
|
|
47064
48354
|
}
|
|
47065
48355
|
const ROW_GAP2 = 3;
|
|
47066
48356
|
const step = poiLabH + ROW_GAP2;
|
|
47067
48357
|
const COL_GAP = 16;
|
|
47068
|
-
const
|
|
47069
|
-
|
|
48358
|
+
const makeItems = (group) => group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
|
|
48359
|
+
const columnRows = (items, side) => {
|
|
47070
48360
|
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
47071
48361
|
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
47072
|
-
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
47073
48362
|
const maxW = Math.max(...items.map((o) => o.w));
|
|
47074
|
-
const
|
|
47075
|
-
const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
|
|
48363
|
+
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
48364
|
+
const colX = side === "right" ? Math.min(right + COL_GAP, width - 2 - maxW) : Math.max(left - COL_GAP, 2 + maxW);
|
|
47076
48365
|
const totalH = items.length * step;
|
|
47077
48366
|
let startY = cyMid - totalH / 2;
|
|
47078
48367
|
startY = Math.max(2, Math.min(startY, height - totalH - 2));
|
|
47079
|
-
items.
|
|
48368
|
+
return items.map((o, i) => {
|
|
47080
48369
|
const rowCy = startY + i * step + step / 2;
|
|
47081
|
-
|
|
47082
|
-
|
|
47083
|
-
|
|
47084
|
-
|
|
47085
|
-
|
|
47086
|
-
|
|
48370
|
+
return {
|
|
48371
|
+
o,
|
|
48372
|
+
colX,
|
|
48373
|
+
rowCy,
|
|
48374
|
+
rect: {
|
|
48375
|
+
x: side === "right" ? colX : colX - o.w,
|
|
48376
|
+
y: rowCy - poiLabH / 2,
|
|
48377
|
+
w: o.w,
|
|
48378
|
+
h: poiLabH
|
|
48379
|
+
}
|
|
48380
|
+
};
|
|
48381
|
+
});
|
|
48382
|
+
};
|
|
48383
|
+
const wouldColumnBeClean = (items, side) => columnRows(items, side).every(
|
|
48384
|
+
({ rect }) => rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect)
|
|
48385
|
+
);
|
|
48386
|
+
const defaultColumnSide = (items) => {
|
|
48387
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48388
|
+
const maxW = Math.max(...items.map((o) => o.w));
|
|
48389
|
+
return right + COL_GAP + maxW <= width - 2 ? "right" : "left";
|
|
48390
|
+
};
|
|
48391
|
+
const commitColumn = (items, side, clusterId) => {
|
|
48392
|
+
for (const { o, colX, rowCy, rect } of columnRows(items, side)) {
|
|
48393
|
+
obstacles.push(rect);
|
|
47087
48394
|
labels.push({
|
|
47088
48395
|
x: colX,
|
|
47089
|
-
y: rowCy +
|
|
48396
|
+
y: rowCy + FONT2 / 3,
|
|
47090
48397
|
text: o.text,
|
|
47091
48398
|
anchor: side === "right" ? "start" : "end",
|
|
47092
48399
|
color: palette.text,
|
|
47093
|
-
halo:
|
|
48400
|
+
halo: false,
|
|
47094
48401
|
haloColor: palette.bg,
|
|
47095
48402
|
leader: {
|
|
47096
48403
|
x1: o.p.cx,
|
|
@@ -47100,24 +48407,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47100
48407
|
},
|
|
47101
48408
|
leaderColor: o.p.fill,
|
|
47102
48409
|
poiId: o.p.id,
|
|
47103
|
-
lineNumber: o.p.lineNumber
|
|
48410
|
+
lineNumber: o.p.lineNumber,
|
|
48411
|
+
...clusterId !== void 0 && { clusterMember: clusterId }
|
|
47104
48412
|
});
|
|
48413
|
+
}
|
|
48414
|
+
};
|
|
48415
|
+
const pushHidden = (p) => {
|
|
48416
|
+
const { text, w } = labelInfo(p);
|
|
48417
|
+
let x = p.cx + p.r + GAP;
|
|
48418
|
+
let anchor = "start";
|
|
48419
|
+
if (x + w > width) {
|
|
48420
|
+
x = p.cx - p.r - GAP - w;
|
|
48421
|
+
anchor = "end";
|
|
48422
|
+
}
|
|
48423
|
+
const y = Math.max(0, Math.min(p.cy - poiLabH / 2, height - poiLabH));
|
|
48424
|
+
labels.push({
|
|
48425
|
+
x: anchor === "start" ? x : x + w,
|
|
48426
|
+
y: y + poiLabH / 2 + FONT2 / 3,
|
|
48427
|
+
text,
|
|
48428
|
+
anchor,
|
|
48429
|
+
color: palette.text,
|
|
48430
|
+
halo: false,
|
|
48431
|
+
haloColor: palette.bg,
|
|
48432
|
+
poiId: p.id,
|
|
48433
|
+
hidden: true,
|
|
48434
|
+
lineNumber: p.lineNumber
|
|
47105
48435
|
});
|
|
47106
48436
|
};
|
|
47107
|
-
for (const
|
|
48437
|
+
for (const [clusterId, members] of clusterMembersById) {
|
|
48438
|
+
if (members.length === 0) continue;
|
|
48439
|
+
const items = makeItems(members);
|
|
48440
|
+
const side = wouldColumnBeClean(items, "right") ? "right" : wouldColumnBeClean(items, "left") ? "left" : defaultColumnSide(items);
|
|
48441
|
+
commitColumn(items, side, clusterId);
|
|
48442
|
+
}
|
|
48443
|
+
const maxExtent = MAX_CLUSTER_EXTENT_FACTOR * Math.min(width, height);
|
|
48444
|
+
const clusterPending = [];
|
|
48445
|
+
for (const g of groups2) {
|
|
48446
|
+
const items = makeItems(g);
|
|
47108
48447
|
if (g.length === 1) {
|
|
47109
|
-
const p =
|
|
47110
|
-
const { text, w } = labelInfo(p);
|
|
48448
|
+
const { p, text, w } = items[0];
|
|
47111
48449
|
const side = ["right", "left", "above", "below"].find(
|
|
47112
48450
|
(s) => inlineFits(p, w, s)
|
|
47113
48451
|
);
|
|
47114
|
-
if (side)
|
|
47115
|
-
|
|
47116
|
-
|
|
48452
|
+
if (side) pushInline(p, text, w, side);
|
|
48453
|
+
else commitColumn(items, defaultColumnSide(items));
|
|
48454
|
+
continue;
|
|
48455
|
+
}
|
|
48456
|
+
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
48457
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48458
|
+
const minCy = Math.min(...items.map((o) => o.p.cy));
|
|
48459
|
+
const maxCy = Math.max(...items.map((o) => o.p.cy));
|
|
48460
|
+
const diag = Math.hypot(right - left, maxCy - minCy);
|
|
48461
|
+
if (diag > maxExtent || items.length > MAX_COLUMN_ROWS) {
|
|
48462
|
+
items.forEach((o) => pushHidden(o.p));
|
|
48463
|
+
} else {
|
|
48464
|
+
clusterPending.push(items);
|
|
48465
|
+
}
|
|
48466
|
+
}
|
|
48467
|
+
for (const items of clusterPending) {
|
|
48468
|
+
const side = ["right", "left"].find(
|
|
48469
|
+
(s) => wouldColumnBeClean(items, s)
|
|
48470
|
+
);
|
|
48471
|
+
if (side) commitColumn(items, side);
|
|
48472
|
+
else items.forEach((o) => pushHidden(o.p));
|
|
48473
|
+
}
|
|
48474
|
+
}
|
|
48475
|
+
if (resolved.directives.noContextLabels !== true) {
|
|
48476
|
+
for (const l of labels) {
|
|
48477
|
+
if (l.hidden) continue;
|
|
48478
|
+
const w = labelW(l.text);
|
|
48479
|
+
const x = l.anchor === "start" ? l.x : l.anchor === "end" ? l.x - w : l.x - w / 2;
|
|
48480
|
+
obstacles.push({ x, y: l.y - labelH / 2, w, h: labelH });
|
|
48481
|
+
}
|
|
48482
|
+
for (const box of insets)
|
|
48483
|
+
obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
|
|
48484
|
+
const countryCandidates = [];
|
|
48485
|
+
for (const f of worldLayer.values()) {
|
|
48486
|
+
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
48487
|
+
if (!iso || regionById.has(iso)) continue;
|
|
48488
|
+
let hasReferencedSub = false;
|
|
48489
|
+
for (const k of regionById.keys())
|
|
48490
|
+
if (k.startsWith(iso + "-")) {
|
|
48491
|
+
hasReferencedSub = true;
|
|
48492
|
+
break;
|
|
47117
48493
|
}
|
|
48494
|
+
if (hasReferencedSub) continue;
|
|
48495
|
+
const b = path.bounds(f);
|
|
48496
|
+
const [x0, y0] = b[0];
|
|
48497
|
+
const [x1, y1] = b[1];
|
|
48498
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48499
|
+
const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
|
|
48500
|
+
const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
|
|
48501
|
+
countryCandidates.push({
|
|
48502
|
+
name: f.properties?.name ?? iso,
|
|
48503
|
+
bbox: [x0, y0, x1, y1],
|
|
48504
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48505
|
+
});
|
|
48506
|
+
}
|
|
48507
|
+
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
48508
|
+
(id) => id.startsWith("US-")
|
|
48509
|
+
);
|
|
48510
|
+
if (usLayer && framedStateContainers) {
|
|
48511
|
+
const containerSet = new Set(resolved.poiFrameContainers);
|
|
48512
|
+
for (const [iso, f] of usLayer) {
|
|
48513
|
+
if (containerSet.has(iso) || regionById.has(iso)) continue;
|
|
48514
|
+
const viewF = cullFeatureToView(f);
|
|
48515
|
+
if (!viewF) continue;
|
|
48516
|
+
const b = path.bounds(viewF);
|
|
48517
|
+
const [x0, y0] = b[0];
|
|
48518
|
+
const [x1, y1] = b[1];
|
|
48519
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48520
|
+
const a = path.centroid(viewF);
|
|
48521
|
+
countryCandidates.push({
|
|
48522
|
+
name: f.properties?.name ?? iso,
|
|
48523
|
+
bbox: [x0, y0, x1, y1],
|
|
48524
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48525
|
+
});
|
|
47118
48526
|
}
|
|
47119
|
-
placeColumn(g);
|
|
47120
48527
|
}
|
|
48528
|
+
const contextLabels = placeContextLabels({
|
|
48529
|
+
projection: resolved.projection,
|
|
48530
|
+
dLonSpan,
|
|
48531
|
+
dLatSpan,
|
|
48532
|
+
width,
|
|
48533
|
+
height,
|
|
48534
|
+
waterBodies: data.waterBodies,
|
|
48535
|
+
countries: countryCandidates,
|
|
48536
|
+
palette,
|
|
48537
|
+
project,
|
|
48538
|
+
collides,
|
|
48539
|
+
// Water labels must stay over open water — `fillAt` returns the ocean
|
|
48540
|
+
// backdrop colour off-land and a region fill on-land (lakes/states count
|
|
48541
|
+
// as land here, which is the safe side for an ocean name).
|
|
48542
|
+
overLand: (x, y) => fillAt(x, y) !== water
|
|
48543
|
+
});
|
|
48544
|
+
labels.push(...contextLabels);
|
|
47121
48545
|
}
|
|
47122
48546
|
let legend = null;
|
|
47123
48547
|
if (!resolved.directives.noLegend) {
|
|
@@ -47154,60 +48578,104 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47154
48578
|
rivers,
|
|
47155
48579
|
relief,
|
|
47156
48580
|
reliefHatch,
|
|
48581
|
+
coastlineStyle,
|
|
47157
48582
|
legs,
|
|
47158
48583
|
pois,
|
|
48584
|
+
clusters,
|
|
47159
48585
|
labels,
|
|
47160
48586
|
legend,
|
|
47161
48587
|
insets,
|
|
47162
48588
|
insetRegions,
|
|
47163
48589
|
projection,
|
|
47164
|
-
stretch: stretchParams
|
|
48590
|
+
stretch: stretchParams,
|
|
48591
|
+
diagnostics: []
|
|
47165
48592
|
};
|
|
47166
48593
|
}
|
|
47167
|
-
var import_d3_geo2, import_topojson_client2, FIT_PAD,
|
|
48594
|
+
var import_d3_geo2, import_topojson_client2, 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;
|
|
47168
48595
|
var init_layout15 = __esm({
|
|
47169
48596
|
"src/map/layout.ts"() {
|
|
47170
48597
|
"use strict";
|
|
47171
48598
|
import_d3_geo2 = require("d3-geo");
|
|
47172
48599
|
import_topojson_client2 = require("topojson-client");
|
|
47173
48600
|
init_color_utils();
|
|
48601
|
+
init_geo();
|
|
48602
|
+
init_colorize();
|
|
47174
48603
|
init_colors();
|
|
47175
48604
|
init_label_layout();
|
|
47176
48605
|
init_legend_constants();
|
|
47177
48606
|
init_title_constants();
|
|
48607
|
+
init_context_labels();
|
|
47178
48608
|
FIT_PAD = 24;
|
|
47179
|
-
|
|
48609
|
+
RAMP_FLOOR2 = 15;
|
|
47180
48610
|
R_DEFAULT = 6;
|
|
47181
48611
|
R_MIN = 4;
|
|
47182
48612
|
R_MAX = 22;
|
|
47183
48613
|
W_MIN = 1.25;
|
|
47184
48614
|
W_MAX = 8;
|
|
47185
|
-
|
|
47186
|
-
|
|
48615
|
+
FONT2 = 11;
|
|
48616
|
+
WORLD_LABEL_ANCHORS = {
|
|
48617
|
+
US: [-98.5, 39.5]
|
|
48618
|
+
// CONUS geographic centre (near Lebanon, Kansas)
|
|
48619
|
+
};
|
|
48620
|
+
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
48621
|
+
MAX_COLUMN_ROWS = 7;
|
|
48622
|
+
REGION_LABEL_HALO_RATIO = 4.5;
|
|
47187
48623
|
LAND_TINT_LIGHT = 12;
|
|
47188
48624
|
LAND_TINT_DARK = 24;
|
|
47189
48625
|
TAG_TINT_LIGHT = 60;
|
|
47190
48626
|
TAG_TINT_DARK = 68;
|
|
47191
|
-
WATER_TINT_LIGHT =
|
|
47192
|
-
WATER_TINT_DARK =
|
|
48627
|
+
WATER_TINT_LIGHT = 24;
|
|
48628
|
+
WATER_TINT_DARK = 24;
|
|
47193
48629
|
RIVER_WIDTH = 1.3;
|
|
48630
|
+
COMPACT_WIDTH_PX = 480;
|
|
47194
48631
|
RELIEF_MIN_AREA = 12;
|
|
47195
48632
|
RELIEF_MIN_DIM = 2;
|
|
47196
|
-
RELIEF_HATCH_SPACING =
|
|
47197
|
-
RELIEF_HATCH_WIDTH = 0.
|
|
47198
|
-
RELIEF_HATCH_STRENGTH =
|
|
48633
|
+
RELIEF_HATCH_SPACING = 1.5;
|
|
48634
|
+
RELIEF_HATCH_WIDTH = 0.2;
|
|
48635
|
+
RELIEF_HATCH_STRENGTH = 26;
|
|
48636
|
+
COASTLINE_RING_COUNT = 5;
|
|
48637
|
+
COASTLINE_D0 = 16e-4;
|
|
48638
|
+
COASTLINE_STEP = 28e-4;
|
|
48639
|
+
COASTLINE_THICKNESS = 14e-4;
|
|
48640
|
+
COASTLINE_OPACITY_NEAR = 0.5;
|
|
48641
|
+
COASTLINE_OPACITY_FAR = 0.1;
|
|
48642
|
+
COASTLINE_MIN_EXTENT = 6e-4;
|
|
48643
|
+
COASTLINE_MIN_EXTENT_GLOBAL = 6e-4;
|
|
48644
|
+
COASTLINE_STROKE_MIX = 32;
|
|
47199
48645
|
FOREIGN_TINT_LIGHT = 30;
|
|
47200
48646
|
FOREIGN_TINT_DARK = 62;
|
|
47201
48647
|
MUTED_FOREIGN_LIGHT = 28;
|
|
47202
48648
|
MUTED_FOREIGN_DARK = 16;
|
|
47203
48649
|
COLO_R = 9;
|
|
47204
48650
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
48651
|
+
STACK_OVERLAP = 1;
|
|
48652
|
+
STACK_RING_MAX = 8;
|
|
48653
|
+
STACK_RING_GAP = 4;
|
|
47205
48654
|
FAN_STEP = 16;
|
|
47206
48655
|
ARC_CURVE_FRAC = 0.18;
|
|
48656
|
+
decodeCache = /* @__PURE__ */ new WeakMap();
|
|
47207
48657
|
usConusProjection = () => (0, import_d3_geo2.geoConicEqualArea)().parallels([29.5, 45.5]).rotate([96, 0]);
|
|
47208
48658
|
alaskaProjection = () => (0, import_d3_geo2.geoConicEqualArea)().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
|
|
47209
48659
|
hawaiiProjection = () => (0, import_d3_geo2.geoMercator)();
|
|
47210
48660
|
INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
|
|
48661
|
+
inAlaska = (lon, lat) => lat >= 51 && (lon <= -129 || lon >= 172);
|
|
48662
|
+
inHawaii = (lon, lat) => lat >= 18 && lat <= 23 && lon >= -161 && lon <= -154;
|
|
48663
|
+
FOREIGN_BORDER = {
|
|
48664
|
+
CA: [
|
|
48665
|
+
"US-AK",
|
|
48666
|
+
"US-WA",
|
|
48667
|
+
"US-ID",
|
|
48668
|
+
"US-MT",
|
|
48669
|
+
"US-ND",
|
|
48670
|
+
"US-MN",
|
|
48671
|
+
"US-MI",
|
|
48672
|
+
"US-NY",
|
|
48673
|
+
"US-VT",
|
|
48674
|
+
"US-NH",
|
|
48675
|
+
"US-ME"
|
|
48676
|
+
],
|
|
48677
|
+
MX: ["US-CA", "US-AZ", "US-NM", "US-TX"]
|
|
48678
|
+
};
|
|
47211
48679
|
US_NON_CONUS = /* @__PURE__ */ new Set([
|
|
47212
48680
|
"US-AK",
|
|
47213
48681
|
"US-HI",
|
|
@@ -47226,6 +48694,98 @@ __export(renderer_exports16, {
|
|
|
47226
48694
|
renderMap: () => renderMap,
|
|
47227
48695
|
renderMapForExport: () => renderMapForExport
|
|
47228
48696
|
});
|
|
48697
|
+
function pointInRing2(px, py, ring) {
|
|
48698
|
+
let inside = false;
|
|
48699
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
48700
|
+
const [xi, yi] = ring[i];
|
|
48701
|
+
const [xj, yj] = ring[j];
|
|
48702
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
48703
|
+
inside = !inside;
|
|
48704
|
+
}
|
|
48705
|
+
return inside;
|
|
48706
|
+
}
|
|
48707
|
+
function ringToPath(ring) {
|
|
48708
|
+
let d = "";
|
|
48709
|
+
for (let i = 0; i < ring.length; i++)
|
|
48710
|
+
d += (i ? "L" : "M") + ring[i][0] + "," + ring[i][1];
|
|
48711
|
+
return d + "Z";
|
|
48712
|
+
}
|
|
48713
|
+
function polylineToPath(pts) {
|
|
48714
|
+
let d = "";
|
|
48715
|
+
for (let i = 0; i < pts.length; i++)
|
|
48716
|
+
d += (i ? "L" : "M") + pts[i][0] + "," + pts[i][1];
|
|
48717
|
+
return d;
|
|
48718
|
+
}
|
|
48719
|
+
function ringToCoastPaths(ring, frame) {
|
|
48720
|
+
if (!frame) return [ringToPath(ring)];
|
|
48721
|
+
const n = ring.length;
|
|
48722
|
+
const eps = 0.75;
|
|
48723
|
+
const onL = (x) => Math.abs(x) <= eps;
|
|
48724
|
+
const onR = (x) => Math.abs(x - frame.w) <= eps;
|
|
48725
|
+
const onT = (y) => Math.abs(y) <= eps;
|
|
48726
|
+
const onB = (y) => Math.abs(y - frame.h) <= eps;
|
|
48727
|
+
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]);
|
|
48728
|
+
let firstBreak = -1;
|
|
48729
|
+
for (let i = 0; i < n; i++)
|
|
48730
|
+
if (isFrameEdge(ring[i], ring[(i + 1) % n])) {
|
|
48731
|
+
firstBreak = i;
|
|
48732
|
+
break;
|
|
48733
|
+
}
|
|
48734
|
+
if (firstBreak === -1) return [ringToPath(ring)];
|
|
48735
|
+
const paths = [];
|
|
48736
|
+
let cur = [];
|
|
48737
|
+
const start = (firstBreak + 1) % n;
|
|
48738
|
+
for (let k = 0; k < n; k++) {
|
|
48739
|
+
const i = (start + k) % n;
|
|
48740
|
+
const a = ring[i];
|
|
48741
|
+
const b = ring[(i + 1) % n];
|
|
48742
|
+
if (isFrameEdge(a, b)) {
|
|
48743
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48744
|
+
cur = [];
|
|
48745
|
+
continue;
|
|
48746
|
+
}
|
|
48747
|
+
if (cur.length === 0) cur.push(a);
|
|
48748
|
+
cur.push(b);
|
|
48749
|
+
}
|
|
48750
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48751
|
+
return paths;
|
|
48752
|
+
}
|
|
48753
|
+
function coastlineOuterRings(regions, minExtent, frame) {
|
|
48754
|
+
const paths = [];
|
|
48755
|
+
for (const r of regions) {
|
|
48756
|
+
const rings = parsePathRings(r.d);
|
|
48757
|
+
for (let i = 0; i < rings.length; i++) {
|
|
48758
|
+
const ring = rings[i];
|
|
48759
|
+
if (ring.length < 3) continue;
|
|
48760
|
+
let minX = Infinity;
|
|
48761
|
+
let minY = Infinity;
|
|
48762
|
+
let maxX = -Infinity;
|
|
48763
|
+
let maxY = -Infinity;
|
|
48764
|
+
for (const [x, y] of ring) {
|
|
48765
|
+
if (x < minX) minX = x;
|
|
48766
|
+
if (x > maxX) maxX = x;
|
|
48767
|
+
if (y < minY) minY = y;
|
|
48768
|
+
if (y > maxY) maxY = y;
|
|
48769
|
+
}
|
|
48770
|
+
if (Math.max(maxX - minX, maxY - minY) < minExtent) continue;
|
|
48771
|
+
const [fx, fy] = ring[0];
|
|
48772
|
+
let depth = 0;
|
|
48773
|
+
for (let j = 0; j < rings.length; j++)
|
|
48774
|
+
if (j !== i && pointInRing2(fx, fy, rings[j])) depth++;
|
|
48775
|
+
if (depth % 2 === 1) continue;
|
|
48776
|
+
paths.push(...ringToCoastPaths(ring, frame));
|
|
48777
|
+
}
|
|
48778
|
+
}
|
|
48779
|
+
return paths;
|
|
48780
|
+
}
|
|
48781
|
+
function appendWaterLines(g, outerRings, style, flatWater) {
|
|
48782
|
+
const d = outerRings.join(" ");
|
|
48783
|
+
const linesOuterFirst = [...style.lines].sort((a, b) => b.d - a.d);
|
|
48784
|
+
for (const line12 of linesOuterFirst) {
|
|
48785
|
+
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");
|
|
48786
|
+
g.append("path").attr("d", d).attr("stroke", flatWater).attr("stroke-width", 2 * line12.d).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
|
|
48787
|
+
}
|
|
48788
|
+
}
|
|
47229
48789
|
function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
|
|
47230
48790
|
d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
47231
48791
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -47238,6 +48798,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47238
48798
|
{
|
|
47239
48799
|
palette,
|
|
47240
48800
|
isDark,
|
|
48801
|
+
// Export-only: forward the contain-fit request from mapExportDimensions so a
|
|
48802
|
+
// clamped/floored (off-aspect) export canvas letterboxes instead of
|
|
48803
|
+
// stretch-distorting. The in-app preview pane passes no exportDims → unset →
|
|
48804
|
+
// keeps the global stretch-fill.
|
|
48805
|
+
preferContain: exportDims?.preferContain ?? false,
|
|
47241
48806
|
...activeGroupOverride !== void 0 && {
|
|
47242
48807
|
activeGroup: activeGroupOverride
|
|
47243
48808
|
}
|
|
@@ -47251,6 +48816,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47251
48816
|
const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
|
|
47252
48817
|
const drawRegion = (g, r, strokeWidth) => {
|
|
47253
48818
|
const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
|
|
48819
|
+
if (r.label) p.attr("data-region-name", r.label);
|
|
48820
|
+
if (r.id && r.id !== "lake") p.attr("data-iso", r.id);
|
|
48821
|
+
if (r.labelX !== void 0 && r.labelY !== void 0) {
|
|
48822
|
+
p.attr("data-label-x", r.labelX).attr("data-label-y", r.labelY);
|
|
48823
|
+
}
|
|
47254
48824
|
if (r.layer !== "base") {
|
|
47255
48825
|
p.classed("dgmo-map-region", true).attr("data-region", r.id);
|
|
47256
48826
|
if (r.value !== void 0) p.attr("data-value", r.value);
|
|
@@ -47280,28 +48850,112 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47280
48850
|
const landClip = defs.append("clipPath").attr("id", landClipId);
|
|
47281
48851
|
for (const r of layout.regions)
|
|
47282
48852
|
if (r.id !== "lake") landClip.append("path").attr("d", r.d);
|
|
47283
|
-
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");
|
|
48853
|
+
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");
|
|
47284
48854
|
for (let y = h.spacing; y < height; y += h.spacing) {
|
|
47285
48855
|
gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
|
|
47286
48856
|
}
|
|
47287
48857
|
}
|
|
48858
|
+
if (layout.coastlineStyle) {
|
|
48859
|
+
const cs = layout.coastlineStyle;
|
|
48860
|
+
const maskId = "dgmo-map-water-mask";
|
|
48861
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
48862
|
+
mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
|
|
48863
|
+
const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
|
|
48864
|
+
const lakeD = layout.regions.filter((r) => r.id === "lake").map((r) => r.d).join(" ");
|
|
48865
|
+
if (landD) mask.append("path").attr("d", landD).attr("fill", "black");
|
|
48866
|
+
if (lakeD) mask.append("path").attr("d", lakeD).attr("fill", "white");
|
|
48867
|
+
if (layout.insets.length) {
|
|
48868
|
+
const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
|
|
48869
|
+
for (const box of layout.insets) {
|
|
48870
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48871
|
+
mask.append("path").attr("d", d).attr("fill", "black").attr("stroke", "black").attr("stroke-width", 2 * reach).attr("stroke-linejoin", "round");
|
|
48872
|
+
}
|
|
48873
|
+
}
|
|
48874
|
+
const gWater = svg.append("g").attr("class", "dgmo-map-water-lines").attr("fill", "none").style("pointer-events", "none").attr("mask", `url(#${maskId})`);
|
|
48875
|
+
appendWaterLines(
|
|
48876
|
+
gWater,
|
|
48877
|
+
// Pass the canvas frame so edges collinear with it (the antimeridian on a
|
|
48878
|
+
// world map, regional clipExtent cuts) don't get ringed as fake coast —
|
|
48879
|
+
// land runs cleanly to the render-area edge.
|
|
48880
|
+
coastlineOuterRings(layout.regions, cs.minExtent, {
|
|
48881
|
+
w: width,
|
|
48882
|
+
h: height
|
|
48883
|
+
}),
|
|
48884
|
+
cs,
|
|
48885
|
+
layout.background
|
|
48886
|
+
);
|
|
48887
|
+
const byStroke = /* @__PURE__ */ new Map();
|
|
48888
|
+
for (const r of layout.regions) {
|
|
48889
|
+
const arr = byStroke.get(r.stroke);
|
|
48890
|
+
if (arr) arr.push(r.d);
|
|
48891
|
+
else byStroke.set(r.stroke, [r.d]);
|
|
48892
|
+
}
|
|
48893
|
+
for (const [stroke2, ds] of byStroke)
|
|
48894
|
+
gWater.append("path").attr("d", ds.join(" ")).attr("stroke", stroke2).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
48895
|
+
}
|
|
47288
48896
|
if (layout.rivers.length) {
|
|
47289
|
-
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
48897
|
+
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none").style("pointer-events", "none");
|
|
47290
48898
|
for (const r of layout.rivers) {
|
|
47291
48899
|
gRivers.append("path").attr("d", r.d).attr("stroke", r.color).attr("stroke-width", r.width).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
47292
48900
|
}
|
|
47293
48901
|
}
|
|
47294
48902
|
if (layout.insets.length) {
|
|
47295
48903
|
const insetG = svg.append("g").attr("class", "dgmo-map-insets");
|
|
47296
|
-
|
|
48904
|
+
layout.insets.forEach((box, bi) => {
|
|
47297
48905
|
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
47298
48906
|
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");
|
|
47299
|
-
|
|
48907
|
+
if (box.contextLand) {
|
|
48908
|
+
const clipId = `dgmo-map-inset-clip-${bi}`;
|
|
48909
|
+
defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
|
|
48910
|
+
insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
|
|
48911
|
+
}
|
|
48912
|
+
});
|
|
47300
48913
|
for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
|
|
47301
|
-
|
|
48914
|
+
if (layout.coastlineStyle) {
|
|
48915
|
+
const cs = layout.coastlineStyle;
|
|
48916
|
+
const maskId = "dgmo-map-inset-water-mask";
|
|
48917
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
48918
|
+
for (const box of layout.insets) {
|
|
48919
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48920
|
+
mask.append("path").attr("d", d).attr("fill", "white");
|
|
48921
|
+
}
|
|
48922
|
+
layout.insets.forEach((box, bi) => {
|
|
48923
|
+
if (box.contextLand)
|
|
48924
|
+
mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
|
|
48925
|
+
});
|
|
48926
|
+
for (const r of layout.insetRegions)
|
|
48927
|
+
if (r.id !== "lake")
|
|
48928
|
+
mask.append("path").attr("d", r.d).attr("fill", "black");
|
|
48929
|
+
for (const r of layout.insetRegions)
|
|
48930
|
+
if (r.id === "lake")
|
|
48931
|
+
mask.append("path").attr("d", r.d).attr("fill", "white");
|
|
48932
|
+
const clipId = "dgmo-map-inset-water-clip";
|
|
48933
|
+
const clip = defs.append("clipPath").attr("id", clipId);
|
|
48934
|
+
for (const box of layout.insets) {
|
|
48935
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48936
|
+
clip.append("path").attr("d", d);
|
|
48937
|
+
}
|
|
48938
|
+
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})`);
|
|
48939
|
+
appendWaterLines(
|
|
48940
|
+
gInsetWater,
|
|
48941
|
+
coastlineOuterRings(layout.insetRegions, cs.minExtent),
|
|
48942
|
+
cs,
|
|
48943
|
+
layout.background
|
|
48944
|
+
);
|
|
48945
|
+
for (const r of layout.insetRegions)
|
|
48946
|
+
gInsetWater.append("path").attr("d", r.d).attr("stroke", r.stroke).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
48947
|
+
}
|
|
48948
|
+
}
|
|
48949
|
+
const wireSync = (sel, lineNumber) => {
|
|
48950
|
+
if (lineNumber < 1) return;
|
|
48951
|
+
sel.attr("data-line-number", lineNumber);
|
|
48952
|
+
if (onClickItem)
|
|
48953
|
+
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
48954
|
+
};
|
|
47302
48955
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
47303
48956
|
layout.legs.forEach((leg, i) => {
|
|
47304
48957
|
const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
|
|
48958
|
+
wireSync(p, leg.lineNumber);
|
|
47305
48959
|
if (leg.arrow) {
|
|
47306
48960
|
const id = `dgmo-map-arrow-${i}`;
|
|
47307
48961
|
const s = arrowSize(leg.width);
|
|
@@ -47309,25 +48963,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47309
48963
|
p.attr("marker-end", `url(#${id})`);
|
|
47310
48964
|
}
|
|
47311
48965
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
47312
|
-
emitText(
|
|
48966
|
+
const lt = emitText(
|
|
47313
48967
|
gLegs,
|
|
47314
48968
|
leg.labelX,
|
|
47315
48969
|
leg.labelY ?? 0,
|
|
47316
48970
|
leg.label,
|
|
47317
48971
|
"middle",
|
|
47318
|
-
palette.textMuted,
|
|
47319
|
-
haloColor,
|
|
47320
|
-
true,
|
|
48972
|
+
leg.labelColor ?? palette.textMuted,
|
|
48973
|
+
leg.labelHaloColor ?? haloColor,
|
|
48974
|
+
leg.labelHalo ?? true,
|
|
47321
48975
|
LABEL_FONT - 1
|
|
47322
48976
|
);
|
|
48977
|
+
wireSync(lt, leg.lineNumber);
|
|
47323
48978
|
}
|
|
47324
48979
|
});
|
|
48980
|
+
const gSpider = svg.append("g").attr("class", "dgmo-map-spider");
|
|
48981
|
+
for (const cl of layout.clusters) {
|
|
48982
|
+
if (!exportDims) {
|
|
48983
|
+
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");
|
|
48984
|
+
}
|
|
48985
|
+
for (const leg of cl.legs) {
|
|
48986
|
+
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");
|
|
48987
|
+
}
|
|
48988
|
+
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");
|
|
48989
|
+
}
|
|
47325
48990
|
const gPois = svg.append("g").attr("class", "dgmo-map-pois");
|
|
47326
48991
|
for (const poi of layout.pois) {
|
|
47327
48992
|
if (poi.isOrigin) {
|
|
47328
48993
|
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);
|
|
47329
48994
|
}
|
|
47330
48995
|
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);
|
|
48996
|
+
if (poi.clusterId !== void 0)
|
|
48997
|
+
c.attr("data-cluster-member", poi.clusterId);
|
|
47331
48998
|
if (poi.tags) {
|
|
47332
48999
|
for (const [group, value] of Object.entries(poi.tags)) {
|
|
47333
49000
|
c.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
@@ -47355,12 +49022,32 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47355
49022
|
}
|
|
47356
49023
|
const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
|
|
47357
49024
|
for (const lab of layout.labels) {
|
|
49025
|
+
if (lab.hidden) {
|
|
49026
|
+
if (exportDims) continue;
|
|
49027
|
+
emitText(
|
|
49028
|
+
gLabels,
|
|
49029
|
+
lab.x,
|
|
49030
|
+
lab.y,
|
|
49031
|
+
lab.text,
|
|
49032
|
+
lab.anchor,
|
|
49033
|
+
lab.color,
|
|
49034
|
+
lab.haloColor,
|
|
49035
|
+
lab.halo,
|
|
49036
|
+
LABEL_FONT,
|
|
49037
|
+
lab.italic,
|
|
49038
|
+
lab.letterSpacing
|
|
49039
|
+
).attr("data-poi", lab.poiId ?? null).attr("data-poi-hidden", "").style("opacity", 0).style("pointer-events", "none");
|
|
49040
|
+
continue;
|
|
49041
|
+
}
|
|
47358
49042
|
if (lab.leader) {
|
|
47359
49043
|
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(
|
|
47360
49044
|
"stroke",
|
|
47361
49045
|
lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
|
|
47362
49046
|
).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
|
|
47363
49047
|
if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
|
|
49048
|
+
if (lab.clusterMember !== void 0)
|
|
49049
|
+
line12.attr("data-cluster-member", lab.clusterMember);
|
|
49050
|
+
wireSync(line12, lab.lineNumber);
|
|
47364
49051
|
}
|
|
47365
49052
|
const t = emitText(
|
|
47366
49053
|
gLabels,
|
|
@@ -47371,11 +49058,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47371
49058
|
lab.color,
|
|
47372
49059
|
lab.haloColor,
|
|
47373
49060
|
lab.halo,
|
|
47374
|
-
LABEL_FONT
|
|
49061
|
+
LABEL_FONT,
|
|
49062
|
+
lab.italic,
|
|
49063
|
+
lab.letterSpacing,
|
|
49064
|
+
lab.lines
|
|
47375
49065
|
);
|
|
47376
49066
|
if (lab.poiId !== void 0) {
|
|
47377
49067
|
t.attr("data-poi", lab.poiId).style("cursor", "default");
|
|
47378
49068
|
}
|
|
49069
|
+
if (lab.clusterMember !== void 0) {
|
|
49070
|
+
t.attr("data-cluster-member", lab.clusterMember);
|
|
49071
|
+
}
|
|
49072
|
+
wireSync(t, lab.lineNumber);
|
|
49073
|
+
}
|
|
49074
|
+
if (!exportDims && layout.clusters.length) {
|
|
49075
|
+
const gBadge = svg.append("g").attr("class", "dgmo-map-cluster-badges");
|
|
49076
|
+
for (const cl of layout.clusters) {
|
|
49077
|
+
const g = gBadge.append("g").attr("data-cluster", cl.id).style("opacity", 0).style("pointer-events", "none");
|
|
49078
|
+
const R = 9;
|
|
49079
|
+
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);
|
|
49080
|
+
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);
|
|
49081
|
+
emitText(
|
|
49082
|
+
g,
|
|
49083
|
+
cl.cx,
|
|
49084
|
+
cl.cy + 3,
|
|
49085
|
+
String(cl.count),
|
|
49086
|
+
"middle",
|
|
49087
|
+
palette.text,
|
|
49088
|
+
palette.bg,
|
|
49089
|
+
false,
|
|
49090
|
+
LABEL_FONT
|
|
49091
|
+
);
|
|
49092
|
+
}
|
|
47379
49093
|
}
|
|
47380
49094
|
if (layout.legend) {
|
|
47381
49095
|
const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
|
|
@@ -47412,7 +49126,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47412
49126
|
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);
|
|
47413
49127
|
}
|
|
47414
49128
|
if (layout.subtitle) {
|
|
47415
|
-
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);
|
|
49129
|
+
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);
|
|
47416
49130
|
}
|
|
47417
49131
|
if (layout.caption) {
|
|
47418
49132
|
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);
|
|
@@ -47421,10 +49135,21 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47421
49135
|
function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
|
|
47422
49136
|
renderMap(container, resolved, data, palette, isDark, void 0, exportDims);
|
|
47423
49137
|
}
|
|
47424
|
-
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
|
|
47425
|
-
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color)
|
|
49138
|
+
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic, letterSpacing, lines) {
|
|
49139
|
+
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color);
|
|
49140
|
+
if (lines && lines.length > 1) {
|
|
49141
|
+
const lineHeight = fontSize + 2;
|
|
49142
|
+
const startDy = -((lines.length - 1) / 2) * lineHeight;
|
|
49143
|
+
lines.forEach((ln, i) => {
|
|
49144
|
+
t.append("tspan").attr("x", x).attr("dy", i === 0 ? startDy : lineHeight).text(ln);
|
|
49145
|
+
});
|
|
49146
|
+
} else {
|
|
49147
|
+
t.text(text);
|
|
49148
|
+
}
|
|
49149
|
+
if (italic) t.attr("font-style", "italic");
|
|
49150
|
+
if (letterSpacing) t.attr("letter-spacing", letterSpacing);
|
|
47426
49151
|
if (withHalo) {
|
|
47427
|
-
t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width",
|
|
49152
|
+
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);
|
|
47428
49153
|
}
|
|
47429
49154
|
return t;
|
|
47430
49155
|
}
|
|
@@ -47442,6 +49167,56 @@ var init_renderer16 = __esm({
|
|
|
47442
49167
|
}
|
|
47443
49168
|
});
|
|
47444
49169
|
|
|
49170
|
+
// src/map/dimensions.ts
|
|
49171
|
+
var dimensions_exports = {};
|
|
49172
|
+
__export(dimensions_exports, {
|
|
49173
|
+
mapContentAspect: () => mapContentAspect,
|
|
49174
|
+
mapExportDimensions: () => mapExportDimensions
|
|
49175
|
+
});
|
|
49176
|
+
function mapContentAspect(resolved, data, ref = REF) {
|
|
49177
|
+
const { projection, fitTarget } = buildMapProjection(resolved, data);
|
|
49178
|
+
projection.fitSize([ref, ref], fitTarget);
|
|
49179
|
+
const b = (0, import_d3_geo3.geoPath)(projection).bounds(fitTarget);
|
|
49180
|
+
const w = b[1][0] - b[0][0];
|
|
49181
|
+
const h = b[1][1] - b[0][1];
|
|
49182
|
+
const aspect = w / h;
|
|
49183
|
+
return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
|
|
49184
|
+
}
|
|
49185
|
+
function mapExportDimensions(resolved, data, baseWidth = 1200) {
|
|
49186
|
+
const raw = mapContentAspect(resolved, data);
|
|
49187
|
+
const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
|
|
49188
|
+
const width = baseWidth;
|
|
49189
|
+
let height = Math.round(width / clamped);
|
|
49190
|
+
let chromeReserve = 0;
|
|
49191
|
+
if (resolved.title && resolved.pois.length > 0) {
|
|
49192
|
+
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
49193
|
+
chromeReserve += Math.max(FIT_PAD2, bannerBottom + TITLE_GAP) - FIT_PAD2;
|
|
49194
|
+
}
|
|
49195
|
+
let floored = false;
|
|
49196
|
+
if (height - chromeReserve < MIN_MAP_BAND) {
|
|
49197
|
+
height = Math.round(chromeReserve + MIN_MAP_BAND);
|
|
49198
|
+
floored = true;
|
|
49199
|
+
}
|
|
49200
|
+
const preferContain = clamped !== raw || floored;
|
|
49201
|
+
return { width, height, preferContain };
|
|
49202
|
+
}
|
|
49203
|
+
var import_d3_geo3, FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
|
|
49204
|
+
var init_dimensions = __esm({
|
|
49205
|
+
"src/map/dimensions.ts"() {
|
|
49206
|
+
"use strict";
|
|
49207
|
+
import_d3_geo3 = require("d3-geo");
|
|
49208
|
+
init_title_constants();
|
|
49209
|
+
init_layout15();
|
|
49210
|
+
FIT_PAD2 = 24;
|
|
49211
|
+
TITLE_GAP = 16;
|
|
49212
|
+
ASPECT_MAX = 3;
|
|
49213
|
+
ASPECT_MIN = 0.9;
|
|
49214
|
+
MIN_MAP_BAND = 200;
|
|
49215
|
+
FALLBACK_ASPECT = 1.5;
|
|
49216
|
+
REF = 1e3;
|
|
49217
|
+
}
|
|
49218
|
+
});
|
|
49219
|
+
|
|
47445
49220
|
// src/map/load-data.ts
|
|
47446
49221
|
var load_data_exports = {};
|
|
47447
49222
|
__export(load_data_exports, {
|
|
@@ -47500,12 +49275,17 @@ function loadMapData() {
|
|
|
47500
49275
|
mountainRanges,
|
|
47501
49276
|
naLand,
|
|
47502
49277
|
naLakes,
|
|
49278
|
+
waterBodies,
|
|
47503
49279
|
gazetteer
|
|
47504
49280
|
] = await Promise.all([
|
|
49281
|
+
// worldCoarse (110m) is LOAD-BEARING but NOT a render source: the world
|
|
49282
|
+
// basemap renders from worldDetail (50m) at all scales (resolver pins
|
|
49283
|
+
// basemaps.world = 'detail'). Coarse stays as the authoritative region
|
|
49284
|
+
// name index + dominant-landmass bbox source in resolver.ts. Do not drop it.
|
|
47505
49285
|
readJson(nb, dir, FILES.worldCoarse),
|
|
47506
49286
|
readJson(nb, dir, FILES.worldDetail),
|
|
47507
49287
|
readJson(nb, dir, FILES.usStates),
|
|
47508
|
-
// Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
|
|
49288
|
+
// Lakes/rivers/mountain/NA/water assets are optional — older bundles may predate them.
|
|
47509
49289
|
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
47510
49290
|
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
47511
49291
|
readJson(nb, dir, FILES.mountainRanges).catch(
|
|
@@ -47513,6 +49293,7 @@ function loadMapData() {
|
|
|
47513
49293
|
),
|
|
47514
49294
|
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
47515
49295
|
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
49296
|
+
readJson(nb, dir, FILES.waterBodies).catch(() => void 0),
|
|
47516
49297
|
readJson(nb, dir, FILES.gazetteer)
|
|
47517
49298
|
]);
|
|
47518
49299
|
return validate({
|
|
@@ -47524,7 +49305,8 @@ function loadMapData() {
|
|
|
47524
49305
|
...rivers && { rivers },
|
|
47525
49306
|
...mountainRanges && { mountainRanges },
|
|
47526
49307
|
...naLand && { naLand },
|
|
47527
|
-
...naLakes && { naLakes }
|
|
49308
|
+
...naLakes && { naLakes },
|
|
49309
|
+
...waterBodies && { waterBodies }
|
|
47528
49310
|
});
|
|
47529
49311
|
})().catch((e) => {
|
|
47530
49312
|
cache = void 0;
|
|
@@ -47546,6 +49328,7 @@ var init_load_data = __esm({
|
|
|
47546
49328
|
mountainRanges: "mountain-ranges.json",
|
|
47547
49329
|
naLand: "na-land.json",
|
|
47548
49330
|
naLakes: "na-lakes.json",
|
|
49331
|
+
waterBodies: "water-bodies.json",
|
|
47549
49332
|
gazetteer: "gazetteer.json"
|
|
47550
49333
|
};
|
|
47551
49334
|
CANDIDATE_DIRS = [
|
|
@@ -49558,8 +51341,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
49558
51341
|
const lines = splitParticipantLabel(p.label, LABEL_MAX_CHARS);
|
|
49559
51342
|
if (lines.length === 0) continue;
|
|
49560
51343
|
const widest = Math.max(...lines.map((l) => l.length));
|
|
49561
|
-
const
|
|
49562
|
-
uniformBoxWidth = Math.max(uniformBoxWidth,
|
|
51344
|
+
const labelWidth2 = widest * LABEL_CHAR_WIDTH + 10;
|
|
51345
|
+
uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
|
|
49563
51346
|
}
|
|
49564
51347
|
uniformBoxWidth = Math.min(MAX_BOX_WIDTH, uniformBoxWidth);
|
|
49565
51348
|
const effectiveGap = Math.max(PARTICIPANT_GAP, uniformBoxWidth + 30);
|
|
@@ -52254,15 +54037,15 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
52254
54037
|
textColor,
|
|
52255
54038
|
onClickItem
|
|
52256
54039
|
);
|
|
52257
|
-
const
|
|
52258
|
-
for (const node of nodes)
|
|
54040
|
+
const neighbors2 = /* @__PURE__ */ new Map();
|
|
54041
|
+
for (const node of nodes) neighbors2.set(node, /* @__PURE__ */ new Set());
|
|
52259
54042
|
for (const link of links) {
|
|
52260
|
-
|
|
52261
|
-
|
|
54043
|
+
neighbors2.get(link.source).add(link.target);
|
|
54044
|
+
neighbors2.get(link.target).add(link.source);
|
|
52262
54045
|
}
|
|
52263
54046
|
const FADE_OPACITY3 = 0.1;
|
|
52264
54047
|
function handleMouseEnter(hovered) {
|
|
52265
|
-
const connected =
|
|
54048
|
+
const connected = neighbors2.get(hovered);
|
|
52266
54049
|
g.selectAll(".arc-link").each(function() {
|
|
52267
54050
|
const el = d3Selection23.select(this);
|
|
52268
54051
|
const src = el.attr("data-source");
|
|
@@ -53170,10 +54953,12 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
|
|
|
53170
54953
|
const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
|
|
53171
54954
|
const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
|
|
53172
54955
|
const innerWidth = width - margin.left - margin.right;
|
|
53173
|
-
const
|
|
53174
|
-
const rowH = Math.min(ctx.structural(28),
|
|
54956
|
+
const availInnerHeight = height - margin.top - margin.bottom;
|
|
54957
|
+
const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
|
|
54958
|
+
const innerHeight = rowH * sorted.length;
|
|
54959
|
+
const usedHeight = margin.top + innerHeight + margin.bottom;
|
|
53175
54960
|
const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
53176
|
-
const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height",
|
|
54961
|
+
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);
|
|
53177
54962
|
if (ctx.isBelowFloor) {
|
|
53178
54963
|
svg.attr("width", "100%");
|
|
53179
54964
|
}
|
|
@@ -54197,7 +55982,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54197
55982
|
8,
|
|
54198
55983
|
Math.floor(OVERLAP_WRAP_TARGET_W / OVERLAP_CH_W)
|
|
54199
55984
|
);
|
|
54200
|
-
function
|
|
55985
|
+
function wrapLabel3(text, maxChars) {
|
|
54201
55986
|
const words = text.split(/\s+/).filter(Boolean);
|
|
54202
55987
|
const lines = [];
|
|
54203
55988
|
let cur = "";
|
|
@@ -54243,7 +56028,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54243
56028
|
if (!ov.label) continue;
|
|
54244
56029
|
const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
|
|
54245
56030
|
if (idxs.some((idx) => idx < 0)) continue;
|
|
54246
|
-
const lines =
|
|
56031
|
+
const lines = wrapLabel3(ov.label, MAX_WRAP_CHARS);
|
|
54247
56032
|
wrappedOverlapLabels.set(ov, lines);
|
|
54248
56033
|
const dir = predictOverlapDirRaw(idxs);
|
|
54249
56034
|
const longest = lines.reduce((m, l) => Math.max(m, l.length), 0);
|
|
@@ -55681,6 +57466,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55681
57466
|
const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
|
|
55682
57467
|
const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
|
|
55683
57468
|
const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
|
|
57469
|
+
const { mapExportDimensions: mapExportDimensions2 } = await Promise.resolve().then(() => (init_dimensions(), dimensions_exports));
|
|
55684
57470
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
55685
57471
|
const mapParsed = parseMap2(content);
|
|
55686
57472
|
let mapData = options?.mapData;
|
|
@@ -55693,14 +57479,15 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55693
57479
|
}
|
|
55694
57480
|
}
|
|
55695
57481
|
const mapResolved = resolveMap2(mapParsed, mapData);
|
|
55696
|
-
const
|
|
57482
|
+
const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
|
|
57483
|
+
const container2 = createExportContainer(dims2.width, dims2.height);
|
|
55697
57484
|
renderMapForExport2(
|
|
55698
57485
|
container2,
|
|
55699
57486
|
mapResolved,
|
|
55700
57487
|
mapData,
|
|
55701
57488
|
effectivePalette2,
|
|
55702
57489
|
theme === "dark",
|
|
55703
|
-
|
|
57490
|
+
dims2
|
|
55704
57491
|
);
|
|
55705
57492
|
return finalizeSvgExport(container2, theme, effectivePalette2);
|
|
55706
57493
|
}
|
|
@@ -56561,7 +58348,8 @@ async function render(content, options) {
|
|
|
56561
58348
|
...options?.c4Container !== void 0 && {
|
|
56562
58349
|
c4Container: options.c4Container
|
|
56563
58350
|
},
|
|
56564
|
-
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup }
|
|
58351
|
+
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
|
|
58352
|
+
...options?.mapData !== void 0 && { mapData: options.mapData }
|
|
56565
58353
|
});
|
|
56566
58354
|
if (chartType === "map") {
|
|
56567
58355
|
try {
|
|
@@ -56572,7 +58360,7 @@ async function render(content, options) {
|
|
|
56572
58360
|
Promise.resolve().then(() => (init_load_data(), load_data_exports))
|
|
56573
58361
|
]
|
|
56574
58362
|
);
|
|
56575
|
-
const data = await loadMapData2();
|
|
58363
|
+
const data = options?.mapData ?? await loadMapData2();
|
|
56576
58364
|
diagnostics = [...resolveMap2(parseMap2(content), data).diagnostics];
|
|
56577
58365
|
} catch {
|
|
56578
58366
|
}
|
|
@@ -56701,6 +58489,9 @@ var DIRECTIVE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
56701
58489
|
"hide",
|
|
56702
58490
|
"mode",
|
|
56703
58491
|
"direction",
|
|
58492
|
+
// Boxes-and-lines
|
|
58493
|
+
"box-metric",
|
|
58494
|
+
"show-values",
|
|
56704
58495
|
// ER
|
|
56705
58496
|
"notation",
|
|
56706
58497
|
// Class
|
|
@@ -56740,22 +58531,20 @@ var DIRECTIVE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
56740
58531
|
// Sequence
|
|
56741
58532
|
"activations",
|
|
56742
58533
|
"no-activations",
|
|
56743
|
-
// Map (§24B) directives
|
|
56744
|
-
"region",
|
|
56745
|
-
"projection",
|
|
58534
|
+
// Map (§24B) directives — cosmetics on by default, bare `no-*` opt-outs
|
|
56746
58535
|
"region-metric",
|
|
56747
58536
|
"poi-metric",
|
|
56748
58537
|
"flow-metric",
|
|
56749
|
-
"
|
|
56750
|
-
"
|
|
56751
|
-
"default-country",
|
|
56752
|
-
"default-state",
|
|
56753
|
-
"no-legend",
|
|
56754
|
-
"no-insets",
|
|
56755
|
-
"muted",
|
|
56756
|
-
"natural",
|
|
56757
|
-
"subtitle",
|
|
58538
|
+
"locale",
|
|
58539
|
+
"active-tag",
|
|
56758
58540
|
"caption",
|
|
58541
|
+
"no-legend",
|
|
58542
|
+
"no-coastline",
|
|
58543
|
+
"no-relief",
|
|
58544
|
+
"no-context-labels",
|
|
58545
|
+
"no-region-labels",
|
|
58546
|
+
"no-poi-labels",
|
|
58547
|
+
"no-colorize",
|
|
56759
58548
|
"poi",
|
|
56760
58549
|
"route",
|
|
56761
58550
|
// Data charts
|
|
@@ -57048,7 +58837,11 @@ var ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
|
|
|
57048
58837
|
"collapsed",
|
|
57049
58838
|
"tech",
|
|
57050
58839
|
"span",
|
|
57051
|
-
"split"
|
|
58840
|
+
"split",
|
|
58841
|
+
// Map (§24B) reserved keys
|
|
58842
|
+
"value",
|
|
58843
|
+
"label",
|
|
58844
|
+
"style"
|
|
57052
58845
|
]);
|
|
57053
58846
|
function applyAttributeKeys(tokens) {
|
|
57054
58847
|
for (let i = 0; i < tokens.length - 1; i++) {
|
|
@@ -57421,7 +59214,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
|
|
|
57421
59214
|
|
|
57422
59215
|
// src/auto/index.ts
|
|
57423
59216
|
init_safe_href();
|
|
57424
|
-
var VERSION = "0.
|
|
59217
|
+
var VERSION = "0.23.0";
|
|
57425
59218
|
var DEFAULTS = {
|
|
57426
59219
|
theme: "auto",
|
|
57427
59220
|
palette: "nord",
|