@diagrammo/dgmo 0.21.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/dist/advanced.cjs +2230 -503
- package/dist/advanced.d.cts +5731 -0
- package/dist/advanced.d.ts +5731 -0
- package/dist/advanced.js +2226 -503
- package/dist/auto.cjs +2272 -479
- package/dist/auto.d.cts +39 -0
- package/dist/auto.d.ts +39 -0
- package/dist/auto.js +124 -124
- package/dist/auto.mjs +2274 -480
- package/dist/cli.cjs +170 -170
- package/dist/editor.cjs +16 -16
- package/dist/editor.js +16 -16
- package/dist/highlight.cjs +18 -13
- package/dist/highlight.js +18 -13
- package/dist/index.cjs +2253 -465
- package/dist/index.d.cts +339 -0
- package/dist/index.d.ts +339 -0
- package/dist/index.js +2255 -466
- package/dist/internal.cjs +2230 -503
- package/dist/internal.d.cts +5731 -0
- package/dist/internal.d.ts +5731 -0
- package/dist/internal.js +2226 -503
- package/dist/map-data/PROVENANCE.json +1 -1
- package/dist/map-data/gazetteer.json +1 -1
- package/dist/map-data/mountain-ranges.json +1 -1
- package/dist/map-data/water-bodies.json +1 -0
- package/dist/map-data/world-coarse.json +1 -1
- package/dist/map-data/world-detail.json +1 -1
- package/docs/language-reference.md +55 -9
- package/gallery/fixtures/boxes-and-lines.dgmo +6 -4
- package/gallery/fixtures/map-categorical-world.dgmo +16 -0
- package/gallery/fixtures/map-categorical.dgmo +0 -1
- package/gallery/fixtures/map-choropleth.dgmo +0 -1
- package/gallery/fixtures/map-coastline.dgmo +7 -0
- package/gallery/fixtures/map-colorize.dgmo +11 -0
- package/gallery/fixtures/map-direct-color.dgmo +0 -1
- package/gallery/fixtures/map-reference-world.dgmo +11 -0
- package/gallery/fixtures/map-region-scope.dgmo +0 -3
- package/gallery/fixtures/map-route.dgmo +0 -1
- package/package.json +1 -1
- package/src/advanced.ts +12 -1
- package/src/boxes-and-lines/parser.ts +39 -0
- package/src/boxes-and-lines/renderer.ts +205 -20
- package/src/boxes-and-lines/types.ts +9 -0
- package/src/cli.ts +1 -1
- package/src/completion.ts +36 -30
- package/src/cycle/renderer.ts +14 -1
- package/src/d3.ts +20 -6
- package/src/editor/highlight-api.ts +4 -0
- package/src/editor/keywords.ts +16 -16
- package/src/infra/renderer.ts +35 -7
- package/src/map/colorize.ts +54 -0
- package/src/map/context-labels.ts +429 -0
- package/src/map/data/PROVENANCE.json +1 -1
- package/src/map/data/README.md +6 -0
- package/src/map/data/gazetteer.json +1 -1
- package/src/map/data/mountain-ranges.json +1 -1
- package/src/map/data/types.ts +34 -0
- package/src/map/data/water-bodies.json +1 -0
- package/src/map/data/world-coarse.json +1 -1
- package/src/map/data/world-detail.json +1 -1
- package/src/map/dimensions.ts +117 -0
- package/src/map/geo-query.ts +21 -3
- package/src/map/geo.ts +47 -1
- package/src/map/layout.ts +1408 -266
- package/src/map/load-data.ts +10 -2
- package/src/map/parser.ts +42 -116
- package/src/map/renderer.ts +604 -14
- package/src/map/resolved-types.ts +16 -2
- package/src/map/resolver.ts +208 -59
- package/src/map/types.ts +30 -32
- package/src/mindmap/renderer.ts +10 -1
- package/src/palettes/atlas.ts +77 -0
- package/src/palettes/blueprint.ts +73 -0
- package/src/palettes/color-utils.ts +58 -1
- package/src/palettes/index.ts +12 -3
- package/src/palettes/slate.ts +73 -0
- package/src/palettes/tidewater.ts +73 -0
- package/src/render.ts +8 -1
- package/src/tech-radar/renderer.ts +3 -0
- package/src/tech-radar/types.ts +3 -0
- package/src/utils/d3-types.ts +5 -0
- package/src/utils/legend-layout.ts +21 -4
- package/src/utils/legend-types.ts +7 -0
- package/src/utils/reserved-key-registry.ts +8 -3
- package/src/palettes/bold.ts +0 -67
package/dist/index.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",
|
|
@@ -838,6 +838,9 @@ var init_reserved_key_registry = __esm({
|
|
|
838
838
|
"value",
|
|
839
839
|
"label",
|
|
840
840
|
"style"
|
|
841
|
+
// `surface:` was removed in the 2026-06-02 defaults-on review — it is no longer
|
|
842
|
+
// a recognized metadata key (the route/edge surface feature was cut; §24B.7).
|
|
843
|
+
// A stray `surface: water` is no longer captured as a reserved key.
|
|
841
844
|
]);
|
|
842
845
|
ORG_REGISTRY = staticRegistry([
|
|
843
846
|
"color",
|
|
@@ -892,9 +895,7 @@ var init_reserved_key_registry = __esm({
|
|
|
892
895
|
BOXES_AND_LINES_REGISTRY = staticRegistry([
|
|
893
896
|
"color",
|
|
894
897
|
"description",
|
|
895
|
-
"
|
|
896
|
-
"split",
|
|
897
|
-
"fanout"
|
|
898
|
+
"value"
|
|
898
899
|
]);
|
|
899
900
|
TIMELINE_REGISTRY = staticRegistry([
|
|
900
901
|
"color",
|
|
@@ -1900,77 +1901,266 @@ function getSegmentColors(palette, count) {
|
|
|
1900
1901
|
(_, i) => hslToHex(Math.round((startHue + i * step) % 360), avgS, avgL)
|
|
1901
1902
|
);
|
|
1902
1903
|
}
|
|
1904
|
+
function politicalTints(palette, count, isDark) {
|
|
1905
|
+
if (count <= 0) return [];
|
|
1906
|
+
const base = isDark ? palette.surface : palette.bg;
|
|
1907
|
+
const c = palette.colors;
|
|
1908
|
+
const swatches = [
|
|
1909
|
+
.../* @__PURE__ */ new Set([
|
|
1910
|
+
c.green,
|
|
1911
|
+
c.yellow,
|
|
1912
|
+
c.orange,
|
|
1913
|
+
c.purple,
|
|
1914
|
+
c.red,
|
|
1915
|
+
c.teal,
|
|
1916
|
+
c.cyan,
|
|
1917
|
+
c.blue
|
|
1918
|
+
])
|
|
1919
|
+
];
|
|
1920
|
+
const bands = isDark ? POLITICAL_TINT_BANDS.dark : POLITICAL_TINT_BANDS.light;
|
|
1921
|
+
const out = [];
|
|
1922
|
+
for (const pct of bands) {
|
|
1923
|
+
if (out.length >= count) break;
|
|
1924
|
+
for (const s of swatches) out.push(mix(s, base, pct));
|
|
1925
|
+
}
|
|
1926
|
+
return out.slice(0, count);
|
|
1927
|
+
}
|
|
1928
|
+
var POLITICAL_TINT_BANDS;
|
|
1903
1929
|
var init_color_utils = __esm({
|
|
1904
1930
|
"src/palettes/color-utils.ts"() {
|
|
1905
1931
|
"use strict";
|
|
1932
|
+
POLITICAL_TINT_BANDS = {
|
|
1933
|
+
light: [32, 48, 64, 80],
|
|
1934
|
+
dark: [44, 58, 72, 86]
|
|
1935
|
+
};
|
|
1906
1936
|
}
|
|
1907
1937
|
});
|
|
1908
1938
|
|
|
1909
|
-
// src/palettes/
|
|
1910
|
-
var
|
|
1911
|
-
var
|
|
1912
|
-
"src/palettes/
|
|
1939
|
+
// src/palettes/atlas.ts
|
|
1940
|
+
var atlasPalette;
|
|
1941
|
+
var init_atlas = __esm({
|
|
1942
|
+
"src/palettes/atlas.ts"() {
|
|
1913
1943
|
"use strict";
|
|
1914
1944
|
init_registry();
|
|
1915
|
-
|
|
1916
|
-
id: "
|
|
1917
|
-
name: "
|
|
1945
|
+
atlasPalette = {
|
|
1946
|
+
id: "atlas",
|
|
1947
|
+
name: "Atlas",
|
|
1918
1948
|
light: {
|
|
1919
|
-
bg: "#
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1949
|
+
bg: "#f3ead3",
|
|
1950
|
+
// warm manila / parchment
|
|
1951
|
+
surface: "#ece0c0",
|
|
1952
|
+
// deeper paper (cards, panels)
|
|
1953
|
+
overlay: "#e8dab8",
|
|
1954
|
+
// popovers, dropdowns
|
|
1955
|
+
border: "#bcaa86",
|
|
1956
|
+
// muted sepia rule line
|
|
1957
|
+
text: "#463a26",
|
|
1958
|
+
// aged sepia-brown ink
|
|
1959
|
+
textMuted: "#7a6a4f",
|
|
1960
|
+
// faded annotation ink
|
|
1961
|
+
textOnFillLight: "#f7f1de",
|
|
1962
|
+
// parchment (light text on dark fills)
|
|
1963
|
+
textOnFillDark: "#3a2e1c",
|
|
1964
|
+
// deep ink (dark text on light fills)
|
|
1965
|
+
primary: "#5b7a99",
|
|
1966
|
+
// pull-down map ocean (steel-blue)
|
|
1967
|
+
secondary: "#7e9a6f",
|
|
1968
|
+
// lowland sage / celadon
|
|
1969
|
+
accent: "#b07f7c",
|
|
1970
|
+
// dusty rose
|
|
1971
|
+
destructive: "#b25a45",
|
|
1972
|
+
// brick / terracotta
|
|
1931
1973
|
colors: {
|
|
1932
|
-
red: "#
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1974
|
+
red: "#bf6a52",
|
|
1975
|
+
// terracotta brick
|
|
1976
|
+
orange: "#cf9a5c",
|
|
1977
|
+
// map tan / ochre
|
|
1978
|
+
yellow: "#cdb35e",
|
|
1979
|
+
// straw / muted lemon
|
|
1980
|
+
green: "#7e9a6f",
|
|
1981
|
+
// sage / celadon lowland
|
|
1982
|
+
blue: "#5b7a99",
|
|
1983
|
+
// steel-blue ocean
|
|
1984
|
+
purple: "#9a7fa6",
|
|
1985
|
+
// dusty lilac / mauve
|
|
1986
|
+
teal: "#6fa094",
|
|
1987
|
+
// muted seafoam
|
|
1988
|
+
cyan: "#79a7b5",
|
|
1989
|
+
// shallow-water blue
|
|
1990
|
+
gray: "#8a7d68",
|
|
1991
|
+
// warm taupe
|
|
1992
|
+
black: "#463a26",
|
|
1993
|
+
// ink
|
|
1994
|
+
white: "#ece0c0"
|
|
1995
|
+
// paper
|
|
1943
1996
|
}
|
|
1944
1997
|
},
|
|
1945
1998
|
dark: {
|
|
1946
|
-
bg: "#
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1999
|
+
bg: "#1e2a33",
|
|
2000
|
+
// deep map ocean (night globe)
|
|
2001
|
+
surface: "#27353f",
|
|
2002
|
+
// raised ocean
|
|
2003
|
+
overlay: "#2e3d48",
|
|
2004
|
+
// popovers, dropdowns
|
|
2005
|
+
border: "#3d4f5c",
|
|
2006
|
+
// depth-contour line
|
|
2007
|
+
text: "#e8dcc0",
|
|
2008
|
+
// parchment ink, inverted
|
|
2009
|
+
textMuted: "#a89a7d",
|
|
2010
|
+
// faded label
|
|
2011
|
+
textOnFillLight: "#f7f1de",
|
|
2012
|
+
// parchment
|
|
2013
|
+
textOnFillDark: "#1a242c",
|
|
2014
|
+
// deep ocean ink
|
|
2015
|
+
primary: "#7ba0bf",
|
|
2016
|
+
// brighter ocean
|
|
2017
|
+
secondary: "#9bb588",
|
|
2018
|
+
// sage, lifted
|
|
2019
|
+
accent: "#cf9a96",
|
|
2020
|
+
// dusty rose, lifted
|
|
2021
|
+
destructive: "#c9745c",
|
|
2022
|
+
// brick, lifted
|
|
1958
2023
|
colors: {
|
|
1959
|
-
red: "#
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2024
|
+
red: "#cf7a60",
|
|
2025
|
+
// terracotta
|
|
2026
|
+
orange: "#d9a96a",
|
|
2027
|
+
// tan / ochre
|
|
2028
|
+
yellow: "#d8c074",
|
|
2029
|
+
// straw
|
|
2030
|
+
green: "#9bb588",
|
|
2031
|
+
// sage lowland
|
|
2032
|
+
blue: "#7ba0bf",
|
|
2033
|
+
// ocean
|
|
2034
|
+
purple: "#b59ac0",
|
|
2035
|
+
// lilac / mauve
|
|
2036
|
+
teal: "#85b3a6",
|
|
2037
|
+
// seafoam
|
|
2038
|
+
cyan: "#92bccb",
|
|
2039
|
+
// shallow-water blue
|
|
2040
|
+
gray: "#9a8d76",
|
|
2041
|
+
// warm taupe
|
|
2042
|
+
black: "#27353f",
|
|
2043
|
+
// raised ocean
|
|
2044
|
+
white: "#e8dcc0"
|
|
2045
|
+
// parchment
|
|
1970
2046
|
}
|
|
1971
2047
|
}
|
|
1972
2048
|
};
|
|
1973
|
-
registerPalette(
|
|
2049
|
+
registerPalette(atlasPalette);
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
// src/palettes/blueprint.ts
|
|
2054
|
+
var blueprintPalette;
|
|
2055
|
+
var init_blueprint = __esm({
|
|
2056
|
+
"src/palettes/blueprint.ts"() {
|
|
2057
|
+
"use strict";
|
|
2058
|
+
init_registry();
|
|
2059
|
+
blueprintPalette = {
|
|
2060
|
+
id: "blueprint",
|
|
2061
|
+
name: "Blueprint",
|
|
2062
|
+
light: {
|
|
2063
|
+
bg: "#f4f8fb",
|
|
2064
|
+
// pale drafting white (faint cyan)
|
|
2065
|
+
surface: "#e6eef4",
|
|
2066
|
+
// drafting panel
|
|
2067
|
+
overlay: "#dde9f1",
|
|
2068
|
+
// popovers, dropdowns
|
|
2069
|
+
border: "#aac3d6",
|
|
2070
|
+
// pale blue grid line
|
|
2071
|
+
text: "#123a5e",
|
|
2072
|
+
// blueprint navy ink
|
|
2073
|
+
textMuted: "#4f7390",
|
|
2074
|
+
// faint draft note
|
|
2075
|
+
textOnFillLight: "#f4f8fb",
|
|
2076
|
+
// drafting white
|
|
2077
|
+
textOnFillDark: "#0c2f4d",
|
|
2078
|
+
// deep blueprint navy
|
|
2079
|
+
primary: "#1f5e8c",
|
|
2080
|
+
// blueprint blue
|
|
2081
|
+
secondary: "#5b7d96",
|
|
2082
|
+
// steel
|
|
2083
|
+
accent: "#b08a3e",
|
|
2084
|
+
// draftsman's ochre highlight
|
|
2085
|
+
destructive: "#c0504d",
|
|
2086
|
+
// correction red
|
|
2087
|
+
colors: {
|
|
2088
|
+
red: "#c25a4e",
|
|
2089
|
+
// correction red
|
|
2090
|
+
orange: "#c2823e",
|
|
2091
|
+
// ochre
|
|
2092
|
+
yellow: "#c2a843",
|
|
2093
|
+
// pencil gold
|
|
2094
|
+
green: "#4f8a6b",
|
|
2095
|
+
// drafting green
|
|
2096
|
+
blue: "#1f5e8c",
|
|
2097
|
+
// blueprint blue
|
|
2098
|
+
purple: "#6f5e96",
|
|
2099
|
+
// indigo pencil
|
|
2100
|
+
teal: "#3a8a8a",
|
|
2101
|
+
// teal
|
|
2102
|
+
cyan: "#3f8fb5",
|
|
2103
|
+
// cyan
|
|
2104
|
+
gray: "#7e8e98",
|
|
2105
|
+
// graphite
|
|
2106
|
+
black: "#123a5e",
|
|
2107
|
+
// navy ink
|
|
2108
|
+
white: "#e6eef4"
|
|
2109
|
+
// panel
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2112
|
+
dark: {
|
|
2113
|
+
bg: "#103a5e",
|
|
2114
|
+
// deep blueprint blue (cyanotype ground)
|
|
2115
|
+
surface: "#16466e",
|
|
2116
|
+
// raised sheet
|
|
2117
|
+
overlay: "#1c5180",
|
|
2118
|
+
// popovers, dropdowns
|
|
2119
|
+
border: "#3a6f96",
|
|
2120
|
+
// grid line
|
|
2121
|
+
text: "#eaf2f8",
|
|
2122
|
+
// chalk white
|
|
2123
|
+
textMuted: "#9fc0d6",
|
|
2124
|
+
// faint chalk note
|
|
2125
|
+
textOnFillLight: "#eaf2f8",
|
|
2126
|
+
// chalk white
|
|
2127
|
+
textOnFillDark: "#0c2f4d",
|
|
2128
|
+
// deep blueprint navy
|
|
2129
|
+
primary: "#7fb8d8",
|
|
2130
|
+
// chalk cyan
|
|
2131
|
+
secondary: "#9fb8c8",
|
|
2132
|
+
// pale steel
|
|
2133
|
+
accent: "#d8c27a",
|
|
2134
|
+
// chalk amber
|
|
2135
|
+
destructive: "#e08a7a",
|
|
2136
|
+
// chalk correction red
|
|
2137
|
+
colors: {
|
|
2138
|
+
red: "#e0907e",
|
|
2139
|
+
// chalk red
|
|
2140
|
+
orange: "#e0ab78",
|
|
2141
|
+
// chalk amber
|
|
2142
|
+
yellow: "#e3d089",
|
|
2143
|
+
// chalk gold
|
|
2144
|
+
green: "#93c79e",
|
|
2145
|
+
// chalk green
|
|
2146
|
+
blue: "#8ec3e0",
|
|
2147
|
+
// chalk cyan-blue
|
|
2148
|
+
purple: "#b6a6d8",
|
|
2149
|
+
// chalk indigo
|
|
2150
|
+
teal: "#84c7c2",
|
|
2151
|
+
// chalk teal
|
|
2152
|
+
cyan: "#9fd6e0",
|
|
2153
|
+
// chalk cyan
|
|
2154
|
+
gray: "#aebecb",
|
|
2155
|
+
// chalk graphite
|
|
2156
|
+
black: "#16466e",
|
|
2157
|
+
// raised sheet
|
|
2158
|
+
white: "#eaf2f8"
|
|
2159
|
+
// chalk white
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
registerPalette(blueprintPalette);
|
|
1974
2164
|
}
|
|
1975
2165
|
});
|
|
1976
2166
|
|
|
@@ -2467,6 +2657,120 @@ var init_rose_pine = __esm({
|
|
|
2467
2657
|
}
|
|
2468
2658
|
});
|
|
2469
2659
|
|
|
2660
|
+
// src/palettes/slate.ts
|
|
2661
|
+
var slatePalette;
|
|
2662
|
+
var init_slate = __esm({
|
|
2663
|
+
"src/palettes/slate.ts"() {
|
|
2664
|
+
"use strict";
|
|
2665
|
+
init_registry();
|
|
2666
|
+
slatePalette = {
|
|
2667
|
+
id: "slate",
|
|
2668
|
+
name: "Slate",
|
|
2669
|
+
light: {
|
|
2670
|
+
bg: "#ffffff",
|
|
2671
|
+
// clean slide white
|
|
2672
|
+
surface: "#f3f5f8",
|
|
2673
|
+
// light cool-gray panel
|
|
2674
|
+
overlay: "#eaeef3",
|
|
2675
|
+
// popovers, dropdowns
|
|
2676
|
+
border: "#d4dae1",
|
|
2677
|
+
// hairline rule
|
|
2678
|
+
text: "#1f2933",
|
|
2679
|
+
// near-black slate (softer than pure black)
|
|
2680
|
+
textMuted: "#5b6672",
|
|
2681
|
+
// secondary label
|
|
2682
|
+
textOnFillLight: "#ffffff",
|
|
2683
|
+
// light text on dark fills
|
|
2684
|
+
textOnFillDark: "#1f2933",
|
|
2685
|
+
// dark text on light fills
|
|
2686
|
+
primary: "#3b6ea5",
|
|
2687
|
+
// confident corporate blue
|
|
2688
|
+
secondary: "#5b6672",
|
|
2689
|
+
// slate gray
|
|
2690
|
+
accent: "#3a9188",
|
|
2691
|
+
// muted teal accent
|
|
2692
|
+
destructive: "#c0504d",
|
|
2693
|
+
// brick red
|
|
2694
|
+
colors: {
|
|
2695
|
+
red: "#c0504d",
|
|
2696
|
+
// brick
|
|
2697
|
+
orange: "#cc7a33",
|
|
2698
|
+
// muted amber
|
|
2699
|
+
yellow: "#c9a227",
|
|
2700
|
+
// gold (not neon)
|
|
2701
|
+
green: "#5b9357",
|
|
2702
|
+
// forest / sage
|
|
2703
|
+
blue: "#3b6ea5",
|
|
2704
|
+
// corporate blue
|
|
2705
|
+
purple: "#7d5ba6",
|
|
2706
|
+
// muted violet
|
|
2707
|
+
teal: "#3a9188",
|
|
2708
|
+
// teal
|
|
2709
|
+
cyan: "#4f96c4",
|
|
2710
|
+
// steel cyan
|
|
2711
|
+
gray: "#7e8a97",
|
|
2712
|
+
// cool gray
|
|
2713
|
+
black: "#1f2933",
|
|
2714
|
+
// slate ink
|
|
2715
|
+
white: "#f3f5f8"
|
|
2716
|
+
// panel
|
|
2717
|
+
}
|
|
2718
|
+
},
|
|
2719
|
+
dark: {
|
|
2720
|
+
bg: "#161b22",
|
|
2721
|
+
// deep slate (keynote dark)
|
|
2722
|
+
surface: "#202833",
|
|
2723
|
+
// raised panel
|
|
2724
|
+
overlay: "#29323e",
|
|
2725
|
+
// popovers, dropdowns
|
|
2726
|
+
border: "#38424f",
|
|
2727
|
+
// divider
|
|
2728
|
+
text: "#e6eaef",
|
|
2729
|
+
// off-white
|
|
2730
|
+
textMuted: "#9aa5b1",
|
|
2731
|
+
// secondary label
|
|
2732
|
+
textOnFillLight: "#ffffff",
|
|
2733
|
+
// light text on dark fills
|
|
2734
|
+
textOnFillDark: "#161b22",
|
|
2735
|
+
// dark text on light fills
|
|
2736
|
+
primary: "#5b9bd5",
|
|
2737
|
+
// lifted corporate blue
|
|
2738
|
+
secondary: "#8593a3",
|
|
2739
|
+
// slate gray, lifted
|
|
2740
|
+
accent: "#45b3a3",
|
|
2741
|
+
// teal, lifted
|
|
2742
|
+
destructive: "#e07b6e",
|
|
2743
|
+
// brick, lifted
|
|
2744
|
+
colors: {
|
|
2745
|
+
red: "#e07b6e",
|
|
2746
|
+
// brick
|
|
2747
|
+
orange: "#e0975a",
|
|
2748
|
+
// amber
|
|
2749
|
+
yellow: "#d9bd5a",
|
|
2750
|
+
// gold
|
|
2751
|
+
green: "#74b56e",
|
|
2752
|
+
// forest / sage
|
|
2753
|
+
blue: "#5b9bd5",
|
|
2754
|
+
// corporate blue
|
|
2755
|
+
purple: "#a585c9",
|
|
2756
|
+
// violet
|
|
2757
|
+
teal: "#45b3a3",
|
|
2758
|
+
// teal
|
|
2759
|
+
cyan: "#62b0d9",
|
|
2760
|
+
// steel cyan
|
|
2761
|
+
gray: "#95a1ae",
|
|
2762
|
+
// cool gray
|
|
2763
|
+
black: "#202833",
|
|
2764
|
+
// raised panel
|
|
2765
|
+
white: "#e6eaef"
|
|
2766
|
+
// off-white
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2770
|
+
registerPalette(slatePalette);
|
|
2771
|
+
}
|
|
2772
|
+
});
|
|
2773
|
+
|
|
2470
2774
|
// src/palettes/solarized.ts
|
|
2471
2775
|
var solarizedPalette;
|
|
2472
2776
|
var init_solarized = __esm({
|
|
@@ -2562,6 +2866,120 @@ var init_solarized = __esm({
|
|
|
2562
2866
|
}
|
|
2563
2867
|
});
|
|
2564
2868
|
|
|
2869
|
+
// src/palettes/tidewater.ts
|
|
2870
|
+
var tidewaterPalette;
|
|
2871
|
+
var init_tidewater = __esm({
|
|
2872
|
+
"src/palettes/tidewater.ts"() {
|
|
2873
|
+
"use strict";
|
|
2874
|
+
init_registry();
|
|
2875
|
+
tidewaterPalette = {
|
|
2876
|
+
id: "tidewater",
|
|
2877
|
+
name: "Tidewater",
|
|
2878
|
+
light: {
|
|
2879
|
+
bg: "#eceff0",
|
|
2880
|
+
// weathered sea-mist paper
|
|
2881
|
+
surface: "#e0e4e3",
|
|
2882
|
+
// worn deck panel
|
|
2883
|
+
overlay: "#dadfdf",
|
|
2884
|
+
// popovers, dropdowns
|
|
2885
|
+
border: "#a9b2b3",
|
|
2886
|
+
// muted slate rule
|
|
2887
|
+
text: "#18313f",
|
|
2888
|
+
// ship's-log navy ink
|
|
2889
|
+
textMuted: "#51636b",
|
|
2890
|
+
// faded log entry
|
|
2891
|
+
textOnFillLight: "#f3f5f3",
|
|
2892
|
+
// weathered white
|
|
2893
|
+
textOnFillDark: "#162c38",
|
|
2894
|
+
// deep navy
|
|
2895
|
+
primary: "#1f4e6b",
|
|
2896
|
+
// deep-sea navy
|
|
2897
|
+
secondary: "#b08a4f",
|
|
2898
|
+
// rope / manila tan
|
|
2899
|
+
accent: "#c69a3e",
|
|
2900
|
+
// brass
|
|
2901
|
+
destructive: "#c1433a",
|
|
2902
|
+
// signal-flag red
|
|
2903
|
+
colors: {
|
|
2904
|
+
red: "#c1433a",
|
|
2905
|
+
// signal-flag red
|
|
2906
|
+
orange: "#cc7a38",
|
|
2907
|
+
// weathered amber
|
|
2908
|
+
yellow: "#d6bf5a",
|
|
2909
|
+
// brass gold
|
|
2910
|
+
green: "#4f8a6b",
|
|
2911
|
+
// sea-glass green
|
|
2912
|
+
blue: "#1f4e6b",
|
|
2913
|
+
// deep-sea navy
|
|
2914
|
+
purple: "#6a5a8c",
|
|
2915
|
+
// twilight harbor
|
|
2916
|
+
teal: "#3d8c8c",
|
|
2917
|
+
// sea-glass teal
|
|
2918
|
+
cyan: "#4f9bb5",
|
|
2919
|
+
// shallow water
|
|
2920
|
+
gray: "#8a8d86",
|
|
2921
|
+
// driftwood gray
|
|
2922
|
+
black: "#18313f",
|
|
2923
|
+
// navy ink
|
|
2924
|
+
white: "#e0e4e3"
|
|
2925
|
+
// deck panel
|
|
2926
|
+
}
|
|
2927
|
+
},
|
|
2928
|
+
dark: {
|
|
2929
|
+
bg: "#0f2230",
|
|
2930
|
+
// night-harbor deep sea
|
|
2931
|
+
surface: "#16303f",
|
|
2932
|
+
// raised hull
|
|
2933
|
+
overlay: "#1d3a4a",
|
|
2934
|
+
// popovers, dropdowns
|
|
2935
|
+
border: "#2c4856",
|
|
2936
|
+
// rigging line
|
|
2937
|
+
text: "#e6ebe8",
|
|
2938
|
+
// weathered white
|
|
2939
|
+
textMuted: "#9aaab0",
|
|
2940
|
+
// faded label
|
|
2941
|
+
textOnFillLight: "#f3f5f3",
|
|
2942
|
+
// weathered white
|
|
2943
|
+
textOnFillDark: "#0f2230",
|
|
2944
|
+
// deep sea
|
|
2945
|
+
primary: "#4f9bc4",
|
|
2946
|
+
// lifted sea blue
|
|
2947
|
+
secondary: "#c9a46a",
|
|
2948
|
+
// rope tan, lifted
|
|
2949
|
+
accent: "#d9b25a",
|
|
2950
|
+
// brass, lifted
|
|
2951
|
+
destructive: "#e06a5e",
|
|
2952
|
+
// signal red, lifted
|
|
2953
|
+
colors: {
|
|
2954
|
+
red: "#e06a5e",
|
|
2955
|
+
// signal-flag red
|
|
2956
|
+
orange: "#df9a52",
|
|
2957
|
+
// amber
|
|
2958
|
+
yellow: "#e0c662",
|
|
2959
|
+
// brass gold
|
|
2960
|
+
green: "#6fb58c",
|
|
2961
|
+
// sea-glass green
|
|
2962
|
+
blue: "#4f9bc4",
|
|
2963
|
+
// sea blue
|
|
2964
|
+
purple: "#9486bf",
|
|
2965
|
+
// twilight harbor
|
|
2966
|
+
teal: "#5cb0ac",
|
|
2967
|
+
// sea-glass teal
|
|
2968
|
+
cyan: "#62b4cf",
|
|
2969
|
+
// shallow water
|
|
2970
|
+
gray: "#9aa39c",
|
|
2971
|
+
// driftwood gray
|
|
2972
|
+
black: "#16303f",
|
|
2973
|
+
// raised hull
|
|
2974
|
+
white: "#e6ebe8"
|
|
2975
|
+
// weathered white
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
registerPalette(tidewaterPalette);
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2565
2983
|
// src/palettes/tokyo-night.ts
|
|
2566
2984
|
var tokyoNightPalette;
|
|
2567
2985
|
var init_tokyo_night = __esm({
|
|
@@ -2837,7 +3255,8 @@ var init_monokai = __esm({
|
|
|
2837
3255
|
// src/palettes/index.ts
|
|
2838
3256
|
var palettes_exports = {};
|
|
2839
3257
|
__export(palettes_exports, {
|
|
2840
|
-
|
|
3258
|
+
atlasPalette: () => atlasPalette,
|
|
3259
|
+
blueprintPalette: () => blueprintPalette,
|
|
2841
3260
|
catppuccinPalette: () => catppuccinPalette,
|
|
2842
3261
|
contrastText: () => contrastText,
|
|
2843
3262
|
draculaPalette: () => draculaPalette,
|
|
@@ -2858,7 +3277,9 @@ __export(palettes_exports, {
|
|
|
2858
3277
|
rosePinePalette: () => rosePinePalette,
|
|
2859
3278
|
shade: () => shade,
|
|
2860
3279
|
shapeFill: () => shapeFill,
|
|
3280
|
+
slatePalette: () => slatePalette,
|
|
2861
3281
|
solarizedPalette: () => solarizedPalette,
|
|
3282
|
+
tidewaterPalette: () => tidewaterPalette,
|
|
2862
3283
|
tint: () => tint,
|
|
2863
3284
|
tokyoNightPalette: () => tokyoNightPalette
|
|
2864
3285
|
});
|
|
@@ -2868,17 +3289,21 @@ var init_palettes = __esm({
|
|
|
2868
3289
|
"use strict";
|
|
2869
3290
|
init_registry();
|
|
2870
3291
|
init_color_utils();
|
|
2871
|
-
|
|
3292
|
+
init_atlas();
|
|
3293
|
+
init_blueprint();
|
|
2872
3294
|
init_catppuccin();
|
|
2873
3295
|
init_gruvbox();
|
|
2874
3296
|
init_nord();
|
|
2875
3297
|
init_one_dark();
|
|
2876
3298
|
init_rose_pine();
|
|
3299
|
+
init_slate();
|
|
2877
3300
|
init_solarized();
|
|
3301
|
+
init_tidewater();
|
|
2878
3302
|
init_tokyo_night();
|
|
2879
3303
|
init_dracula();
|
|
2880
3304
|
init_monokai();
|
|
2881
|
-
|
|
3305
|
+
init_atlas();
|
|
3306
|
+
init_blueprint();
|
|
2882
3307
|
init_catppuccin();
|
|
2883
3308
|
init_dracula();
|
|
2884
3309
|
init_gruvbox();
|
|
@@ -2886,9 +3311,15 @@ var init_palettes = __esm({
|
|
|
2886
3311
|
init_nord();
|
|
2887
3312
|
init_one_dark();
|
|
2888
3313
|
init_rose_pine();
|
|
3314
|
+
init_slate();
|
|
2889
3315
|
init_solarized();
|
|
3316
|
+
init_tidewater();
|
|
2890
3317
|
init_tokyo_night();
|
|
2891
3318
|
palettes = {
|
|
3319
|
+
atlas: atlasPalette,
|
|
3320
|
+
blueprint: blueprintPalette,
|
|
3321
|
+
slate: slatePalette,
|
|
3322
|
+
tidewater: tidewaterPalette,
|
|
2892
3323
|
nord: nordPalette,
|
|
2893
3324
|
catppuccin: catppuccinPalette,
|
|
2894
3325
|
solarized: solarizedPalette,
|
|
@@ -2897,8 +3328,7 @@ var init_palettes = __esm({
|
|
|
2897
3328
|
oneDark: oneDarkPalette,
|
|
2898
3329
|
rosePine: rosePinePalette,
|
|
2899
3330
|
dracula: draculaPalette,
|
|
2900
|
-
monokai: monokaiPalette
|
|
2901
|
-
bold: boldPalette
|
|
3331
|
+
monokai: monokaiPalette
|
|
2902
3332
|
};
|
|
2903
3333
|
}
|
|
2904
3334
|
});
|
|
@@ -3408,6 +3838,9 @@ function controlsGroupCapsuleWidth(toggles) {
|
|
|
3408
3838
|
}
|
|
3409
3839
|
return w;
|
|
3410
3840
|
}
|
|
3841
|
+
function isAppHostedControls(config, isExport) {
|
|
3842
|
+
return !isExport && config.controlsHost === "app" && !!config.controlsGroup && config.controlsGroup.toggles.length > 0;
|
|
3843
|
+
}
|
|
3411
3844
|
function buildControlsGroupLayout(config, state) {
|
|
3412
3845
|
const cg = config.controlsGroup;
|
|
3413
3846
|
if (!cg || cg.toggles.length === 0) return void 0;
|
|
@@ -3461,6 +3894,7 @@ function buildControlsGroupLayout(config, state) {
|
|
|
3461
3894
|
function computeLegendLayout(config, state, containerWidth) {
|
|
3462
3895
|
const { groups, controls: configControls, mode } = config;
|
|
3463
3896
|
const isExport = mode === "export";
|
|
3897
|
+
const gated = isAppHostedControls(config, isExport);
|
|
3464
3898
|
const activeGroupName = state.activeGroup?.toLowerCase() ?? null;
|
|
3465
3899
|
if (isExport && !activeGroupName) {
|
|
3466
3900
|
return {
|
|
@@ -3471,7 +3905,7 @@ function computeLegendLayout(config, state, containerWidth) {
|
|
|
3471
3905
|
pills: []
|
|
3472
3906
|
};
|
|
3473
3907
|
}
|
|
3474
|
-
const controlsGroupLayout = isExport ? void 0 : buildControlsGroupLayout(config, state);
|
|
3908
|
+
const controlsGroupLayout = isExport || gated ? void 0 : buildControlsGroupLayout(config, state);
|
|
3475
3909
|
const visibleGroups = config.showEmptyGroups ? groups : groups.filter((g) => g.entries.length > 0 || !!g.gradient);
|
|
3476
3910
|
if (visibleGroups.length === 0 && (!configControls || configControls.length === 0) && !controlsGroupLayout) {
|
|
3477
3911
|
return {
|
|
@@ -8351,8 +8785,8 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8351
8785
|
const pt = points[i];
|
|
8352
8786
|
const ptSize = pt.size ?? symbolSize;
|
|
8353
8787
|
const minGap = ptSize / 2 + 4;
|
|
8354
|
-
const
|
|
8355
|
-
const labelX = pt.px -
|
|
8788
|
+
const labelWidth2 = pt.name.length * fontSize * 0.6 + 8;
|
|
8789
|
+
const labelX = pt.px - labelWidth2 / 2;
|
|
8356
8790
|
let bestLabelY = 0;
|
|
8357
8791
|
let bestOffset = Infinity;
|
|
8358
8792
|
let placed = false;
|
|
@@ -8364,7 +8798,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8364
8798
|
const candidate = {
|
|
8365
8799
|
x: labelX,
|
|
8366
8800
|
y: labelY,
|
|
8367
|
-
w:
|
|
8801
|
+
w: labelWidth2,
|
|
8368
8802
|
h: labelHeight
|
|
8369
8803
|
};
|
|
8370
8804
|
let collision = false;
|
|
@@ -8406,7 +8840,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8406
8840
|
const labelRect = {
|
|
8407
8841
|
x: labelX,
|
|
8408
8842
|
y: bestLabelY,
|
|
8409
|
-
w:
|
|
8843
|
+
w: labelWidth2,
|
|
8410
8844
|
h: labelHeight
|
|
8411
8845
|
};
|
|
8412
8846
|
placedLabels.push(labelRect);
|
|
@@ -8442,7 +8876,7 @@ function computeScatterLabelGraphics(points, chartBounds, fontSize, symbolSize,
|
|
|
8442
8876
|
shape: {
|
|
8443
8877
|
x: labelX - bgPad,
|
|
8444
8878
|
y: bestLabelY - bgPad,
|
|
8445
|
-
width:
|
|
8879
|
+
width: labelWidth2 + bgPad * 2,
|
|
8446
8880
|
height: labelHeight + bgPad * 2
|
|
8447
8881
|
},
|
|
8448
8882
|
style: { fill: bg },
|
|
@@ -15882,10 +16316,6 @@ function parseMap(content) {
|
|
|
15882
16316
|
handleTag(trimmed, lineNumber);
|
|
15883
16317
|
continue;
|
|
15884
16318
|
}
|
|
15885
|
-
if ((firstWord === "muted" || firstWord === "natural") && trimmed === firstWord) {
|
|
15886
|
-
handleDirective(firstWord, "", lineNumber);
|
|
15887
|
-
continue;
|
|
15888
|
-
}
|
|
15889
16319
|
if (DIRECTIVE_SET.has(firstWord) && !trimmed.slice(firstWord.length).trimStart().startsWith(":")) {
|
|
15890
16320
|
handleDirective(
|
|
15891
16321
|
firstWord,
|
|
@@ -15932,24 +16362,6 @@ function parseMap(content) {
|
|
|
15932
16362
|
pushWarning(line12, `Duplicate directive "${key}" \u2014 last value wins.`);
|
|
15933
16363
|
};
|
|
15934
16364
|
switch (key) {
|
|
15935
|
-
case "region":
|
|
15936
|
-
dup(d.region);
|
|
15937
|
-
d.region = value;
|
|
15938
|
-
break;
|
|
15939
|
-
case "projection":
|
|
15940
|
-
dup(d.projection);
|
|
15941
|
-
if (value && ![
|
|
15942
|
-
"equirectangular",
|
|
15943
|
-
"natural-earth",
|
|
15944
|
-
"albers-usa",
|
|
15945
|
-
"mercator"
|
|
15946
|
-
].includes(value))
|
|
15947
|
-
pushWarning(
|
|
15948
|
-
line12,
|
|
15949
|
-
`Unknown projection "${value}" (expected equirectangular | natural-earth | albers-usa | mercator).`
|
|
15950
|
-
);
|
|
15951
|
-
d.projection = value;
|
|
15952
|
-
break;
|
|
15953
16365
|
case "region-metric": {
|
|
15954
16366
|
dup(d.regionMetric);
|
|
15955
16367
|
const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
|
|
@@ -15965,91 +16377,43 @@ function parseMap(content) {
|
|
|
15965
16377
|
dup(d.flowMetric);
|
|
15966
16378
|
d.flowMetric = value;
|
|
15967
16379
|
break;
|
|
15968
|
-
case "
|
|
15969
|
-
dup(d.
|
|
15970
|
-
|
|
15971
|
-
const s = parseScale(value, line12);
|
|
15972
|
-
if (s) d.scale = s;
|
|
15973
|
-
}
|
|
15974
|
-
break;
|
|
15975
|
-
case "region-labels":
|
|
15976
|
-
dup(d.regionLabels);
|
|
15977
|
-
if (value && !["full", "abbrev", "off"].includes(value))
|
|
15978
|
-
pushWarning(
|
|
15979
|
-
line12,
|
|
15980
|
-
`Unknown region-labels "${value}" (expected full | abbrev | off).`
|
|
15981
|
-
);
|
|
15982
|
-
d.regionLabels = value;
|
|
15983
|
-
break;
|
|
15984
|
-
case "poi-labels":
|
|
15985
|
-
dup(d.poiLabels);
|
|
15986
|
-
if (value && !["off", "auto", "all"].includes(value))
|
|
15987
|
-
pushWarning(
|
|
15988
|
-
line12,
|
|
15989
|
-
`Unknown poi-labels "${value}" (expected off | auto | all).`
|
|
15990
|
-
);
|
|
15991
|
-
d.poiLabels = value;
|
|
15992
|
-
break;
|
|
15993
|
-
case "default-country":
|
|
15994
|
-
dup(d.defaultCountry);
|
|
15995
|
-
d.defaultCountry = value;
|
|
15996
|
-
break;
|
|
15997
|
-
case "default-state":
|
|
15998
|
-
dup(d.defaultState);
|
|
15999
|
-
d.defaultState = value;
|
|
16380
|
+
case "locale":
|
|
16381
|
+
dup(d.locale);
|
|
16382
|
+
d.locale = value;
|
|
16000
16383
|
break;
|
|
16001
16384
|
case "active-tag":
|
|
16002
16385
|
dup(d.activeTag);
|
|
16003
16386
|
d.activeTag = value;
|
|
16004
16387
|
break;
|
|
16388
|
+
case "caption":
|
|
16389
|
+
dup(d.caption);
|
|
16390
|
+
d.caption = value;
|
|
16391
|
+
break;
|
|
16392
|
+
// ── Cosmetic `no-*` opt-outs: bare flags, idempotent (mirror `no-legend`,
|
|
16393
|
+
// no dup warning); each defaults the feature ON when absent. ──
|
|
16005
16394
|
case "no-legend":
|
|
16006
16395
|
d.noLegend = true;
|
|
16007
16396
|
break;
|
|
16008
|
-
case "no-
|
|
16009
|
-
d.
|
|
16397
|
+
case "no-coastline":
|
|
16398
|
+
d.noCoastline = true;
|
|
16010
16399
|
break;
|
|
16011
|
-
case "relief":
|
|
16012
|
-
d.
|
|
16400
|
+
case "no-relief":
|
|
16401
|
+
d.noRelief = true;
|
|
16013
16402
|
break;
|
|
16014
|
-
case "
|
|
16015
|
-
|
|
16016
|
-
if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
|
|
16017
|
-
pushWarning(
|
|
16018
|
-
line12,
|
|
16019
|
-
`Conflicting basemap dress \u2014 "${d.basemapStyle}" then "${key}"; last wins.`
|
|
16020
|
-
);
|
|
16021
|
-
d.basemapStyle = key;
|
|
16403
|
+
case "no-context-labels":
|
|
16404
|
+
d.noContextLabels = true;
|
|
16022
16405
|
break;
|
|
16023
|
-
case "
|
|
16024
|
-
|
|
16025
|
-
d.subtitle = value;
|
|
16406
|
+
case "no-region-labels":
|
|
16407
|
+
d.noRegionLabels = true;
|
|
16026
16408
|
break;
|
|
16027
|
-
case "
|
|
16028
|
-
|
|
16029
|
-
|
|
16409
|
+
case "no-poi-labels":
|
|
16410
|
+
d.noPoiLabels = true;
|
|
16411
|
+
break;
|
|
16412
|
+
case "no-colorize":
|
|
16413
|
+
d.noColorize = true;
|
|
16030
16414
|
break;
|
|
16031
16415
|
}
|
|
16032
16416
|
}
|
|
16033
|
-
function parseScale(value, line12) {
|
|
16034
|
-
const toks = value.split(/\s+/).filter(Boolean);
|
|
16035
|
-
const min = Number(toks[0]);
|
|
16036
|
-
const max = Number(toks[1]);
|
|
16037
|
-
if (!Number.isFinite(min) || !Number.isFinite(max)) {
|
|
16038
|
-
pushError(line12, `scale requires numeric <min> <max> (got "${value}").`);
|
|
16039
|
-
return null;
|
|
16040
|
-
}
|
|
16041
|
-
const scale = { min, max };
|
|
16042
|
-
if (toks[2] === "center") {
|
|
16043
|
-
const c = Number(toks[3]);
|
|
16044
|
-
if (Number.isFinite(c)) scale.center = c;
|
|
16045
|
-
else
|
|
16046
|
-
pushError(
|
|
16047
|
-
line12,
|
|
16048
|
-
`scale center requires a number (got "${toks[3] ?? ""}").`
|
|
16049
|
-
);
|
|
16050
|
-
}
|
|
16051
|
-
return scale;
|
|
16052
|
-
}
|
|
16053
16417
|
function handleTag(trimmed, line12) {
|
|
16054
16418
|
const m = matchTagBlockHeading(trimmed);
|
|
16055
16419
|
if (!m) {
|
|
@@ -16249,13 +16613,15 @@ function parseMap(content) {
|
|
|
16249
16613
|
pushError(line12, `Edge has an empty endpoint: "${trimmed}".`);
|
|
16250
16614
|
continue;
|
|
16251
16615
|
}
|
|
16252
|
-
const
|
|
16616
|
+
const isLast = k === links.length - 1;
|
|
16617
|
+
const meta = isLast ? lastSplit.meta : {};
|
|
16618
|
+
const style = links[k].style === "arc" ? "arc" : "straight";
|
|
16253
16619
|
edges.push({
|
|
16254
16620
|
from,
|
|
16255
16621
|
to,
|
|
16256
16622
|
...links[k].label !== void 0 && { label: links[k].label },
|
|
16257
16623
|
directed: links[k].directed,
|
|
16258
|
-
style
|
|
16624
|
+
style,
|
|
16259
16625
|
meta,
|
|
16260
16626
|
lineNumber: line12
|
|
16261
16627
|
});
|
|
@@ -16341,22 +16707,19 @@ var init_parser12 = __esm({
|
|
|
16341
16707
|
LEG_ARROW_RE = /^(-[^>]*?->|->|~[^>]*?~>|~>|--)\s+(.+)$/;
|
|
16342
16708
|
AT_RE = /(^|[\s,])at\s*:/i;
|
|
16343
16709
|
DIRECTIVE_SET = /* @__PURE__ */ new Set([
|
|
16344
|
-
"region",
|
|
16345
|
-
"projection",
|
|
16346
16710
|
"region-metric",
|
|
16347
16711
|
"poi-metric",
|
|
16348
16712
|
"flow-metric",
|
|
16349
|
-
"
|
|
16350
|
-
"region-labels",
|
|
16351
|
-
"poi-labels",
|
|
16352
|
-
"default-country",
|
|
16353
|
-
"default-state",
|
|
16713
|
+
"locale",
|
|
16354
16714
|
"active-tag",
|
|
16715
|
+
"caption",
|
|
16355
16716
|
"no-legend",
|
|
16356
|
-
"no-
|
|
16357
|
-
"relief",
|
|
16358
|
-
"
|
|
16359
|
-
"
|
|
16717
|
+
"no-coastline",
|
|
16718
|
+
"no-relief",
|
|
16719
|
+
"no-context-labels",
|
|
16720
|
+
"no-region-labels",
|
|
16721
|
+
"no-poi-labels",
|
|
16722
|
+
"no-colorize"
|
|
16360
16723
|
]);
|
|
16361
16724
|
}
|
|
16362
16725
|
});
|
|
@@ -16534,6 +16897,21 @@ function parseBoxesAndLines(content) {
|
|
|
16534
16897
|
}
|
|
16535
16898
|
continue;
|
|
16536
16899
|
}
|
|
16900
|
+
if (!contentStarted) {
|
|
16901
|
+
const metricMatch = trimmed.match(/^box-metric\s+(.+)$/i);
|
|
16902
|
+
if (metricMatch) {
|
|
16903
|
+
const { label, colorName } = peelTrailingColorName(
|
|
16904
|
+
metricMatch[1].trim()
|
|
16905
|
+
);
|
|
16906
|
+
result.boxMetric = label;
|
|
16907
|
+
if (colorName !== void 0) result.boxMetricColor = colorName;
|
|
16908
|
+
continue;
|
|
16909
|
+
}
|
|
16910
|
+
if (/^show-values$/i.test(trimmed)) {
|
|
16911
|
+
result.showValues = true;
|
|
16912
|
+
continue;
|
|
16913
|
+
}
|
|
16914
|
+
}
|
|
16537
16915
|
if (!contentStarted) {
|
|
16538
16916
|
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
16539
16917
|
if (optMatch) {
|
|
@@ -16912,6 +17290,19 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16912
17290
|
description = [metadata["description"]];
|
|
16913
17291
|
delete metadata["description"];
|
|
16914
17292
|
}
|
|
17293
|
+
let value;
|
|
17294
|
+
if (metadata["value"] !== void 0) {
|
|
17295
|
+
const raw = metadata["value"];
|
|
17296
|
+
const num = Number(raw);
|
|
17297
|
+
if (Number.isFinite(num)) {
|
|
17298
|
+
value = num;
|
|
17299
|
+
} else {
|
|
17300
|
+
diagnostics.push(
|
|
17301
|
+
makeDgmoError(lineNum, `value must be a number (got "${raw}")`, "error")
|
|
17302
|
+
);
|
|
17303
|
+
}
|
|
17304
|
+
delete metadata["value"];
|
|
17305
|
+
}
|
|
16915
17306
|
if (split.alias) {
|
|
16916
17307
|
nameAliasMap?.set(normalizeName(split.alias), label);
|
|
16917
17308
|
}
|
|
@@ -16920,7 +17311,8 @@ function parseNodeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
|
|
|
16920
17311
|
label,
|
|
16921
17312
|
lineNumber: lineNum,
|
|
16922
17313
|
metadata,
|
|
16923
|
-
...description !== void 0 && { description }
|
|
17314
|
+
...description !== void 0 && { description },
|
|
17315
|
+
...value !== void 0 && { value }
|
|
16924
17316
|
};
|
|
16925
17317
|
}
|
|
16926
17318
|
function splitTargetAndMeta(rest, metaAliasMap) {
|
|
@@ -24277,8 +24669,8 @@ function renderKanban(container, parsed, palette, isDark, options) {
|
|
|
24277
24669
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24278
24670
|
for (const meta of tagMeta) {
|
|
24279
24671
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(`${meta.label}: `);
|
|
24280
|
-
const
|
|
24281
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
24672
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
24673
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24282
24674
|
metaY += sCardMetaLineHeight;
|
|
24283
24675
|
}
|
|
24284
24676
|
for (const detail of card.details) {
|
|
@@ -24622,8 +25014,8 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
|
|
|
24622
25014
|
let metaY = separatorY + sCardSeparatorGap + sCardMetaFontSize;
|
|
24623
25015
|
for (const meta of tagMeta) {
|
|
24624
25016
|
cg.append("text").attr("x", cx + sCardPaddingX).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", palette.textMuted).text(`${meta.label}: `);
|
|
24625
|
-
const
|
|
24626
|
-
cg.append("text").attr("x", cx + sCardPaddingX +
|
|
25017
|
+
const labelWidth2 = (meta.label.length + 2) * sCardMetaFontSize * 0.6;
|
|
25018
|
+
cg.append("text").attr("x", cx + sCardPaddingX + labelWidth2).attr("y", metaY).attr("font-size", sCardMetaFontSize).attr("fill", onCardText).text(meta.value);
|
|
24627
25019
|
metaY += sCardMetaLineHeight;
|
|
24628
25020
|
}
|
|
24629
25021
|
for (const detail of card.details) {
|
|
@@ -25458,8 +25850,8 @@ function classifyEREntities(tables, relationships) {
|
|
|
25458
25850
|
}
|
|
25459
25851
|
}
|
|
25460
25852
|
const mmParticipants = /* @__PURE__ */ new Set();
|
|
25461
|
-
for (const [id,
|
|
25462
|
-
if (
|
|
25853
|
+
for (const [id, neighbors2] of tableStarNeighbors) {
|
|
25854
|
+
if (neighbors2.size >= 2) mmParticipants.add(id);
|
|
25463
25855
|
}
|
|
25464
25856
|
const indegreeValues = Object.values(indegreeMap);
|
|
25465
25857
|
const mean = indegreeValues.reduce((a, b) => a + b, 0) / indegreeValues.length;
|
|
@@ -26040,7 +26432,18 @@ function fitLabelToHeader(label, nodeWidth, maxLines) {
|
|
|
26040
26432
|
const truncated = label.length > maxChars ? label.slice(0, maxChars - 1) + "\u2026" : label;
|
|
26041
26433
|
return { lines: [truncated], fontSize: MIN_NODE_FONT_SIZE };
|
|
26042
26434
|
}
|
|
26043
|
-
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, solid) {
|
|
26435
|
+
function nodeColors(node, tagGroups, activeGroupName, palette, isDark, value, solid) {
|
|
26436
|
+
const neutralFill = mix(palette.bg, palette.text, isDark ? 90 : 95);
|
|
26437
|
+
if (value.active) {
|
|
26438
|
+
const fill3 = node.value !== void 0 ? value.fillForValue(node.value) : neutralFill;
|
|
26439
|
+
const stroke3 = value.hue;
|
|
26440
|
+
const text2 = contrastText(
|
|
26441
|
+
fill3,
|
|
26442
|
+
palette.textOnFillLight,
|
|
26443
|
+
palette.textOnFillDark
|
|
26444
|
+
);
|
|
26445
|
+
return { fill: fill3, stroke: stroke3, text: text2 };
|
|
26446
|
+
}
|
|
26044
26447
|
const tagColor = resolveTagColor(
|
|
26045
26448
|
node.metadata,
|
|
26046
26449
|
[...tagGroups],
|
|
@@ -26130,7 +26533,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26130
26533
|
controlsExpanded,
|
|
26131
26534
|
onToggleDescriptions,
|
|
26132
26535
|
onToggleControlsExpand,
|
|
26133
|
-
exportMode = false
|
|
26536
|
+
exportMode = false,
|
|
26537
|
+
controlsHost
|
|
26134
26538
|
} = options ?? {};
|
|
26135
26539
|
d3Selection6.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
26136
26540
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -26148,21 +26552,65 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26148
26552
|
const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
|
|
26149
26553
|
const sTitleFontSize = sctx.text(TITLE_FONT_SIZE);
|
|
26150
26554
|
const sTitleY = sctx.structural(TITLE_Y);
|
|
26151
|
-
const
|
|
26555
|
+
const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
|
|
26556
|
+
const hasRamp = nodeValues.length > 0;
|
|
26557
|
+
const allNonNegative = hasRamp && nodeValues.every((v) => v >= 0);
|
|
26558
|
+
const rampMin = allNonNegative ? 0 : Math.min(...nodeValues);
|
|
26559
|
+
const rampMax = Math.max(...nodeValues);
|
|
26560
|
+
const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
|
|
26561
|
+
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
26562
|
+
const fillForValue = (v) => {
|
|
26563
|
+
const t = rampMax > rampMin ? (v - rampMin) / (rampMax - rampMin) : 1;
|
|
26564
|
+
const pct = RAMP_FLOOR + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR);
|
|
26565
|
+
return mix(rampHue, rampBase, pct);
|
|
26566
|
+
};
|
|
26567
|
+
const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
|
|
26568
|
+
const matchColorGroup = (v) => {
|
|
26569
|
+
const lv = v.trim().toLowerCase();
|
|
26570
|
+
if (lv === "none") return null;
|
|
26571
|
+
const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
|
|
26572
|
+
if (tg) return tg.name;
|
|
26573
|
+
if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
|
|
26574
|
+
return v;
|
|
26575
|
+
};
|
|
26576
|
+
const override = activeTagGroup;
|
|
26577
|
+
let activeGroup;
|
|
26578
|
+
if (override !== void 0) {
|
|
26579
|
+
activeGroup = override === null ? null : matchColorGroup(override);
|
|
26580
|
+
} else if (parsed.options["active-tag"] !== void 0) {
|
|
26581
|
+
activeGroup = matchColorGroup(parsed.options["active-tag"]);
|
|
26582
|
+
} else {
|
|
26583
|
+
activeGroup = VALUE_NAME ?? (parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null);
|
|
26584
|
+
}
|
|
26585
|
+
const activeIsValue = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
26586
|
+
const valueGroup = VALUE_NAME !== null ? {
|
|
26587
|
+
name: VALUE_NAME,
|
|
26588
|
+
entries: [],
|
|
26589
|
+
gradient: {
|
|
26590
|
+
min: rampMin,
|
|
26591
|
+
max: rampMax,
|
|
26592
|
+
hue: rampHue,
|
|
26593
|
+
base: rampBase
|
|
26594
|
+
}
|
|
26595
|
+
} : null;
|
|
26596
|
+
const legendGroups = [
|
|
26597
|
+
...valueGroup ? [valueGroup] : [],
|
|
26598
|
+
...parsed.tagGroups
|
|
26599
|
+
];
|
|
26600
|
+
const reserveHasDescriptions = parsed.nodes.some(
|
|
26601
|
+
(n) => n.description && n.description.length > 0
|
|
26602
|
+
);
|
|
26603
|
+
const willRenderLegend = legendGroups.length > 0 || reserveHasDescriptions && controlsHost !== "app";
|
|
26604
|
+
const sLegendHeight = willRenderLegend ? sctx.structural(
|
|
26152
26605
|
getMaxLegendReservedHeight(
|
|
26153
26606
|
{
|
|
26154
|
-
groups:
|
|
26607
|
+
groups: legendGroups,
|
|
26155
26608
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26156
26609
|
mode: exportMode ? "export" : "preview"
|
|
26157
26610
|
},
|
|
26158
26611
|
width
|
|
26159
26612
|
)
|
|
26160
|
-
);
|
|
26161
|
-
const activeGroup = resolveActiveTagGroup(
|
|
26162
|
-
parsed.tagGroups,
|
|
26163
|
-
parsed.options["active-tag"],
|
|
26164
|
-
activeTagGroup
|
|
26165
|
-
);
|
|
26613
|
+
) : 0;
|
|
26166
26614
|
const hidden = hiddenTagValues ?? parsed.initialHiddenTagValues;
|
|
26167
26615
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
26168
26616
|
for (const node of parsed.nodes) nodeMap.set(node.label, node);
|
|
@@ -26173,7 +26621,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26173
26621
|
const hasAnyDescriptions = parsed.nodes.some(
|
|
26174
26622
|
(n) => n.description && n.description.length > 0
|
|
26175
26623
|
);
|
|
26176
|
-
const needsLegend =
|
|
26624
|
+
const needsLegend = legendGroups.length > 0 || hasAnyDescriptions && onToggleDescriptions;
|
|
26177
26625
|
const legendH = needsLegend ? sLegendHeight + 8 : 0;
|
|
26178
26626
|
const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
|
|
26179
26627
|
let labelZoneExtension = 0;
|
|
@@ -26379,12 +26827,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26379
26827
|
activeGroup,
|
|
26380
26828
|
palette,
|
|
26381
26829
|
isDark,
|
|
26830
|
+
{ active: activeIsValue, hue: rampHue, fillForValue },
|
|
26382
26831
|
parsed.options["solid-fill"] === "on"
|
|
26383
26832
|
);
|
|
26384
26833
|
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);
|
|
26385
26834
|
for (const [key, val] of Object.entries(node.metadata)) {
|
|
26386
26835
|
nodeG.attr(`data-tag-${key.toLowerCase()}`, val.toLowerCase());
|
|
26387
26836
|
}
|
|
26837
|
+
if (node.value !== void 0) {
|
|
26838
|
+
nodeG.attr("data-value", node.value);
|
|
26839
|
+
}
|
|
26388
26840
|
if (onClickItem) {
|
|
26389
26841
|
nodeG.on("click", (event) => {
|
|
26390
26842
|
const target = event.target;
|
|
@@ -26468,14 +26920,30 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26468
26920
|
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]);
|
|
26469
26921
|
}
|
|
26470
26922
|
}
|
|
26923
|
+
if (parsed.showValues && node.value !== void 0) {
|
|
26924
|
+
const valueText = String(node.value);
|
|
26925
|
+
const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
|
|
26926
|
+
if (descShown) {
|
|
26927
|
+
const padX = 6;
|
|
26928
|
+
const padY = 5;
|
|
26929
|
+
const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
|
|
26930
|
+
const bh = VALUE_FONT_SIZE + 4;
|
|
26931
|
+
const bx = ln.width / 2 - bw - 4;
|
|
26932
|
+
const by = -ln.height / 2 + 4;
|
|
26933
|
+
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);
|
|
26934
|
+
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);
|
|
26935
|
+
} else {
|
|
26936
|
+
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);
|
|
26937
|
+
}
|
|
26938
|
+
}
|
|
26471
26939
|
}
|
|
26472
26940
|
const hasDescriptions = parsed.nodes.some(
|
|
26473
26941
|
(n) => n.description && n.description.length > 0
|
|
26474
26942
|
);
|
|
26475
|
-
const hasLegend =
|
|
26943
|
+
const hasLegend = legendGroups.length > 0 || hasDescriptions && controlsHost !== "app";
|
|
26476
26944
|
if (hasLegend) {
|
|
26477
26945
|
let controlsGroup;
|
|
26478
|
-
if (hasDescriptions && onToggleDescriptions) {
|
|
26946
|
+
if (hasDescriptions && (onToggleDescriptions || controlsHost === "app")) {
|
|
26479
26947
|
controlsGroup = {
|
|
26480
26948
|
toggles: [
|
|
26481
26949
|
{
|
|
@@ -26490,10 +26958,17 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
|
|
|
26490
26958
|
};
|
|
26491
26959
|
}
|
|
26492
26960
|
const legendConfig = {
|
|
26493
|
-
groups:
|
|
26961
|
+
groups: legendGroups,
|
|
26494
26962
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
26495
26963
|
mode: exportMode ? "export" : "preview",
|
|
26496
|
-
|
|
26964
|
+
// Keep inactive sibling tag groups visible as collapsed pills so the user
|
|
26965
|
+
// can click one to flip the active colouring dimension (preview only —
|
|
26966
|
+
// export shows just the active group). Without this, declaring a second
|
|
26967
|
+
// tag group (e.g. Team) leaves it invisible whenever another group is
|
|
26968
|
+
// active. The app's BoxesAndLinesPreview already wires pill clicks.
|
|
26969
|
+
showInactivePills: true,
|
|
26970
|
+
...controlsGroup !== void 0 && { controlsGroup },
|
|
26971
|
+
...controlsHost !== void 0 && { controlsHost }
|
|
26497
26972
|
};
|
|
26498
26973
|
const legendState = {
|
|
26499
26974
|
activeGroup,
|
|
@@ -26538,7 +27013,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
|
|
|
26538
27013
|
}
|
|
26539
27014
|
});
|
|
26540
27015
|
}
|
|
26541
|
-
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;
|
|
27016
|
+
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;
|
|
26542
27017
|
var init_renderer6 = __esm({
|
|
26543
27018
|
"src/boxes-and-lines/renderer.ts"() {
|
|
26544
27019
|
"use strict";
|
|
@@ -26549,6 +27024,7 @@ var init_renderer6 = __esm({
|
|
|
26549
27024
|
init_legend_layout();
|
|
26550
27025
|
init_title_constants();
|
|
26551
27026
|
init_color_utils();
|
|
27027
|
+
init_colors();
|
|
26552
27028
|
init_tag_groups();
|
|
26553
27029
|
init_inline_markdown();
|
|
26554
27030
|
init_wrapped_desc();
|
|
@@ -26571,6 +27047,8 @@ var init_renderer6 = __esm({
|
|
|
26571
27047
|
GROUP_RX = 8;
|
|
26572
27048
|
GROUP_LABEL_FONT_SIZE = 14;
|
|
26573
27049
|
GROUP_LABEL_ZONE = 32;
|
|
27050
|
+
RAMP_FLOOR = 15;
|
|
27051
|
+
VALUE_FONT_SIZE = 11;
|
|
26574
27052
|
lineGeneratorLR = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26575
27053
|
lineGeneratorTB = d3Shape4.line().x((d) => d.x).y((d) => d.y).curve(d3Shape4.curveBasis);
|
|
26576
27054
|
}
|
|
@@ -27741,8 +28219,9 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27741
28219
|
const containerHeight = exportDims?.height ?? (container.getBoundingClientRect().height || 600);
|
|
27742
28220
|
d3Selection7.select(container).selectAll("*").remove();
|
|
27743
28221
|
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);
|
|
28222
|
+
const appHosted = options?.controlsHost === "app";
|
|
27744
28223
|
const hasControls = !!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
|
|
27745
|
-
const hasLegend = parsed.tagGroups.length > 0 || hasControls;
|
|
28224
|
+
const hasLegend = parsed.tagGroups.length > 0 || hasControls && !appHosted;
|
|
27746
28225
|
const fixedLegend = !isExport && hasLegend;
|
|
27747
28226
|
const legendReserve = fixedLegend ? getMaxLegendReservedHeight(
|
|
27748
28227
|
{
|
|
@@ -27836,7 +28315,10 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
|
|
|
27836
28315
|
}),
|
|
27837
28316
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
27838
28317
|
mode: options?.exportMode ? "export" : "preview",
|
|
27839
|
-
...controlsToggles !== void 0 && { controlsGroup: controlsToggles }
|
|
28318
|
+
...controlsToggles !== void 0 && { controlsGroup: controlsToggles },
|
|
28319
|
+
...options?.controlsHost !== void 0 && {
|
|
28320
|
+
controlsHost: options.controlsHost
|
|
28321
|
+
}
|
|
27840
28322
|
};
|
|
27841
28323
|
const legendState = {
|
|
27842
28324
|
activeGroup: options?.colorByDepth ? null : activeTagGroup !== void 0 ? activeTagGroup : parsed.options["active-tag"] ?? null,
|
|
@@ -28280,8 +28762,8 @@ function computeFieldAlignX(children) {
|
|
|
28280
28762
|
for (const child of children) {
|
|
28281
28763
|
if (child.metadata["_labelField"] === "true" && child.children.length >= 2) {
|
|
28282
28764
|
const labelEl = child.children[0];
|
|
28283
|
-
const
|
|
28284
|
-
maxLabelWidth = Math.max(maxLabelWidth,
|
|
28765
|
+
const labelWidth2 = labelEl.label.length * CHAR_WIDTH5;
|
|
28766
|
+
maxLabelWidth = Math.max(maxLabelWidth, labelWidth2);
|
|
28285
28767
|
labelFieldCount++;
|
|
28286
28768
|
}
|
|
28287
28769
|
}
|
|
@@ -33245,7 +33727,7 @@ function hasRoles(node) {
|
|
|
33245
33727
|
function computeNodeWidth2(node, expanded, options) {
|
|
33246
33728
|
const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
|
|
33247
33729
|
const badgeLen = badgeVal > 0 ? `${badgeVal}x`.length + 2 : 0;
|
|
33248
|
-
const
|
|
33730
|
+
const labelWidth2 = (node.label.length + badgeLen) * CHAR_WIDTH7 + PADDING_X3;
|
|
33249
33731
|
const allKeys = [];
|
|
33250
33732
|
if (node.computedRps > 0) allKeys.push("RPS");
|
|
33251
33733
|
if (expanded) {
|
|
@@ -33289,7 +33771,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33289
33771
|
allKeys.push("overflow");
|
|
33290
33772
|
}
|
|
33291
33773
|
}
|
|
33292
|
-
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2,
|
|
33774
|
+
if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
|
|
33293
33775
|
const maxKeyLen = Math.max(...allKeys.map((k) => k.length));
|
|
33294
33776
|
let maxRowWidth = 0;
|
|
33295
33777
|
if (node.computedRps > 0) {
|
|
@@ -33377,7 +33859,7 @@ function computeNodeWidth2(node, expanded, options) {
|
|
|
33377
33859
|
truncated.length * META_CHAR_WIDTH3 + PADDING_X3
|
|
33378
33860
|
);
|
|
33379
33861
|
}
|
|
33380
|
-
return Math.max(MIN_NODE_WIDTH2,
|
|
33862
|
+
return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
|
|
33381
33863
|
}
|
|
33382
33864
|
function computeNodeHeight2(node, expanded, options) {
|
|
33383
33865
|
const propCount = countDisplayProps(node, expanded, options);
|
|
@@ -34926,8 +35408,9 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
|
|
|
34926
35408
|
}
|
|
34927
35409
|
return groups;
|
|
34928
35410
|
}
|
|
34929
|
-
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false) {
|
|
35411
|
+
function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDark, activeGroup, playback, exportMode = false, controlsHost) {
|
|
34930
35412
|
if (legendGroups.length === 0 && !playback) return;
|
|
35413
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
34931
35414
|
const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
|
|
34932
35415
|
if (activeGroup) {
|
|
34933
35416
|
legendG.attr("data-legend-active", activeGroup.toLowerCase());
|
|
@@ -34936,14 +35419,29 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
34936
35419
|
name: g.name,
|
|
34937
35420
|
entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
|
|
34938
35421
|
}));
|
|
34939
|
-
if (playback) {
|
|
35422
|
+
if (playback && !appHostedPlayback) {
|
|
34940
35423
|
allGroups.push({ name: "Playback", entries: [] });
|
|
34941
35424
|
}
|
|
34942
35425
|
const legendConfig = {
|
|
34943
35426
|
groups: allGroups,
|
|
34944
35427
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
34945
35428
|
mode: exportMode ? "export" : "preview",
|
|
34946
|
-
showEmptyGroups: true
|
|
35429
|
+
showEmptyGroups: true,
|
|
35430
|
+
...appHostedPlayback && {
|
|
35431
|
+
controlsHost: "app",
|
|
35432
|
+
controlsGroup: {
|
|
35433
|
+
toggles: [
|
|
35434
|
+
{
|
|
35435
|
+
id: "playback",
|
|
35436
|
+
type: "toggle",
|
|
35437
|
+
label: "Playback",
|
|
35438
|
+
active: true,
|
|
35439
|
+
onToggle: () => {
|
|
35440
|
+
}
|
|
35441
|
+
}
|
|
35442
|
+
]
|
|
35443
|
+
}
|
|
35444
|
+
}
|
|
34947
35445
|
};
|
|
34948
35446
|
const legendState = { activeGroup };
|
|
34949
35447
|
renderLegendD3(
|
|
@@ -34994,8 +35492,9 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
|
|
|
34994
35492
|
}
|
|
34995
35493
|
}
|
|
34996
35494
|
}
|
|
34997
|
-
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes) {
|
|
35495
|
+
function renderInfra(container, layout, palette, isDark, title, titleLineNumber, tagGroups, activeGroup, animate, playback, expandedNodeIds, exportMode, collapsedNodes, controlsHost) {
|
|
34998
35496
|
d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
35497
|
+
const appHostedPlayback = controlsHost === "app" && !!playback;
|
|
34999
35498
|
const ctx = ScaleContext.identity();
|
|
35000
35499
|
const sc = buildScaledConstants(ctx);
|
|
35001
35500
|
const legendGroups = computeInfraLegendGroups(
|
|
@@ -35004,7 +35503,7 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35004
35503
|
palette,
|
|
35005
35504
|
layout.edges
|
|
35006
35505
|
);
|
|
35007
|
-
const hasLegend = legendGroups.length > 0 || !!playback;
|
|
35506
|
+
const hasLegend = legendGroups.length > 0 || !!playback && !appHostedPlayback;
|
|
35008
35507
|
const fixedLegend = !exportMode && hasLegend;
|
|
35009
35508
|
const legendDynamicH = hasLegend ? getMaxLegendReservedHeight(
|
|
35010
35509
|
{
|
|
@@ -35148,7 +35647,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35148
35647
|
isDark,
|
|
35149
35648
|
activeGroup ?? null,
|
|
35150
35649
|
playback ?? void 0,
|
|
35151
|
-
exportMode
|
|
35650
|
+
exportMode,
|
|
35651
|
+
controlsHost
|
|
35152
35652
|
);
|
|
35153
35653
|
legendSvg.selectAll(".infra-legend-group").style("pointer-events", "auto");
|
|
35154
35654
|
} else {
|
|
@@ -35161,7 +35661,8 @@ function renderInfra(container, layout, palette, isDark, title, titleLineNumber,
|
|
|
35161
35661
|
isDark,
|
|
35162
35662
|
activeGroup ?? null,
|
|
35163
35663
|
playback ?? void 0,
|
|
35164
|
-
exportMode
|
|
35664
|
+
exportMode,
|
|
35665
|
+
controlsHost
|
|
35165
35666
|
);
|
|
35166
35667
|
}
|
|
35167
35668
|
}
|
|
@@ -42796,6 +43297,9 @@ function renderTechRadar(container, parsed, palette, isDark, onClickItem, export
|
|
|
42796
43297
|
onToggle: (active) => options.onToggleListing(active)
|
|
42797
43298
|
}
|
|
42798
43299
|
]
|
|
43300
|
+
},
|
|
43301
|
+
...options.controlsHost !== void 0 && {
|
|
43302
|
+
controlsHost: options.controlsHost
|
|
42799
43303
|
}
|
|
42800
43304
|
};
|
|
42801
43305
|
const legendState = {
|
|
@@ -44619,7 +45123,7 @@ function computeCycleLayout(parsed, options) {
|
|
|
44619
45123
|
const circleNodes = parsed.options["circle-nodes"] === "true";
|
|
44620
45124
|
const nodeDims = parsed.nodes.map((node) => {
|
|
44621
45125
|
const hasDesc = !hideDescriptions && node.description.length > 0;
|
|
44622
|
-
const
|
|
45126
|
+
const labelWidth2 = Math.max(
|
|
44623
45127
|
MIN_NODE_WIDTH4,
|
|
44624
45128
|
node.label.length * LABEL_CHAR_W + NODE_PAD_X * 2
|
|
44625
45129
|
);
|
|
@@ -44628,12 +45132,12 @@ function computeCycleLayout(parsed, options) {
|
|
|
44628
45132
|
}
|
|
44629
45133
|
if (!hasDesc) {
|
|
44630
45134
|
return {
|
|
44631
|
-
width: Math.min(MAX_NODE_WIDTH3,
|
|
45135
|
+
width: Math.min(MAX_NODE_WIDTH3, labelWidth2),
|
|
44632
45136
|
height: PLAIN_NODE_HEIGHT,
|
|
44633
45137
|
wrappedDesc: []
|
|
44634
45138
|
};
|
|
44635
45139
|
}
|
|
44636
|
-
return chooseDescribedRectDims(node.description,
|
|
45140
|
+
return chooseDescribedRectDims(node.description, labelWidth2);
|
|
44637
45141
|
});
|
|
44638
45142
|
if (circleNodes) {
|
|
44639
45143
|
const maxDiam = Math.max(...nodeDims.map((d) => d.width));
|
|
@@ -44829,10 +45333,10 @@ function computeCycleLayout(parsed, options) {
|
|
|
44829
45333
|
scale
|
|
44830
45334
|
};
|
|
44831
45335
|
}
|
|
44832
|
-
function chooseDescribedRectDims(description,
|
|
45336
|
+
function chooseDescribedRectDims(description, labelWidth2) {
|
|
44833
45337
|
const minW = Math.min(
|
|
44834
45338
|
MAX_NODE_WIDTH3,
|
|
44835
|
-
Math.max(MIN_NODE_WIDTH4,
|
|
45339
|
+
Math.max(MIN_NODE_WIDTH4, labelWidth2, DESC_MIN_WIDTH)
|
|
44836
45340
|
);
|
|
44837
45341
|
let best = null;
|
|
44838
45342
|
let bestScore = Infinity;
|
|
@@ -45260,7 +45764,8 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45260
45764
|
const hideDescriptions = (renderOptions?.hideDescriptions ?? false) || parsed.options["no-descriptions"] === "true" || viewState?.hd === true;
|
|
45261
45765
|
const showDescriptions = !hideDescriptions;
|
|
45262
45766
|
const hasDescriptions = parsed.nodes.some((n) => n.description.length > 0) || parsed.edges.some((e) => e.description.length > 0);
|
|
45263
|
-
const
|
|
45767
|
+
const appHostedControls = renderOptions?.controlsHost === "app";
|
|
45768
|
+
const hasLegend = !appHostedControls && hasDescriptions && !!renderOptions?.onToggleDescriptions;
|
|
45264
45769
|
const showTitle = !!parsed.title && parsed.options["no-title"] !== "on";
|
|
45265
45770
|
const legendOffset = hasLegend ? sLegendHeight : 0;
|
|
45266
45771
|
const layoutHeight = height - (showTitle ? sTitleAreaHeight : 0) - legendOffset;
|
|
@@ -45297,7 +45802,10 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
|
|
|
45297
45802
|
groups: [],
|
|
45298
45803
|
position: { placement: "top-center", titleRelation: "below-title" },
|
|
45299
45804
|
mode: renderOptions?.exportMode ? "export" : "preview",
|
|
45300
|
-
controlsGroup
|
|
45805
|
+
controlsGroup,
|
|
45806
|
+
...renderOptions?.controlsHost !== void 0 && {
|
|
45807
|
+
controlsHost: renderOptions.controlsHost
|
|
45808
|
+
}
|
|
45301
45809
|
};
|
|
45302
45810
|
const legendState = {
|
|
45303
45811
|
activeGroup: null,
|
|
@@ -45568,6 +46076,107 @@ function featureIndex(topo) {
|
|
|
45568
46076
|
}
|
|
45569
46077
|
return idx;
|
|
45570
46078
|
}
|
|
46079
|
+
function buildAdjacency(topo) {
|
|
46080
|
+
const cached = adjacencyCache.get(topo);
|
|
46081
|
+
if (cached) return cached;
|
|
46082
|
+
const geometries = geomObject(topo).geometries;
|
|
46083
|
+
const nb = (0, import_topojson_client.neighbors)(geometries);
|
|
46084
|
+
const sets = /* @__PURE__ */ new Map();
|
|
46085
|
+
geometries.forEach((g, i) => {
|
|
46086
|
+
if (!g.type || g.type === "null") return;
|
|
46087
|
+
let set = sets.get(g.id);
|
|
46088
|
+
if (!set) {
|
|
46089
|
+
set = /* @__PURE__ */ new Set();
|
|
46090
|
+
sets.set(g.id, set);
|
|
46091
|
+
}
|
|
46092
|
+
for (const j of nb[i] ?? []) {
|
|
46093
|
+
const nid = geometries[j]?.id;
|
|
46094
|
+
if (nid && nid !== g.id) set.add(nid);
|
|
46095
|
+
}
|
|
46096
|
+
});
|
|
46097
|
+
const out = /* @__PURE__ */ new Map();
|
|
46098
|
+
for (const [iso, set] of sets) out.set(iso, [...set].sort());
|
|
46099
|
+
adjacencyCache.set(topo, out);
|
|
46100
|
+
return out;
|
|
46101
|
+
}
|
|
46102
|
+
function decodeFeatures(topo) {
|
|
46103
|
+
return geomObject(topo).geometries.map((g) => {
|
|
46104
|
+
const f = (0, import_topojson_client.feature)(topo, g);
|
|
46105
|
+
return {
|
|
46106
|
+
type: "Feature",
|
|
46107
|
+
id: g.id,
|
|
46108
|
+
properties: g.properties,
|
|
46109
|
+
geometry: f.geometry
|
|
46110
|
+
};
|
|
46111
|
+
});
|
|
46112
|
+
}
|
|
46113
|
+
function pointInRing(lon, lat, ring) {
|
|
46114
|
+
let inside = false;
|
|
46115
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46116
|
+
const xi = ring[i][0];
|
|
46117
|
+
const yi = ring[i][1];
|
|
46118
|
+
const xj = ring[j][0];
|
|
46119
|
+
const yj = ring[j][1];
|
|
46120
|
+
const intersect = yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi;
|
|
46121
|
+
if (intersect) inside = !inside;
|
|
46122
|
+
}
|
|
46123
|
+
return inside;
|
|
46124
|
+
}
|
|
46125
|
+
function pointOnRingEdge(lon, lat, ring) {
|
|
46126
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
46127
|
+
const xi = ring[i][0];
|
|
46128
|
+
const yi = ring[i][1];
|
|
46129
|
+
const xj = ring[j][0];
|
|
46130
|
+
const yj = ring[j][1];
|
|
46131
|
+
if (lon < Math.min(xi, xj) - EDGE_EPS || lon > Math.max(xi, xj) + EDGE_EPS)
|
|
46132
|
+
continue;
|
|
46133
|
+
if (lat < Math.min(yi, yj) - EDGE_EPS || lat > Math.max(yi, yj) + EDGE_EPS)
|
|
46134
|
+
continue;
|
|
46135
|
+
const cross = (xj - xi) * (lat - yi) - (yj - yi) * (lon - xi);
|
|
46136
|
+
if (Math.abs(cross) <= EDGE_EPS) return true;
|
|
46137
|
+
}
|
|
46138
|
+
return false;
|
|
46139
|
+
}
|
|
46140
|
+
function pointInGeometry(geometry, lon, lat) {
|
|
46141
|
+
const g = geometry;
|
|
46142
|
+
if (!g) return false;
|
|
46143
|
+
const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
|
|
46144
|
+
for (const rings of polys) {
|
|
46145
|
+
if (!rings.length) continue;
|
|
46146
|
+
if (pointOnRingEdge(lon, lat, rings[0])) return true;
|
|
46147
|
+
if (!pointInRing(lon, lat, rings[0])) continue;
|
|
46148
|
+
let inHole = false;
|
|
46149
|
+
for (let h = 1; h < rings.length; h++) {
|
|
46150
|
+
if (pointInRing(lon, lat, rings[h]) && !pointOnRingEdge(lon, lat, rings[h])) {
|
|
46151
|
+
inHole = true;
|
|
46152
|
+
break;
|
|
46153
|
+
}
|
|
46154
|
+
}
|
|
46155
|
+
if (!inHole) return true;
|
|
46156
|
+
}
|
|
46157
|
+
return false;
|
|
46158
|
+
}
|
|
46159
|
+
function regionAt(lonLat, countries, states) {
|
|
46160
|
+
const lon = lonLat[0];
|
|
46161
|
+
const lat = lonLat[1];
|
|
46162
|
+
let country = null;
|
|
46163
|
+
for (const f of countries) {
|
|
46164
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46165
|
+
country = { iso: f.id, name: f.properties.name };
|
|
46166
|
+
break;
|
|
46167
|
+
}
|
|
46168
|
+
}
|
|
46169
|
+
let state = null;
|
|
46170
|
+
if (country?.iso === "US" && states) {
|
|
46171
|
+
for (const f of states) {
|
|
46172
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
46173
|
+
state = { iso: f.id, name: f.properties.name };
|
|
46174
|
+
break;
|
|
46175
|
+
}
|
|
46176
|
+
}
|
|
46177
|
+
}
|
|
46178
|
+
return { country, state };
|
|
46179
|
+
}
|
|
45571
46180
|
function featureBbox(topo, geomId) {
|
|
45572
46181
|
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45573
46182
|
if (!geom) return null;
|
|
@@ -45685,13 +46294,15 @@ function unionLongitudes(lons) {
|
|
|
45685
46294
|
}
|
|
45686
46295
|
return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
|
|
45687
46296
|
}
|
|
45688
|
-
var import_topojson_client, import_d3_geo, fold, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
46297
|
+
var import_topojson_client, import_d3_geo, fold, adjacencyCache, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
45689
46298
|
var init_geo = __esm({
|
|
45690
46299
|
"src/map/geo.ts"() {
|
|
45691
46300
|
"use strict";
|
|
45692
46301
|
import_topojson_client = require("topojson-client");
|
|
45693
46302
|
import_d3_geo = require("d3-geo");
|
|
45694
46303
|
fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
46304
|
+
adjacencyCache = /* @__PURE__ */ new WeakMap();
|
|
46305
|
+
EDGE_EPS = 1e-9;
|
|
45695
46306
|
DETACH_GAP_DEG = 10;
|
|
45696
46307
|
DETACH_AREA_FRAC = 0.25;
|
|
45697
46308
|
}
|
|
@@ -45711,6 +46322,12 @@ function looksUS(lat, lon) {
|
|
|
45711
46322
|
if (lat < 15 || lat > 72) return false;
|
|
45712
46323
|
return lon >= -180 && lon <= -64 || lon >= 172;
|
|
45713
46324
|
}
|
|
46325
|
+
function looksNorthAmericaNeighbor(lat, lon) {
|
|
46326
|
+
return lat >= 14 && lat <= 72 && lon >= -141 && lon <= -52;
|
|
46327
|
+
}
|
|
46328
|
+
function isWholeSphere(bb) {
|
|
46329
|
+
return bb[0][0] <= -179 && bb[1][0] >= 179 && bb[0][1] <= -89 && bb[1][1] >= 89;
|
|
46330
|
+
}
|
|
45714
46331
|
function resolveMap(parsed, data) {
|
|
45715
46332
|
const diagnostics = [...parsed.diagnostics];
|
|
45716
46333
|
const err = (line12, message, code) => {
|
|
@@ -45721,9 +46338,6 @@ function resolveMap(parsed, data) {
|
|
|
45721
46338
|
};
|
|
45722
46339
|
const result = {
|
|
45723
46340
|
title: parsed.title,
|
|
45724
|
-
...parsed.directives.subtitle !== void 0 && {
|
|
45725
|
-
subtitle: parsed.directives.subtitle
|
|
45726
|
-
},
|
|
45727
46341
|
...parsed.directives.caption !== void 0 && {
|
|
45728
46342
|
caption: parsed.directives.caption
|
|
45729
46343
|
},
|
|
@@ -45733,7 +46347,7 @@ function resolveMap(parsed, data) {
|
|
|
45733
46347
|
// renderer's job (step 4) — the resolver only carries `tags` + `tagGroups`
|
|
45734
46348
|
// through; it never resolves a tag value to a palette color (#10).
|
|
45735
46349
|
directives: { ...parsed.directives },
|
|
45736
|
-
basemaps: { world: "
|
|
46350
|
+
basemaps: { world: "detail", subdivisions: [] },
|
|
45737
46351
|
regions: [],
|
|
45738
46352
|
pois: [],
|
|
45739
46353
|
edges: [],
|
|
@@ -45742,7 +46356,8 @@ function resolveMap(parsed, data) {
|
|
|
45742
46356
|
[-180, -85],
|
|
45743
46357
|
[180, 85]
|
|
45744
46358
|
],
|
|
45745
|
-
projection: "
|
|
46359
|
+
projection: "equirectangular",
|
|
46360
|
+
poiFrameContainers: [],
|
|
45746
46361
|
diagnostics,
|
|
45747
46362
|
error: parsed.error
|
|
45748
46363
|
};
|
|
@@ -45752,7 +46367,10 @@ function resolveMap(parsed, data) {
|
|
|
45752
46367
|
...[...countryIndex.values()].map((v) => v.name),
|
|
45753
46368
|
...[...usStateIndex.values()].map((v) => v.name)
|
|
45754
46369
|
];
|
|
45755
|
-
const
|
|
46370
|
+
const localeRaw = parsed.directives.locale?.toUpperCase();
|
|
46371
|
+
const localeCountry = localeRaw ? localeRaw.split("-")[0] : void 0;
|
|
46372
|
+
const localeSubdivision = localeRaw && /^[A-Z]{2}-/.test(localeRaw) ? localeRaw : void 0;
|
|
46373
|
+
const usScoped = localeCountry === "US" || parsed.regions.some((r) => {
|
|
45756
46374
|
const f = fold(r.name);
|
|
45757
46375
|
return usStateIndex.has(f) && !countryIndex.has(f);
|
|
45758
46376
|
}) || parsed.regions.some(
|
|
@@ -45903,7 +46521,7 @@ function resolveMap(parsed, data) {
|
|
|
45903
46521
|
if (!scope)
|
|
45904
46522
|
warn(
|
|
45905
46523
|
line12,
|
|
45906
|
-
`"${name}" is ambiguous \u2014 resolved to the most-populous match.`,
|
|
46524
|
+
`"${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.`,
|
|
45907
46525
|
"W_MAP_AMBIGUOUS_NAME"
|
|
45908
46526
|
);
|
|
45909
46527
|
}
|
|
@@ -45916,17 +46534,21 @@ function resolveMap(parsed, data) {
|
|
|
45916
46534
|
return fold(pos.name);
|
|
45917
46535
|
};
|
|
45918
46536
|
const poiCountries = [];
|
|
45919
|
-
let
|
|
46537
|
+
let anyUsPoi = false;
|
|
46538
|
+
let anyNonNaPoi = false;
|
|
45920
46539
|
const noteCountry = (iso) => {
|
|
45921
46540
|
if (iso) {
|
|
45922
46541
|
poiCountries.push(iso);
|
|
45923
|
-
if (iso
|
|
46542
|
+
if (iso === "US") anyUsPoi = true;
|
|
46543
|
+
if (iso !== "US" && iso !== "CA" && iso !== "MX") anyNonNaPoi = true;
|
|
45924
46544
|
}
|
|
45925
46545
|
};
|
|
45926
46546
|
const deferred = [];
|
|
45927
46547
|
for (const p of parsed.pois) {
|
|
45928
46548
|
if (p.pos.kind === "coords") {
|
|
45929
|
-
if (
|
|
46549
|
+
if (looksUS(p.pos.lat, p.pos.lon)) anyUsPoi = true;
|
|
46550
|
+
else if (!looksNorthAmericaNeighbor(p.pos.lat, p.pos.lon))
|
|
46551
|
+
anyNonNaPoi = true;
|
|
45930
46552
|
addResolvedPoi(p.pos.lat, p.pos.lon, p);
|
|
45931
46553
|
continue;
|
|
45932
46554
|
}
|
|
@@ -45944,14 +46566,15 @@ function resolveMap(parsed, data) {
|
|
|
45944
46566
|
deferred.push(p);
|
|
45945
46567
|
}
|
|
45946
46568
|
}
|
|
45947
|
-
const inferredCountry =
|
|
46569
|
+
const inferredCountry = localeCountry ?? mostCommonCountry(regions, poiCountries) ?? void 0;
|
|
46570
|
+
const inferredScope = localeSubdivision ?? inferredCountry;
|
|
45948
46571
|
for (const p of deferred) {
|
|
45949
46572
|
if (p.pos.kind !== "name") continue;
|
|
45950
46573
|
const got = lookupName(
|
|
45951
46574
|
p.pos.name,
|
|
45952
46575
|
p.pos.scope,
|
|
45953
46576
|
p.lineNumber,
|
|
45954
|
-
|
|
46577
|
+
inferredScope,
|
|
45955
46578
|
true
|
|
45956
46579
|
);
|
|
45957
46580
|
if (got.kind === "ok") {
|
|
@@ -46021,7 +46644,8 @@ function resolveMap(parsed, data) {
|
|
|
46021
46644
|
const meta = sizeValue !== void 0 ? { value: sizeValue } : {};
|
|
46022
46645
|
if (pos.kind === "coords") {
|
|
46023
46646
|
const id = alias ? fold(alias) : `@${pos.lat},${pos.lon}`;
|
|
46024
|
-
if (
|
|
46647
|
+
if (looksUS(pos.lat, pos.lon)) anyUsPoi = true;
|
|
46648
|
+
else if (!looksNorthAmericaNeighbor(pos.lat, pos.lon)) anyNonNaPoi = true;
|
|
46025
46649
|
if (!registry.has(id)) {
|
|
46026
46650
|
registerPoi(
|
|
46027
46651
|
id,
|
|
@@ -46044,7 +46668,7 @@ function resolveMap(parsed, data) {
|
|
|
46044
46668
|
if (registry.has(f)) return f;
|
|
46045
46669
|
const aliased = declaredByName.get(f);
|
|
46046
46670
|
if (aliased) return aliased;
|
|
46047
|
-
const got = lookupName(pos.name, pos.scope, line12,
|
|
46671
|
+
const got = lookupName(pos.name, pos.scope, line12, inferredScope, true);
|
|
46048
46672
|
if (got.kind !== "ok") return null;
|
|
46049
46673
|
noteCountry(got.iso);
|
|
46050
46674
|
registerPoi(
|
|
@@ -46101,9 +46725,12 @@ function resolveMap(parsed, data) {
|
|
|
46101
46725
|
}
|
|
46102
46726
|
routes.push({ stopIds, legs, lineNumber: rt.lineNumber });
|
|
46103
46727
|
}
|
|
46728
|
+
const hasUsContent = usSubdivisionReferenced || anyUsPoi || localeCountry === "US";
|
|
46729
|
+
const usOriented = !anyNonNaPoi && !regions.some(
|
|
46730
|
+
(r) => r.layer === "country" && !["US", "CA", "MX"].includes(r.iso)
|
|
46731
|
+
) && hasUsContent;
|
|
46104
46732
|
const subdivisions = [];
|
|
46105
|
-
if (usSubdivisionReferenced ||
|
|
46106
|
-
subdivisions.push("us-states");
|
|
46733
|
+
if (usSubdivisionReferenced || usOriented) subdivisions.push("us-states");
|
|
46107
46734
|
const regionBoxes = [];
|
|
46108
46735
|
for (const ref of referencedRegionIds) {
|
|
46109
46736
|
const bb = featureBbox(data.usStates, ref.id);
|
|
@@ -46121,17 +46748,51 @@ function resolveMap(parsed, data) {
|
|
|
46121
46748
|
[-180, -85],
|
|
46122
46749
|
[180, 85]
|
|
46123
46750
|
];
|
|
46124
|
-
|
|
46751
|
+
const basePad = regions.length > 0 ? REGION_PAD_FRACTION : PAD_FRACTION;
|
|
46752
|
+
let extent2 = unioned ? pad(unioned, basePad) : DEFAULT_EXTENT;
|
|
46753
|
+
const isPoiOnly = pois.length > 0 && regions.length === 0;
|
|
46754
|
+
const containerRegionIds = [];
|
|
46755
|
+
if (isPoiOnly) {
|
|
46756
|
+
const countries = decodeFeatures(data.worldDetail);
|
|
46757
|
+
const states = decodeFeatures(data.usStates);
|
|
46758
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46759
|
+
const containerBoxes = [];
|
|
46760
|
+
for (const p of pois) {
|
|
46761
|
+
const { country, state } = regionAt([p.lon, p.lat], countries, states);
|
|
46762
|
+
const id = state?.iso ?? country?.iso;
|
|
46763
|
+
if (!id || seen.has(id)) continue;
|
|
46764
|
+
seen.add(id);
|
|
46765
|
+
containerRegionIds.push(id);
|
|
46766
|
+
const bb = state ? featureBbox(data.usStates, id) : featureBboxPrimary(data.worldCoarse, id);
|
|
46767
|
+
if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
|
|
46768
|
+
}
|
|
46769
|
+
const containerUnion = unionExtent(containerBoxes, points);
|
|
46770
|
+
if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
|
|
46771
|
+
}
|
|
46772
|
+
if (isPoiOnly) {
|
|
46773
|
+
const cx = (extent2[0][0] + extent2[1][0]) / 2;
|
|
46774
|
+
const cy = (extent2[0][1] + extent2[1][1]) / 2;
|
|
46775
|
+
const lon = extent2[1][0] - extent2[0][0];
|
|
46776
|
+
const lat = extent2[1][1] - extent2[0][1];
|
|
46777
|
+
const longer = Math.max(lon, lat);
|
|
46778
|
+
if (longer > 0 && longer < POI_ZOOM_FLOOR_DEG) {
|
|
46779
|
+
const k = POI_ZOOM_FLOOR_DEG / longer;
|
|
46780
|
+
const halfLon = lon * k / 2;
|
|
46781
|
+
const halfLat = lat * k / 2;
|
|
46782
|
+
extent2 = [
|
|
46783
|
+
[cx - halfLon, cy - halfLat],
|
|
46784
|
+
[cx + halfLon, cy + halfLat]
|
|
46785
|
+
];
|
|
46786
|
+
}
|
|
46787
|
+
}
|
|
46125
46788
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
46126
46789
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
46127
46790
|
const span = Math.max(lonSpan, latSpan);
|
|
46128
46791
|
const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
|
|
46129
|
-
const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
46130
46792
|
let projection;
|
|
46131
|
-
|
|
46132
|
-
|
|
46133
|
-
|
|
46134
|
-
} else if (usDominant) {
|
|
46793
|
+
if (isPoiOnly && usOriented && lonSpan < US_NATIONAL_LON_SPAN) {
|
|
46794
|
+
projection = "mercator";
|
|
46795
|
+
} else if (usOriented) {
|
|
46135
46796
|
projection = "albers-usa";
|
|
46136
46797
|
} else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
|
|
46137
46798
|
projection = "equirectangular";
|
|
@@ -46149,11 +46810,20 @@ function resolveMap(parsed, data) {
|
|
|
46149
46810
|
result.edges = edges;
|
|
46150
46811
|
result.routes = routes;
|
|
46151
46812
|
result.basemaps = {
|
|
46152
|
-
|
|
46813
|
+
// Tier is intentionally pinned to detail (50m) at ALL scales. Diagrammo maps
|
|
46814
|
+
// are presentational (palette tints, relief hachures, POI hubs), not
|
|
46815
|
+
// survey-grade — recognizability > generalization: 110m coarse drops the
|
|
46816
|
+
// Italian boot to a stump at world scale. `WORLD_SPAN` lives on only for the
|
|
46817
|
+
// projection decision (the `usOriented`/`span > WORLD_SPAN` chain above); it
|
|
46818
|
+
// no longer gates basemap resolution.
|
|
46819
|
+
// `worldCoarse` is still loaded — it's the authoritative name/bbox index
|
|
46820
|
+
// (featureIndex, featureBboxPrimary), not dead code.
|
|
46821
|
+
world: "detail",
|
|
46153
46822
|
subdivisions
|
|
46154
46823
|
};
|
|
46155
46824
|
result.extent = extent2;
|
|
46156
46825
|
result.projection = projection;
|
|
46826
|
+
result.poiFrameContainers = containerRegionIds;
|
|
46157
46827
|
result.error = parsed.error ?? firstError(diagnostics);
|
|
46158
46828
|
return result;
|
|
46159
46829
|
}
|
|
@@ -46190,7 +46860,7 @@ function firstError(diags) {
|
|
|
46190
46860
|
const e = diags.find((d) => d.severity === "error");
|
|
46191
46861
|
return e ? formatDgmoError(e) : null;
|
|
46192
46862
|
}
|
|
46193
|
-
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
|
|
46863
|
+
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;
|
|
46194
46864
|
var init_resolver2 = __esm({
|
|
46195
46865
|
"src/map/resolver.ts"() {
|
|
46196
46866
|
"use strict";
|
|
@@ -46199,8 +46869,11 @@ var init_resolver2 = __esm({
|
|
|
46199
46869
|
WORLD_SPAN = 90;
|
|
46200
46870
|
MERCATOR_MAX_LAT = 80;
|
|
46201
46871
|
PAD_FRACTION = 0.05;
|
|
46872
|
+
REGION_PAD_FRACTION = 0.12;
|
|
46202
46873
|
WORLD_LAT_SOUTH = -58;
|
|
46203
46874
|
WORLD_LAT_NORTH = 78;
|
|
46875
|
+
POI_ZOOM_FLOOR_DEG = 7;
|
|
46876
|
+
US_NATIONAL_LON_SPAN = 48;
|
|
46204
46877
|
REGION_ALIASES = {
|
|
46205
46878
|
// Common everyday names → the Natural-Earth display name actually shipped.
|
|
46206
46879
|
"united states": "united states of america",
|
|
@@ -46278,17 +46951,305 @@ var init_resolver2 = __esm({
|
|
|
46278
46951
|
}
|
|
46279
46952
|
});
|
|
46280
46953
|
|
|
46954
|
+
// src/map/colorize.ts
|
|
46955
|
+
function assignColors(isos, adjacency) {
|
|
46956
|
+
const sorted = [...isos].sort();
|
|
46957
|
+
const byIso = /* @__PURE__ */ new Map();
|
|
46958
|
+
let maxIndex = -1;
|
|
46959
|
+
for (const iso of sorted) {
|
|
46960
|
+
const taken = /* @__PURE__ */ new Set();
|
|
46961
|
+
for (const n of adjacency.get(iso) ?? []) {
|
|
46962
|
+
const c = byIso.get(n);
|
|
46963
|
+
if (c !== void 0) taken.add(c);
|
|
46964
|
+
}
|
|
46965
|
+
let h = 0;
|
|
46966
|
+
while (taken.has(h)) h++;
|
|
46967
|
+
byIso.set(iso, h);
|
|
46968
|
+
if (h > maxIndex) maxIndex = h;
|
|
46969
|
+
}
|
|
46970
|
+
return { byIso, huesNeeded: maxIndex + 1 };
|
|
46971
|
+
}
|
|
46972
|
+
var init_colorize = __esm({
|
|
46973
|
+
"src/map/colorize.ts"() {
|
|
46974
|
+
"use strict";
|
|
46975
|
+
}
|
|
46976
|
+
});
|
|
46977
|
+
|
|
46978
|
+
// src/map/context-labels.ts
|
|
46979
|
+
function tierBand(maxSpanDeg) {
|
|
46980
|
+
if (maxSpanDeg >= 90) return "world";
|
|
46981
|
+
if (maxSpanDeg >= 20) return "continental";
|
|
46982
|
+
if (maxSpanDeg >= 5) return "regional";
|
|
46983
|
+
return "local";
|
|
46984
|
+
}
|
|
46985
|
+
function labelBudget(width, height, band) {
|
|
46986
|
+
const bandCap = {
|
|
46987
|
+
world: 6,
|
|
46988
|
+
continental: 5,
|
|
46989
|
+
regional: 4,
|
|
46990
|
+
local: 3
|
|
46991
|
+
};
|
|
46992
|
+
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
46993
|
+
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
46994
|
+
}
|
|
46995
|
+
function waterEligible(tier, kind, band) {
|
|
46996
|
+
switch (band) {
|
|
46997
|
+
case "world":
|
|
46998
|
+
return tier <= 1 && (kind === "ocean" || kind === "sea");
|
|
46999
|
+
case "continental":
|
|
47000
|
+
return tier <= 2;
|
|
47001
|
+
case "regional":
|
|
47002
|
+
return tier <= 3;
|
|
47003
|
+
case "local":
|
|
47004
|
+
return tier <= 4;
|
|
47005
|
+
}
|
|
47006
|
+
}
|
|
47007
|
+
function insideViewport(p, width, height) {
|
|
47008
|
+
return !!p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && p[0] >= 0 && p[0] <= width && p[1] >= 0 && p[1] <= height;
|
|
47009
|
+
}
|
|
47010
|
+
function labelWidth(text, letterSpacing) {
|
|
47011
|
+
const spacing = letterSpacing > 0 ? Math.max(0, text.length - 1) * letterSpacing : 0;
|
|
47012
|
+
return measureLegendText(text, FONT) + spacing + 2 * PADX;
|
|
47013
|
+
}
|
|
47014
|
+
function wrapLabel2(text, letterSpacing) {
|
|
47015
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
47016
|
+
if (words.length <= 1) return [text];
|
|
47017
|
+
const maxLines = words.length >= 4 ? 3 : 2;
|
|
47018
|
+
const n = words.length;
|
|
47019
|
+
let best = null;
|
|
47020
|
+
for (let mask = 0; mask < 1 << n - 1; mask++) {
|
|
47021
|
+
const lines = [];
|
|
47022
|
+
let cur = [words[0]];
|
|
47023
|
+
for (let i = 1; i < n; i++) {
|
|
47024
|
+
if (mask & 1 << i - 1) {
|
|
47025
|
+
lines.push(cur.join(" "));
|
|
47026
|
+
cur = [words[i]];
|
|
47027
|
+
} else cur.push(words[i]);
|
|
47028
|
+
}
|
|
47029
|
+
lines.push(cur.join(" "));
|
|
47030
|
+
if (lines.length > maxLines) continue;
|
|
47031
|
+
const cost = Math.round(
|
|
47032
|
+
Math.max(...lines.map((l) => labelWidth(l, letterSpacing)))
|
|
47033
|
+
);
|
|
47034
|
+
const head = labelWidth(lines[0], letterSpacing);
|
|
47035
|
+
if (!best || cost < best.cost || cost === best.cost && lines.length < best.lines.length || cost === best.cost && lines.length === best.lines.length && head > best.head)
|
|
47036
|
+
best = { lines, cost, head };
|
|
47037
|
+
}
|
|
47038
|
+
return best?.lines ?? [text];
|
|
47039
|
+
}
|
|
47040
|
+
function rectAround(cx, cy, lines, letterSpacing) {
|
|
47041
|
+
const w = Math.max(...lines.map((l) => labelWidth(l, letterSpacing)));
|
|
47042
|
+
const h = (lines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY;
|
|
47043
|
+
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
47044
|
+
}
|
|
47045
|
+
function rectFits(r, width, height) {
|
|
47046
|
+
return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
|
|
47047
|
+
}
|
|
47048
|
+
function overlapsPadded(a, b, pad2) {
|
|
47049
|
+
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;
|
|
47050
|
+
}
|
|
47051
|
+
function placeContextLabels(args) {
|
|
47052
|
+
const {
|
|
47053
|
+
projection,
|
|
47054
|
+
dLonSpan,
|
|
47055
|
+
dLatSpan,
|
|
47056
|
+
width,
|
|
47057
|
+
height,
|
|
47058
|
+
waterBodies,
|
|
47059
|
+
countries,
|
|
47060
|
+
palette,
|
|
47061
|
+
project,
|
|
47062
|
+
collides,
|
|
47063
|
+
overLand
|
|
47064
|
+
} = args;
|
|
47065
|
+
void projection;
|
|
47066
|
+
const band = tierBand(Math.max(dLonSpan, dLatSpan));
|
|
47067
|
+
const budget = labelBudget(width, height, band);
|
|
47068
|
+
if (budget <= 0) return [];
|
|
47069
|
+
const waterColor = mix(palette.colors.blue, palette.textMuted, 50);
|
|
47070
|
+
const countryColor = palette.textMuted;
|
|
47071
|
+
const haloColor = palette.bg;
|
|
47072
|
+
const candidates = [];
|
|
47073
|
+
const center = [width / 2, height / 2];
|
|
47074
|
+
for (const e of waterBodies?.entries ?? []) {
|
|
47075
|
+
const [lat, lon, name, tier, kind, alt] = e;
|
|
47076
|
+
if (!waterEligible(tier, kind, band)) continue;
|
|
47077
|
+
const wlines = wrapLabel2(name, WATER_LETTER_SPACING);
|
|
47078
|
+
const anchorsLngLat = [[lon, lat]];
|
|
47079
|
+
for (const a of alt ?? []) anchorsLngLat.push([a[1], a[0]]);
|
|
47080
|
+
let best = null;
|
|
47081
|
+
let bestD = Infinity;
|
|
47082
|
+
let nearestProj = null;
|
|
47083
|
+
let nearestProjD = Infinity;
|
|
47084
|
+
for (const [aLon, aLat] of anchorsLngLat) {
|
|
47085
|
+
const p = project(aLon, aLat);
|
|
47086
|
+
if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
|
|
47087
|
+
const d = (p[0] - center[0]) ** 2 + (p[1] - center[1]) ** 2;
|
|
47088
|
+
if (d < nearestProjD) {
|
|
47089
|
+
nearestProjD = d;
|
|
47090
|
+
nearestProj = p;
|
|
47091
|
+
}
|
|
47092
|
+
if (!insideViewport(p, width, height)) continue;
|
|
47093
|
+
if (d < bestD) {
|
|
47094
|
+
bestD = d;
|
|
47095
|
+
best = p;
|
|
47096
|
+
}
|
|
47097
|
+
}
|
|
47098
|
+
if (!best && tier === 0 && nearestProj) {
|
|
47099
|
+
const overX = Math.max(0, -nearestProj[0], nearestProj[0] - width);
|
|
47100
|
+
const overY = Math.max(0, -nearestProj[1], nearestProj[1] - height);
|
|
47101
|
+
if (overX <= width * EDGE_CLAMP_OVERSHOOT && overY <= height * EDGE_CLAMP_OVERSHOOT) {
|
|
47102
|
+
const halfW = Math.max(...wlines.map((l) => labelWidth(l, WATER_LETTER_SPACING))) / 2;
|
|
47103
|
+
const halfH = ((wlines.length - 1) * LINE_HEIGHT + FONT + 2 * PADY) / 2;
|
|
47104
|
+
const m = EDGE_CLAMP_MARGIN;
|
|
47105
|
+
best = [
|
|
47106
|
+
Math.min(Math.max(nearestProj[0], halfW + m), width - halfW - m),
|
|
47107
|
+
Math.min(Math.max(nearestProj[1], halfH + m), height - halfH - m)
|
|
47108
|
+
];
|
|
47109
|
+
}
|
|
47110
|
+
}
|
|
47111
|
+
if (!best) continue;
|
|
47112
|
+
candidates.push({
|
|
47113
|
+
text: name,
|
|
47114
|
+
lines: wlines,
|
|
47115
|
+
cx: best[0],
|
|
47116
|
+
cy: best[1],
|
|
47117
|
+
italic: true,
|
|
47118
|
+
letterSpacing: WATER_LETTER_SPACING,
|
|
47119
|
+
color: waterColor,
|
|
47120
|
+
// Water before any country (×1000), then by tier, then kind, then name.
|
|
47121
|
+
sort: tier * 10 + KIND_ORDER[kind]
|
|
47122
|
+
});
|
|
47123
|
+
}
|
|
47124
|
+
const ranked = countries.map((c) => {
|
|
47125
|
+
const [x0, y0, x1, y1] = c.bbox;
|
|
47126
|
+
const w = x1 - x0;
|
|
47127
|
+
const h = y1 - y0;
|
|
47128
|
+
return { c, w, h, area: w * h };
|
|
47129
|
+
}).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
|
|
47130
|
+
let ci = 0;
|
|
47131
|
+
for (const r of ranked) {
|
|
47132
|
+
const { c, w, h } = r;
|
|
47133
|
+
if (w > width * 0.66 || h > height * 0.66) continue;
|
|
47134
|
+
if (!insideViewport(c.anchor, width, height)) continue;
|
|
47135
|
+
const text = c.name;
|
|
47136
|
+
const tw = labelWidth(text, 0);
|
|
47137
|
+
if (tw > w || FONT + 2 * PADY > h) continue;
|
|
47138
|
+
candidates.push({
|
|
47139
|
+
text,
|
|
47140
|
+
lines: [text],
|
|
47141
|
+
cx: c.anchor[0],
|
|
47142
|
+
cy: c.anchor[1],
|
|
47143
|
+
italic: false,
|
|
47144
|
+
letterSpacing: 0,
|
|
47145
|
+
color: countryColor,
|
|
47146
|
+
// Always after every water body (+1e6); larger area = earlier.
|
|
47147
|
+
sort: 1e6 + ci++
|
|
47148
|
+
});
|
|
47149
|
+
}
|
|
47150
|
+
candidates.sort((a, b) => a.sort - b.sort);
|
|
47151
|
+
const placed = [];
|
|
47152
|
+
const placedRects = [];
|
|
47153
|
+
for (const cand of candidates) {
|
|
47154
|
+
if (placed.length >= budget) break;
|
|
47155
|
+
const rect = rectAround(cand.cx, cand.cy, cand.lines, cand.letterSpacing);
|
|
47156
|
+
if (!rectFits(rect, width, height)) continue;
|
|
47157
|
+
if (cand.italic && overLand) {
|
|
47158
|
+
const inset = 2;
|
|
47159
|
+
const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
|
|
47160
|
+
const touchesLand = cand.lines.some((line12, li) => {
|
|
47161
|
+
const lw = labelWidth(line12, cand.letterSpacing);
|
|
47162
|
+
const x0 = cand.cx - lw / 2 + inset;
|
|
47163
|
+
const x1 = cand.cx + lw / 2 - inset;
|
|
47164
|
+
const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
|
|
47165
|
+
const base = top + li * LINE_HEIGHT;
|
|
47166
|
+
return [base, base - FONT * 0.4, base - FONT * 0.8].some(
|
|
47167
|
+
(y) => xs.some((x) => overLand(x, y))
|
|
47168
|
+
);
|
|
47169
|
+
});
|
|
47170
|
+
if (touchesLand) continue;
|
|
47171
|
+
}
|
|
47172
|
+
if (collides(rect)) continue;
|
|
47173
|
+
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
47174
|
+
placedRects.push(rect);
|
|
47175
|
+
placed.push({
|
|
47176
|
+
x: cand.cx,
|
|
47177
|
+
y: cand.cy,
|
|
47178
|
+
text: cand.text,
|
|
47179
|
+
anchor: "middle",
|
|
47180
|
+
color: cand.color,
|
|
47181
|
+
// No halo: the bg-coloured outline reads as a ghost box behind the text
|
|
47182
|
+
// over the tinted water/land. Context labels are muted enough to sit
|
|
47183
|
+
// cleanly on the basemap without one.
|
|
47184
|
+
halo: false,
|
|
47185
|
+
haloColor,
|
|
47186
|
+
italic: cand.italic,
|
|
47187
|
+
letterSpacing: cand.letterSpacing,
|
|
47188
|
+
...cand.lines.length > 1 ? { lines: cand.lines } : {},
|
|
47189
|
+
lineNumber: 0
|
|
47190
|
+
});
|
|
47191
|
+
}
|
|
47192
|
+
return placed;
|
|
47193
|
+
}
|
|
47194
|
+
var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, KIND_ORDER;
|
|
47195
|
+
var init_context_labels = __esm({
|
|
47196
|
+
"src/map/context-labels.ts"() {
|
|
47197
|
+
"use strict";
|
|
47198
|
+
init_color_utils();
|
|
47199
|
+
init_legend_constants();
|
|
47200
|
+
FONT = 11;
|
|
47201
|
+
LINE_HEIGHT = FONT + 2;
|
|
47202
|
+
PADX = 4;
|
|
47203
|
+
PADY = 3;
|
|
47204
|
+
WATER_LETTER_SPACING = 1.5;
|
|
47205
|
+
CONTEXT_PAD = 4;
|
|
47206
|
+
EDGE_CLAMP_MARGIN = 8;
|
|
47207
|
+
EDGE_CLAMP_OVERSHOOT = 0.35;
|
|
47208
|
+
KIND_ORDER = {
|
|
47209
|
+
ocean: 0,
|
|
47210
|
+
sea: 1,
|
|
47211
|
+
gulf: 2,
|
|
47212
|
+
bay: 3,
|
|
47213
|
+
strait: 4,
|
|
47214
|
+
channel: 5,
|
|
47215
|
+
sound: 6
|
|
47216
|
+
};
|
|
47217
|
+
}
|
|
47218
|
+
});
|
|
47219
|
+
|
|
46281
47220
|
// src/map/layout.ts
|
|
46282
47221
|
function geomObject2(topo) {
|
|
46283
47222
|
const key = Object.keys(topo.objects)[0];
|
|
46284
47223
|
return topo.objects[key];
|
|
46285
47224
|
}
|
|
47225
|
+
function mergeFeatures(a, b) {
|
|
47226
|
+
const polysOf = (f) => {
|
|
47227
|
+
const g = f.geometry;
|
|
47228
|
+
if (!g) return null;
|
|
47229
|
+
if (g.type === "Polygon") return [g.coordinates];
|
|
47230
|
+
if (g.type === "MultiPolygon") return g.coordinates;
|
|
47231
|
+
return null;
|
|
47232
|
+
};
|
|
47233
|
+
const pa = polysOf(a);
|
|
47234
|
+
const pb = polysOf(b);
|
|
47235
|
+
if (!pa || !pb) return a;
|
|
47236
|
+
return {
|
|
47237
|
+
...a,
|
|
47238
|
+
geometry: { type: "MultiPolygon", coordinates: [...pa, ...pb] }
|
|
47239
|
+
};
|
|
47240
|
+
}
|
|
46286
47241
|
function decodeLayer(topo) {
|
|
47242
|
+
const cached = decodeCache.get(topo);
|
|
47243
|
+
if (cached) return cached;
|
|
46287
47244
|
const out = /* @__PURE__ */ new Map();
|
|
46288
47245
|
for (const g of geomObject2(topo).geometries) {
|
|
46289
47246
|
const f = (0, import_topojson_client2.feature)(topo, g);
|
|
46290
|
-
|
|
47247
|
+
if (!f.geometry) continue;
|
|
47248
|
+
const tagged = { ...f, id: g.id };
|
|
47249
|
+
const existing = out.get(g.id);
|
|
47250
|
+
out.set(g.id, existing ? mergeFeatures(existing, tagged) : tagged);
|
|
46291
47251
|
}
|
|
47252
|
+
decodeCache.set(topo, out);
|
|
46292
47253
|
return out;
|
|
46293
47254
|
}
|
|
46294
47255
|
function projectionFor(family) {
|
|
@@ -46297,9 +47258,12 @@ function projectionFor(family) {
|
|
|
46297
47258
|
return usConusProjection();
|
|
46298
47259
|
case "mercator":
|
|
46299
47260
|
return (0, import_d3_geo2.geoMercator)();
|
|
47261
|
+
case "equal-earth":
|
|
47262
|
+
return (0, import_d3_geo2.geoEqualEarth)();
|
|
47263
|
+
case "equirectangular":
|
|
47264
|
+
return (0, import_d3_geo2.geoEquirectangular)();
|
|
46300
47265
|
case "natural-earth":
|
|
46301
47266
|
return (0, import_d3_geo2.geoNaturalEarth1)();
|
|
46302
|
-
case "equirectangular":
|
|
46303
47267
|
default:
|
|
46304
47268
|
return (0, import_d3_geo2.geoEquirectangular)();
|
|
46305
47269
|
}
|
|
@@ -46318,13 +47282,11 @@ function mapNeutralLandColor(palette, isDark, _dataActive = false) {
|
|
|
46318
47282
|
isDark ? LAND_TINT_DARK : LAND_TINT_LIGHT
|
|
46319
47283
|
);
|
|
46320
47284
|
}
|
|
46321
|
-
function
|
|
46322
|
-
const { palette, isDark } = opts;
|
|
46323
|
-
const { width, height } = size;
|
|
47285
|
+
function buildMapProjection(resolved, data) {
|
|
46324
47286
|
const wantsUsStates = resolved.basemaps.subdivisions.includes("us-states");
|
|
46325
|
-
const usCrisp = resolved.projection === "albers-usa" && wantsUsStates && !!data.naLand;
|
|
47287
|
+
const usCrisp = (resolved.projection === "albers-usa" || resolved.projection === "mercator") && wantsUsStates && !!data.naLand;
|
|
46326
47288
|
const worldTopo = usCrisp ? data.worldDetail : resolved.basemaps.world === "detail" ? data.worldDetail : data.worldCoarse;
|
|
46327
|
-
const worldLayer = decodeLayer(worldTopo);
|
|
47289
|
+
const worldLayer = new Map(decodeLayer(worldTopo));
|
|
46328
47290
|
if (usCrisp && data.naLand) {
|
|
46329
47291
|
const [nbW, nbS, nbE, nbN] = [-140, 10, -52, 66];
|
|
46330
47292
|
const crisp = decodeLayer(data.naLand);
|
|
@@ -46333,16 +47295,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46333
47295
|
if (!base) continue;
|
|
46334
47296
|
const [[bw, bs], [be, bn]] = (0, import_d3_geo2.geoBounds)(base);
|
|
46335
47297
|
if (bw >= nbW && be <= nbE && bs >= nbS && bn <= nbN)
|
|
46336
|
-
worldLayer.set(iso, cf);
|
|
47298
|
+
worldLayer.set(iso, { ...cf, properties: base.properties });
|
|
46337
47299
|
}
|
|
46338
47300
|
}
|
|
46339
47301
|
const usLayer = wantsUsStates ? decodeLayer(data.usStates) : null;
|
|
47302
|
+
const extentOutline = () => {
|
|
47303
|
+
const [[w, s], [e, n]] = resolved.extent;
|
|
47304
|
+
const N = 16;
|
|
47305
|
+
const coords = [];
|
|
47306
|
+
for (let i = 0; i <= N; i++) {
|
|
47307
|
+
const t = i / N;
|
|
47308
|
+
const lon = w + (e - w) * t;
|
|
47309
|
+
const lat = s + (n - s) * t;
|
|
47310
|
+
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
47311
|
+
}
|
|
47312
|
+
return {
|
|
47313
|
+
type: "Feature",
|
|
47314
|
+
properties: {},
|
|
47315
|
+
geometry: { type: "MultiPoint", coordinates: coords }
|
|
47316
|
+
};
|
|
47317
|
+
};
|
|
47318
|
+
let fitFeatures;
|
|
47319
|
+
if (resolved.projection === "albers-usa" && usLayer) {
|
|
47320
|
+
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
47321
|
+
const neighborPoints = resolved.pois.filter((p) => !inAlaska(p.lon, p.lat) && !inHawaii(p.lon, p.lat)).map((p) => [p.lon, p.lat]);
|
|
47322
|
+
if (neighborPoints.length > 0) {
|
|
47323
|
+
fitFeatures.push({
|
|
47324
|
+
type: "Feature",
|
|
47325
|
+
properties: {},
|
|
47326
|
+
geometry: { type: "MultiPoint", coordinates: neighborPoints }
|
|
47327
|
+
});
|
|
47328
|
+
}
|
|
47329
|
+
for (const r of resolved.regions) {
|
|
47330
|
+
if (r.layer === "country" && (r.iso === "CA" || r.iso === "MX")) {
|
|
47331
|
+
const cf = worldLayer.get(r.iso);
|
|
47332
|
+
if (cf) fitFeatures.push(cf);
|
|
47333
|
+
}
|
|
47334
|
+
}
|
|
47335
|
+
} else {
|
|
47336
|
+
fitFeatures = [extentOutline()];
|
|
47337
|
+
}
|
|
47338
|
+
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
47339
|
+
const projection = projectionFor(resolved.projection);
|
|
47340
|
+
if (resolved.projection !== "albers-usa") {
|
|
47341
|
+
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
47342
|
+
if (centerLon > 180) centerLon -= 360;
|
|
47343
|
+
projection.rotate([-centerLon, 0]);
|
|
47344
|
+
}
|
|
47345
|
+
const fitGB = (0, import_d3_geo2.geoBounds)(fitTarget);
|
|
47346
|
+
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
47347
|
+
return {
|
|
47348
|
+
projection,
|
|
47349
|
+
fitTarget,
|
|
47350
|
+
fitIsGlobal,
|
|
47351
|
+
worldLayer,
|
|
47352
|
+
usLayer,
|
|
47353
|
+
usCrisp,
|
|
47354
|
+
wantsUsStates,
|
|
47355
|
+
worldTopo
|
|
47356
|
+
};
|
|
47357
|
+
}
|
|
47358
|
+
function parsePathRings(d) {
|
|
47359
|
+
const rings = [];
|
|
47360
|
+
let cur = [];
|
|
47361
|
+
const re = /([MLZ])([^MLZ]*)/g;
|
|
47362
|
+
let m;
|
|
47363
|
+
while (m = re.exec(d)) {
|
|
47364
|
+
if (m[1] === "Z") {
|
|
47365
|
+
if (cur.length) rings.push(cur);
|
|
47366
|
+
cur = [];
|
|
47367
|
+
continue;
|
|
47368
|
+
}
|
|
47369
|
+
if (m[1] === "M" && cur.length) {
|
|
47370
|
+
rings.push(cur);
|
|
47371
|
+
cur = [];
|
|
47372
|
+
}
|
|
47373
|
+
const nums = m[2].split(/[ ,]+/).map(Number);
|
|
47374
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
47375
|
+
const x = nums[i];
|
|
47376
|
+
const y = nums[i + 1];
|
|
47377
|
+
if (Number.isFinite(x) && Number.isFinite(y)) cur.push([x, y]);
|
|
47378
|
+
}
|
|
47379
|
+
}
|
|
47380
|
+
if (cur.length) rings.push(cur);
|
|
47381
|
+
return rings;
|
|
47382
|
+
}
|
|
47383
|
+
function dropAntimeridianWrapSlivers(d, width, height) {
|
|
47384
|
+
const rings = parsePathRings(d);
|
|
47385
|
+
if (rings.length <= 1) return d;
|
|
47386
|
+
const eps = 0.75;
|
|
47387
|
+
const minArea = 3e-3 * width * height;
|
|
47388
|
+
const ringArea = (r) => {
|
|
47389
|
+
let s = 0;
|
|
47390
|
+
for (let i = 0; i < r.length; i++) {
|
|
47391
|
+
const a = r[i];
|
|
47392
|
+
const b = r[(i + 1) % r.length];
|
|
47393
|
+
s += a[0] * b[1] - b[0] * a[1];
|
|
47394
|
+
}
|
|
47395
|
+
return Math.abs(s) / 2;
|
|
47396
|
+
};
|
|
47397
|
+
const areas = rings.map(ringArea);
|
|
47398
|
+
const maxArea = Math.max(...areas);
|
|
47399
|
+
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;
|
|
47400
|
+
let dropped = false;
|
|
47401
|
+
const kept = rings.filter((r, idx) => {
|
|
47402
|
+
if (areas[idx] >= maxArea || areas[idx] >= minArea) return true;
|
|
47403
|
+
const touches = r.some((p, i) => onVEdge(p, r[(i + 1) % r.length]));
|
|
47404
|
+
if (touches) {
|
|
47405
|
+
dropped = true;
|
|
47406
|
+
return false;
|
|
47407
|
+
}
|
|
47408
|
+
return true;
|
|
47409
|
+
});
|
|
47410
|
+
if (!dropped) return d;
|
|
47411
|
+
return kept.map(
|
|
47412
|
+
(r) => r.map((p, i) => (i ? "L" : "M") + p[0] + "," + p[1]).join("") + "Z"
|
|
47413
|
+
).join("");
|
|
47414
|
+
}
|
|
47415
|
+
function layoutMap(resolved, data, size, opts) {
|
|
47416
|
+
const { palette, isDark } = opts;
|
|
47417
|
+
const { width, height } = size;
|
|
47418
|
+
const {
|
|
47419
|
+
projection,
|
|
47420
|
+
fitTarget,
|
|
47421
|
+
fitIsGlobal,
|
|
47422
|
+
worldLayer,
|
|
47423
|
+
usLayer,
|
|
47424
|
+
usCrisp,
|
|
47425
|
+
worldTopo
|
|
47426
|
+
} = buildMapProjection(resolved, data);
|
|
46340
47427
|
const usContext = usLayer !== null;
|
|
46341
47428
|
const regionStroke = isDark ? mix(palette.bg, palette.text, 78) : mix(palette.text, palette.bg, 78);
|
|
46342
47429
|
const values = resolved.regions.filter((r) => r.value !== void 0).map((r) => r.value);
|
|
46343
|
-
const
|
|
46344
|
-
const rampMin =
|
|
46345
|
-
const rampMax =
|
|
47430
|
+
const allNonNegative = values.length > 0 && values.every((v) => v >= 0);
|
|
47431
|
+
const rampMin = allNonNegative ? 0 : Math.min(...values);
|
|
47432
|
+
const rampMax = Math.max(...values);
|
|
46346
47433
|
const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
|
|
46347
47434
|
const hasRamp = values.length > 0;
|
|
46348
47435
|
const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
|
|
@@ -46363,7 +47450,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46363
47450
|
activeGroup = VALUE_NAME ?? (resolved.tagGroups.length > 0 ? resolved.tagGroups[0].name : null);
|
|
46364
47451
|
}
|
|
46365
47452
|
const activeIsScore = VALUE_NAME !== null && activeGroup === VALUE_NAME;
|
|
46366
|
-
const mutedBasemap =
|
|
47453
|
+
const mutedBasemap = activeGroup !== null;
|
|
46367
47454
|
const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
|
|
46368
47455
|
const water = mapBackgroundColor(palette, isDark, mutedBasemap);
|
|
46369
47456
|
const lakeStroke = mix(regionStroke, water, 45);
|
|
@@ -46372,10 +47459,43 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46372
47459
|
palette.bg,
|
|
46373
47460
|
mutedBasemap ? isDark ? MUTED_FOREIGN_DARK : MUTED_FOREIGN_LIGHT : isDark ? FOREIGN_TINT_DARK : FOREIGN_TINT_LIGHT
|
|
46374
47461
|
);
|
|
47462
|
+
const colorizeActive = resolved.directives.noColorize !== true && !hasRamp && resolved.tagGroups.length === 0;
|
|
47463
|
+
const colorByIso = /* @__PURE__ */ new Map();
|
|
47464
|
+
if (colorizeActive) {
|
|
47465
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
47466
|
+
const addEdges = (src) => {
|
|
47467
|
+
for (const [iso, ns] of src) {
|
|
47468
|
+
const cur = adjacency.get(iso);
|
|
47469
|
+
if (cur) cur.push(...ns);
|
|
47470
|
+
else adjacency.set(iso, [...ns]);
|
|
47471
|
+
}
|
|
47472
|
+
};
|
|
47473
|
+
addEdges(buildAdjacency(worldTopo));
|
|
47474
|
+
if (usLayer) {
|
|
47475
|
+
addEdges(buildAdjacency(data.usStates));
|
|
47476
|
+
for (const [country, states] of Object.entries(FOREIGN_BORDER)) {
|
|
47477
|
+
const cn = adjacency.get(country);
|
|
47478
|
+
if (!cn) continue;
|
|
47479
|
+
for (const st of states) {
|
|
47480
|
+
const sn = adjacency.get(st);
|
|
47481
|
+
if (!sn) continue;
|
|
47482
|
+
cn.push(st);
|
|
47483
|
+
sn.push(country);
|
|
47484
|
+
}
|
|
47485
|
+
}
|
|
47486
|
+
}
|
|
47487
|
+
const { byIso, huesNeeded } = assignColors(
|
|
47488
|
+
[...adjacency.keys()],
|
|
47489
|
+
adjacency
|
|
47490
|
+
);
|
|
47491
|
+
const tints = politicalTints(palette, huesNeeded, isDark);
|
|
47492
|
+
for (const [iso, idx] of byIso) colorByIso.set(iso, tints[idx]);
|
|
47493
|
+
}
|
|
47494
|
+
const colorizeStroke = (fill2) => mix(fill2, palette.text, 35);
|
|
46375
47495
|
const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
|
|
46376
47496
|
const fillForValue = (s) => {
|
|
46377
47497
|
const t = rampMax > rampMin ? (s - rampMin) / (rampMax - rampMin) : 1;
|
|
46378
|
-
const pct =
|
|
47498
|
+
const pct = RAMP_FLOOR2 + Math.max(0, Math.min(1, t)) * (100 - RAMP_FLOOR2);
|
|
46379
47499
|
return mix(rampHue, rampBase, pct);
|
|
46380
47500
|
};
|
|
46381
47501
|
const tagFill = (tags, groupName) => {
|
|
@@ -46407,43 +47527,15 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46407
47527
|
if (activeIsScore) {
|
|
46408
47528
|
return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
|
|
46409
47529
|
}
|
|
47530
|
+
if (colorizeActive) return (r.iso && colorByIso.get(r.iso)) ?? neutralFill;
|
|
46410
47531
|
return tagFill(r.tags, activeGroup) ?? neutralFill;
|
|
46411
47532
|
};
|
|
46412
47533
|
const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
|
|
46413
|
-
const
|
|
46414
|
-
const [[w, s], [e, n]] = resolved.extent;
|
|
46415
|
-
const N = 16;
|
|
46416
|
-
const coords = [];
|
|
46417
|
-
for (let i = 0; i <= N; i++) {
|
|
46418
|
-
const t = i / N;
|
|
46419
|
-
const lon = w + (e - w) * t;
|
|
46420
|
-
const lat = s + (n - s) * t;
|
|
46421
|
-
coords.push([lon, s], [lon, n], [w, lat], [e, lat]);
|
|
46422
|
-
}
|
|
46423
|
-
return {
|
|
46424
|
-
type: "Feature",
|
|
46425
|
-
properties: {},
|
|
46426
|
-
geometry: { type: "MultiPoint", coordinates: coords }
|
|
46427
|
-
};
|
|
46428
|
-
};
|
|
46429
|
-
let fitFeatures;
|
|
46430
|
-
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46431
|
-
fitFeatures = [...usLayer.entries()].filter(([iso]) => !US_NON_CONUS.has(iso)).map(([, f]) => f);
|
|
46432
|
-
} else {
|
|
46433
|
-
fitFeatures = [extentOutline()];
|
|
46434
|
-
}
|
|
46435
|
-
const fitTarget = { type: "FeatureCollection", features: fitFeatures };
|
|
46436
|
-
const projection = projectionFor(resolved.projection);
|
|
46437
|
-
if (resolved.projection !== "albers-usa") {
|
|
46438
|
-
let centerLon = (resolved.extent[0][0] + resolved.extent[1][0]) / 2;
|
|
46439
|
-
if (centerLon > 180) centerLon -= 360;
|
|
46440
|
-
projection.rotate([-centerLon, 0]);
|
|
46441
|
-
}
|
|
46442
|
-
const TITLE_GAP = 16;
|
|
47534
|
+
const TITLE_GAP2 = 16;
|
|
46443
47535
|
let topPad = FIT_PAD;
|
|
46444
47536
|
if (resolved.title && resolved.pois.length > 0) {
|
|
46445
47537
|
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
46446
|
-
topPad = Math.max(FIT_PAD, bannerBottom +
|
|
47538
|
+
topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
|
|
46447
47539
|
}
|
|
46448
47540
|
const fitBox = [
|
|
46449
47541
|
[FIT_PAD, topPad],
|
|
@@ -46453,21 +47545,20 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46453
47545
|
]
|
|
46454
47546
|
];
|
|
46455
47547
|
projection.fitExtent(fitBox, fitTarget);
|
|
46456
|
-
const fitGB = (0, import_d3_geo2.geoBounds)(fitTarget);
|
|
46457
|
-
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46458
47548
|
let path;
|
|
46459
47549
|
let project;
|
|
46460
47550
|
let stretchParams = null;
|
|
46461
|
-
if (fitIsGlobal) {
|
|
47551
|
+
if (fitIsGlobal && !opts.preferContain) {
|
|
46462
47552
|
const cb = (0, import_d3_geo2.geoPath)(projection).bounds(fitTarget);
|
|
46463
47553
|
const bx0 = cb[0][0];
|
|
46464
47554
|
const by0 = cb[0][1];
|
|
46465
47555
|
const cw = cb[1][0] - bx0;
|
|
46466
47556
|
const ch = cb[1][1] - by0;
|
|
46467
|
-
const
|
|
46468
|
-
const
|
|
46469
|
-
const
|
|
46470
|
-
const
|
|
47557
|
+
const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
|
|
47558
|
+
const ox = 0;
|
|
47559
|
+
const oy = topReserve;
|
|
47560
|
+
const sx = cw > 0 ? width / cw : 1;
|
|
47561
|
+
const sy = ch > 0 ? (height - topReserve) / ch : 1;
|
|
46471
47562
|
stretchParams = { sx, sy, ox, oy, bx0, by0 };
|
|
46472
47563
|
const stretch = (x, y) => [
|
|
46473
47564
|
ox + (x - bx0) * sx,
|
|
@@ -46500,7 +47591,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46500
47591
|
const insets = [];
|
|
46501
47592
|
const insetRegions = [];
|
|
46502
47593
|
const insetLabelSeeds = [];
|
|
46503
|
-
|
|
47594
|
+
const akRef = resolved.regions.some((r) => r.iso === "US-AK") || resolved.pois.some((p) => inAlaska(p.lon, p.lat));
|
|
47595
|
+
const hiRef = resolved.regions.some((r) => r.iso === "US-HI") || resolved.pois.some((p) => inHawaii(p.lon, p.lat));
|
|
47596
|
+
if (resolved.projection === "albers-usa" && usLayer && (akRef || hiRef)) {
|
|
46504
47597
|
const PAD = 8;
|
|
46505
47598
|
const GAP = 12;
|
|
46506
47599
|
const yB = height - FIT_PAD;
|
|
@@ -46565,8 +47658,18 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46565
47658
|
);
|
|
46566
47659
|
const d = (0, import_d3_geo2.geoPath)(proj)(f) ?? "";
|
|
46567
47660
|
if (!d) return xr;
|
|
47661
|
+
let contextLand;
|
|
47662
|
+
if (iso === "US-AK") {
|
|
47663
|
+
const can = worldLayer.get("CA");
|
|
47664
|
+
const cd = can ? (0, import_d3_geo2.geoPath)(proj)(can) ?? "" : "";
|
|
47665
|
+
if (cd)
|
|
47666
|
+
contextLand = {
|
|
47667
|
+
d: cd,
|
|
47668
|
+
fill: colorizeActive ? colorByIso.get("CA") ?? foreignFill : foreignFill
|
|
47669
|
+
};
|
|
47670
|
+
}
|
|
46568
47671
|
const r = regionById.get(iso);
|
|
46569
|
-
let fill2 = neutralFill;
|
|
47672
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? neutralFill : neutralFill;
|
|
46570
47673
|
let lineNumber = -1;
|
|
46571
47674
|
if (r?.layer === "us-state") {
|
|
46572
47675
|
fill2 = regionFill(r);
|
|
@@ -46585,13 +47688,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46585
47688
|
],
|
|
46586
47689
|
// The FITTED inset projection (just fit to this box) — captured so the
|
|
46587
47690
|
// geo-query can invert pixels inside the frame back to AK/HI coords.
|
|
46588
|
-
projection: proj
|
|
47691
|
+
projection: proj,
|
|
47692
|
+
...contextLand && { contextLand }
|
|
46589
47693
|
});
|
|
46590
47694
|
insetRegions.push({
|
|
46591
47695
|
id: iso,
|
|
46592
47696
|
d,
|
|
46593
47697
|
fill: fill2,
|
|
46594
|
-
stroke: regionStroke,
|
|
47698
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46595
47699
|
lineNumber,
|
|
46596
47700
|
layer: "us-state",
|
|
46597
47701
|
...r?.value !== void 0 && { value: r.value },
|
|
@@ -46604,13 +47708,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46604
47708
|
}
|
|
46605
47709
|
return xr;
|
|
46606
47710
|
};
|
|
46607
|
-
|
|
46608
|
-
|
|
46609
|
-
alaskaProjection(),
|
|
46610
|
-
|
|
46611
|
-
|
|
46612
|
-
|
|
46613
|
-
|
|
47711
|
+
let akRight = FIT_PAD;
|
|
47712
|
+
if (akRef)
|
|
47713
|
+
akRight = placeInset("US-AK", alaskaProjection(), FIT_PAD, width * 0.15);
|
|
47714
|
+
if (hiRef)
|
|
47715
|
+
placeInset(
|
|
47716
|
+
"US-HI",
|
|
47717
|
+
hawaiiProjection(),
|
|
47718
|
+
akRef ? akRight + 24 : FIT_PAD,
|
|
47719
|
+
width * 0.1
|
|
47720
|
+
);
|
|
46614
47721
|
}
|
|
46615
47722
|
const conusFit = resolved.projection === "albers-usa" && !!usLayer;
|
|
46616
47723
|
const classifyExtent = conusFit ? (0, import_d3_geo2.geoBounds)(fitTarget) : resolved.extent;
|
|
@@ -46626,15 +47733,24 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46626
47733
|
};
|
|
46627
47734
|
const ringOverlapsView = (ring) => {
|
|
46628
47735
|
let loMin = Infinity, loMax = -Infinity, rawMin = Infinity, rawMax = -Infinity;
|
|
47736
|
+
const lons = [];
|
|
46629
47737
|
for (const [rawLon] of ring) {
|
|
46630
47738
|
const lon = normLon(rawLon);
|
|
47739
|
+
lons.push(lon);
|
|
46631
47740
|
if (lon < loMin) loMin = lon;
|
|
46632
47741
|
if (lon > loMax) loMax = lon;
|
|
46633
47742
|
if (rawLon < rawMin) rawMin = rawLon;
|
|
46634
47743
|
if (rawLon > rawMax) rawMax = rawLon;
|
|
46635
47744
|
}
|
|
46636
|
-
|
|
46637
|
-
|
|
47745
|
+
lons.sort((a, b) => a - b);
|
|
47746
|
+
let maxGap = 0;
|
|
47747
|
+
for (let i = 1; i < lons.length; i++)
|
|
47748
|
+
maxGap = Math.max(maxGap, lons[i] - lons[i - 1]);
|
|
47749
|
+
if (lons.length > 1)
|
|
47750
|
+
maxGap = Math.max(maxGap, lons[0] + 360 - lons[lons.length - 1]);
|
|
47751
|
+
const occupiedArc = 360 - maxGap;
|
|
47752
|
+
if (occupiedArc > 270) return false;
|
|
47753
|
+
if (rawMax - rawMin > 180 && occupiedArc < 90) return false;
|
|
46638
47754
|
let px0 = Infinity, py0 = Infinity, px1 = -Infinity, py1 = -Infinity, anyFinite = false;
|
|
46639
47755
|
for (const [lon, lat] of ring) {
|
|
46640
47756
|
const p = project(lon, lat);
|
|
@@ -46707,7 +47823,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46707
47823
|
const regions = [];
|
|
46708
47824
|
const pushRegionLayer = (layerFeatures, layerKind, shouldCull) => {
|
|
46709
47825
|
for (const [iso, f] of layerFeatures) {
|
|
46710
|
-
if (layerKind === "us-state" && usContext && INSET_STATES.has(iso))
|
|
47826
|
+
if (layerKind === "us-state" && usContext && resolved.projection === "albers-usa" && INSET_STATES.has(iso))
|
|
46711
47827
|
continue;
|
|
46712
47828
|
if (layerKind === "country" && usContext && iso === "US") continue;
|
|
46713
47829
|
if (layerKind === "country" && iso === "AQ" && !regionById.has("AQ"))
|
|
@@ -46715,11 +47831,13 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46715
47831
|
const r = regionById.get(iso);
|
|
46716
47832
|
const viewF = shouldCull ? cullFeatureToView(f) : dropFrameFillers(f);
|
|
46717
47833
|
if (!viewF) continue;
|
|
46718
|
-
const
|
|
47834
|
+
const raw = path(viewF) ?? "";
|
|
47835
|
+
const d = fitIsGlobal ? dropAntimeridianWrapSlivers(raw, width, height) : raw;
|
|
46719
47836
|
if (!d) continue;
|
|
46720
47837
|
const isThisLayer = r?.layer === layerKind;
|
|
46721
47838
|
const isForeign = layerKind === "country" && usContext && iso !== "US";
|
|
46722
|
-
|
|
47839
|
+
const baseFill = isForeign ? foreignFill : neutralFill;
|
|
47840
|
+
let fill2 = colorizeActive ? colorByIso.get(iso) ?? baseFill : baseFill;
|
|
46723
47841
|
let label;
|
|
46724
47842
|
let lineNumber = -1;
|
|
46725
47843
|
let layer = "base";
|
|
@@ -46728,15 +47846,21 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46728
47846
|
lineNumber = r.lineNumber;
|
|
46729
47847
|
layer = layerKind;
|
|
46730
47848
|
label = r.name;
|
|
47849
|
+
} else {
|
|
47850
|
+
label = f.properties?.name;
|
|
46731
47851
|
}
|
|
47852
|
+
const labelAnchor = WORLD_LABEL_ANCHORS[iso];
|
|
47853
|
+
const c = labelAnchor ? project(labelAnchor[0], labelAnchor[1]) : path.centroid(viewF);
|
|
47854
|
+
const hasCentroid = c != null && Number.isFinite(c[0]) && Number.isFinite(c[1]);
|
|
46732
47855
|
regions.push({
|
|
46733
47856
|
id: iso,
|
|
46734
47857
|
d,
|
|
46735
47858
|
fill: fill2,
|
|
46736
|
-
stroke: regionStroke,
|
|
47859
|
+
stroke: colorizeActive ? colorizeStroke(fill2) : regionStroke,
|
|
46737
47860
|
lineNumber,
|
|
46738
47861
|
layer,
|
|
46739
47862
|
...label !== void 0 && { label },
|
|
47863
|
+
...hasCentroid && { labelX: c[0], labelY: c[1] },
|
|
46740
47864
|
...isThisLayer && r.value !== void 0 && { value: r.value },
|
|
46741
47865
|
...isThisLayer && Object.keys(r.tags).length > 0 && { tags: r.tags }
|
|
46742
47866
|
});
|
|
@@ -46761,9 +47885,41 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46761
47885
|
});
|
|
46762
47886
|
}
|
|
46763
47887
|
}
|
|
47888
|
+
const pointInRings = (px, py, rings) => {
|
|
47889
|
+
let inside = false;
|
|
47890
|
+
for (const ring of rings) {
|
|
47891
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
47892
|
+
const [xi, yi] = ring[i];
|
|
47893
|
+
const [xj, yj] = ring[j];
|
|
47894
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
47895
|
+
inside = !inside;
|
|
47896
|
+
}
|
|
47897
|
+
}
|
|
47898
|
+
return inside;
|
|
47899
|
+
};
|
|
47900
|
+
const fillHitTargets = [...regions, ...insetRegions].map((r) => ({
|
|
47901
|
+
fill: r.fill,
|
|
47902
|
+
rings: parsePathRings(r.d)
|
|
47903
|
+
}));
|
|
47904
|
+
const fillAt = (x, y) => {
|
|
47905
|
+
let hit = water;
|
|
47906
|
+
for (const t of fillHitTargets)
|
|
47907
|
+
if (pointInRings(x, y, t.rings)) hit = t.fill;
|
|
47908
|
+
return hit;
|
|
47909
|
+
};
|
|
47910
|
+
const labelOnFill = (fill2) => {
|
|
47911
|
+
const color = contrastRatio(fill2, palette.textOnFillDark) >= contrastRatio(fill2, palette.textOnFillLight) ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47912
|
+
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47913
|
+
return {
|
|
47914
|
+
color,
|
|
47915
|
+
halo: contrastRatio(fill2, color) < REGION_LABEL_HALO_RATIO,
|
|
47916
|
+
haloColor
|
|
47917
|
+
};
|
|
47918
|
+
};
|
|
47919
|
+
const reliefAllowed = resolved.directives.noRelief !== true;
|
|
46764
47920
|
const relief = [];
|
|
46765
47921
|
let reliefHatch = null;
|
|
46766
|
-
if (
|
|
47922
|
+
if (reliefAllowed && data.mountainRanges) {
|
|
46767
47923
|
for (const [, f] of decodeLayer(data.mountainRanges)) {
|
|
46768
47924
|
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
46769
47925
|
if (!viewF) continue;
|
|
@@ -46779,16 +47935,32 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46779
47935
|
if (relief.length) {
|
|
46780
47936
|
const darkTone = isDark ? palette.bg : palette.text;
|
|
46781
47937
|
const lightTone = isDark ? palette.text : palette.bg;
|
|
46782
|
-
const
|
|
47938
|
+
const reliefLandRef = colorizeActive ? isDark ? palette.surface : palette.bg : neutralFill;
|
|
47939
|
+
const landLum = relativeLuminance(reliefLandRef);
|
|
46783
47940
|
const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
|
|
46784
47941
|
reliefHatch = {
|
|
46785
|
-
color: mix(tone,
|
|
47942
|
+
color: mix(tone, reliefLandRef, RELIEF_HATCH_STRENGTH),
|
|
46786
47943
|
spacing: RELIEF_HATCH_SPACING,
|
|
46787
47944
|
width: RELIEF_HATCH_WIDTH
|
|
46788
47945
|
};
|
|
46789
47946
|
}
|
|
46790
47947
|
}
|
|
46791
|
-
|
|
47948
|
+
let coastlineStyle = null;
|
|
47949
|
+
if (resolved.directives.noCoastline !== true) {
|
|
47950
|
+
const minDim = Math.min(width, height);
|
|
47951
|
+
coastlineStyle = {
|
|
47952
|
+
color: mix(regionStroke, water, COASTLINE_STROKE_MIX),
|
|
47953
|
+
// N equal-width rings: distance steps outward by COASTLINE_STEP; opacity
|
|
47954
|
+
// fades linearly from NEAR (innermost) to FAR (outermost).
|
|
47955
|
+
lines: Array.from({ length: COASTLINE_RING_COUNT }, (_, k) => ({
|
|
47956
|
+
d: (COASTLINE_D0 + k * COASTLINE_STEP) * minDim,
|
|
47957
|
+
thickness: COASTLINE_THICKNESS * minDim,
|
|
47958
|
+
opacity: COASTLINE_OPACITY_NEAR + (COASTLINE_OPACITY_FAR - COASTLINE_OPACITY_NEAR) * k / (COASTLINE_RING_COUNT - 1)
|
|
47959
|
+
})),
|
|
47960
|
+
minExtent: (isGlobalView ? COASTLINE_MIN_EXTENT_GLOBAL : COASTLINE_MIN_EXTENT) * minDim
|
|
47961
|
+
};
|
|
47962
|
+
}
|
|
47963
|
+
const riverColor = mix(palette.colors.blue, water, 32);
|
|
46792
47964
|
const rivers = [];
|
|
46793
47965
|
if (data.rivers) {
|
|
46794
47966
|
for (const [, f] of decodeLayer(data.rivers)) {
|
|
@@ -46844,38 +48016,108 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46844
48016
|
const xy = project(p.lon, p.lat);
|
|
46845
48017
|
if (xy) projected.push({ p, xy });
|
|
46846
48018
|
}
|
|
46847
|
-
const
|
|
48019
|
+
const placePoi = (e, cx, cy, clusterId) => {
|
|
48020
|
+
const { fill: fill2, stroke: stroke2 } = poiFill(e.p);
|
|
48021
|
+
poiScreen.set(e.p.id, { cx, cy, r: radiusFor(e.p) });
|
|
48022
|
+
const num = routeNumberById.get(e.p.id);
|
|
48023
|
+
pois.push({
|
|
48024
|
+
id: e.p.id,
|
|
48025
|
+
cx,
|
|
48026
|
+
cy,
|
|
48027
|
+
r: radiusFor(e.p),
|
|
48028
|
+
fill: fill2,
|
|
48029
|
+
stroke: stroke2,
|
|
48030
|
+
lineNumber: e.p.lineNumber,
|
|
48031
|
+
implicit: !!e.p.implicit,
|
|
48032
|
+
isOrigin: originIds.has(e.p.id),
|
|
48033
|
+
...num !== void 0 && { routeNumber: num },
|
|
48034
|
+
...Object.keys(e.p.tags).length > 0 && { tags: e.p.tags },
|
|
48035
|
+
...clusterId !== void 0 && { clusterId }
|
|
48036
|
+
});
|
|
48037
|
+
};
|
|
48038
|
+
const clusters = [];
|
|
48039
|
+
const connected = /* @__PURE__ */ new Set();
|
|
48040
|
+
for (const e of resolved.edges) {
|
|
48041
|
+
connected.add(e.fromId);
|
|
48042
|
+
connected.add(e.toId);
|
|
48043
|
+
}
|
|
48044
|
+
for (const rt of resolved.routes) {
|
|
48045
|
+
rt.stopIds.forEach((id) => connected.add(id));
|
|
48046
|
+
}
|
|
48047
|
+
const radiusOf = (e) => radiusFor(e.p);
|
|
46848
48048
|
for (const e of projected) {
|
|
46849
|
-
|
|
46850
|
-
|
|
46851
|
-
|
|
46852
|
-
|
|
46853
|
-
|
|
46854
|
-
|
|
46855
|
-
|
|
46856
|
-
|
|
46857
|
-
|
|
46858
|
-
|
|
46859
|
-
|
|
46860
|
-
|
|
46861
|
-
|
|
46862
|
-
|
|
46863
|
-
|
|
46864
|
-
|
|
46865
|
-
|
|
46866
|
-
|
|
46867
|
-
|
|
46868
|
-
|
|
46869
|
-
|
|
46870
|
-
|
|
46871
|
-
|
|
46872
|
-
|
|
46873
|
-
|
|
46874
|
-
|
|
46875
|
-
|
|
46876
|
-
|
|
46877
|
-
|
|
46878
|
-
|
|
48049
|
+
if (connected.has(e.p.id)) placePoi(e, e.xy[0], e.xy[1]);
|
|
48050
|
+
}
|
|
48051
|
+
const groups = [];
|
|
48052
|
+
for (const e of projected) {
|
|
48053
|
+
if (connected.has(e.p.id)) continue;
|
|
48054
|
+
const r = radiusOf(e);
|
|
48055
|
+
const near = groups.find(
|
|
48056
|
+
(g) => g.some(
|
|
48057
|
+
(q) => Math.hypot(q.xy[0] - e.xy[0], q.xy[1] - e.xy[1]) < (r + radiusOf(q)) * STACK_OVERLAP
|
|
48058
|
+
)
|
|
48059
|
+
);
|
|
48060
|
+
if (near) near.push(e);
|
|
48061
|
+
else groups.push([e]);
|
|
48062
|
+
}
|
|
48063
|
+
for (const g of groups) {
|
|
48064
|
+
if (g.length === 1) {
|
|
48065
|
+
placePoi(g[0], g[0].xy[0], g[0].xy[1]);
|
|
48066
|
+
continue;
|
|
48067
|
+
}
|
|
48068
|
+
const clusterId = g[0].p.id;
|
|
48069
|
+
const cx0 = g.reduce((s, e) => s + e.xy[0], 0) / g.length;
|
|
48070
|
+
const cy0 = g.reduce((s, e) => s + e.xy[1], 0) / g.length;
|
|
48071
|
+
const maxR = Math.max(...g.map(radiusOf));
|
|
48072
|
+
const sep = 2 * maxR + STACK_RING_GAP;
|
|
48073
|
+
const ringR = Math.max(
|
|
48074
|
+
COLO_R,
|
|
48075
|
+
sep / (2 * Math.sin(Math.PI / Math.max(g.length, 2)))
|
|
48076
|
+
);
|
|
48077
|
+
const positions = g.map((e, i) => {
|
|
48078
|
+
if (g.length <= STACK_RING_MAX) {
|
|
48079
|
+
const ang2 = -Math.PI / 2 + i * 2 * Math.PI / g.length;
|
|
48080
|
+
return {
|
|
48081
|
+
e,
|
|
48082
|
+
mx: cx0 + Math.cos(ang2) * ringR,
|
|
48083
|
+
my: cy0 + Math.sin(ang2) * ringR
|
|
48084
|
+
};
|
|
48085
|
+
}
|
|
48086
|
+
const ang = i * GOLDEN_ANGLE;
|
|
48087
|
+
const rr = ringR * Math.sqrt((i + 1) / g.length);
|
|
48088
|
+
return { e, mx: cx0 + Math.cos(ang) * rr, my: cy0 + Math.sin(ang) * rr };
|
|
48089
|
+
});
|
|
48090
|
+
let minX = cx0 - maxR;
|
|
48091
|
+
let maxX = cx0 + maxR;
|
|
48092
|
+
let minY = cy0 - maxR;
|
|
48093
|
+
let maxY = cy0 + maxR;
|
|
48094
|
+
for (const { mx, my, e } of positions) {
|
|
48095
|
+
const r = radiusOf(e);
|
|
48096
|
+
minX = Math.min(minX, mx - r);
|
|
48097
|
+
maxX = Math.max(maxX, mx + r);
|
|
48098
|
+
minY = Math.min(minY, my - r);
|
|
48099
|
+
maxY = Math.max(maxY, my + r);
|
|
48100
|
+
}
|
|
48101
|
+
let dx = 0;
|
|
48102
|
+
let dy = 0;
|
|
48103
|
+
if (minX + dx < 2) dx = 2 - minX;
|
|
48104
|
+
if (maxX + dx > width - 2) dx = width - 2 - maxX;
|
|
48105
|
+
if (minY + dy < 2) dy = 2 - minY;
|
|
48106
|
+
if (maxY + dy > height - 2) dy = height - 2 - maxY;
|
|
48107
|
+
const legsOut = [];
|
|
48108
|
+
for (const { e, mx, my } of positions) {
|
|
48109
|
+
const fx = mx + dx;
|
|
48110
|
+
const fy = my + dy;
|
|
48111
|
+
placePoi(e, fx, fy, clusterId);
|
|
48112
|
+
legsOut.push({ x2: fx, y2: fy, color: poiFill(e.p).fill });
|
|
48113
|
+
}
|
|
48114
|
+
clusters.push({
|
|
48115
|
+
id: clusterId,
|
|
48116
|
+
cx: cx0 + dx,
|
|
48117
|
+
cy: cy0 + dy,
|
|
48118
|
+
count: g.length,
|
|
48119
|
+
hitR: ringR + maxR + 6,
|
|
48120
|
+
legs: legsOut
|
|
46879
48121
|
});
|
|
46880
48122
|
}
|
|
46881
48123
|
const legs = [];
|
|
@@ -46925,16 +48167,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46925
48167
|
if (!a || !b) continue;
|
|
46926
48168
|
const mx = (a.cx + b.cx) / 2;
|
|
46927
48169
|
const my = (a.cy + b.cy) / 2;
|
|
48170
|
+
const bow = {
|
|
48171
|
+
curved: leg.style === "arc",
|
|
48172
|
+
offset: 0,
|
|
48173
|
+
labelX: mx,
|
|
48174
|
+
labelY: my - 4
|
|
48175
|
+
};
|
|
48176
|
+
const routeLabelStyle = leg.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46928
48177
|
legs.push({
|
|
46929
|
-
d: legPath(a, b,
|
|
48178
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46930
48179
|
width: routeWidthFor(Number(leg.value)),
|
|
46931
48180
|
color: mix(palette.text, palette.bg, 72),
|
|
46932
48181
|
arrow: true,
|
|
46933
48182
|
lineNumber: leg.lineNumber,
|
|
46934
48183
|
...leg.label !== void 0 && {
|
|
46935
48184
|
label: leg.label,
|
|
46936
|
-
labelX:
|
|
46937
|
-
labelY:
|
|
48185
|
+
labelX: bow.labelX,
|
|
48186
|
+
labelY: bow.labelY,
|
|
48187
|
+
labelColor: routeLabelStyle.color,
|
|
48188
|
+
labelHalo: routeLabelStyle.halo,
|
|
48189
|
+
labelHaloColor: routeLabelStyle.haloColor
|
|
46938
48190
|
}
|
|
46939
48191
|
});
|
|
46940
48192
|
}
|
|
@@ -46962,20 +48214,29 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46962
48214
|
const a = poiScreen.get(e.fromId);
|
|
46963
48215
|
const b = poiScreen.get(e.toId);
|
|
46964
48216
|
if (!a || !b) return;
|
|
46965
|
-
const
|
|
46966
|
-
const offset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
48217
|
+
const fanOffset = n > 1 ? (i - (n - 1) / 2) * FAN_STEP : 0;
|
|
46967
48218
|
const mx = (a.cx + b.cx) / 2;
|
|
46968
48219
|
const my = (a.cy + b.cy) / 2;
|
|
48220
|
+
const bow = {
|
|
48221
|
+
curved: e.style === "arc" || n > 1,
|
|
48222
|
+
offset: fanOffset,
|
|
48223
|
+
labelX: mx,
|
|
48224
|
+
labelY: my - 4
|
|
48225
|
+
};
|
|
48226
|
+
const edgeLabelStyle = e.label !== void 0 ? labelOnFill(fillAt(bow.labelX, bow.labelY)) : void 0;
|
|
46969
48227
|
legs.push({
|
|
46970
|
-
d: legPath(a, b, curved, offset),
|
|
48228
|
+
d: legPath(a, b, bow.curved, bow.offset),
|
|
46971
48229
|
width: widthFor(e),
|
|
46972
48230
|
color: mix(palette.text, palette.bg, 66),
|
|
46973
48231
|
arrow: e.directed,
|
|
46974
48232
|
lineNumber: e.lineNumber,
|
|
46975
48233
|
...e.label !== void 0 && {
|
|
46976
48234
|
label: e.label,
|
|
46977
|
-
labelX:
|
|
46978
|
-
labelY:
|
|
48235
|
+
labelX: bow.labelX,
|
|
48236
|
+
labelY: bow.labelY,
|
|
48237
|
+
labelColor: edgeLabelStyle.color,
|
|
48238
|
+
labelHalo: edgeLabelStyle.halo,
|
|
48239
|
+
labelHaloColor: edgeLabelStyle.haloColor
|
|
46979
48240
|
}
|
|
46980
48241
|
});
|
|
46981
48242
|
});
|
|
@@ -47017,48 +48278,73 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47017
48278
|
}
|
|
47018
48279
|
}
|
|
47019
48280
|
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));
|
|
47020
|
-
const
|
|
48281
|
+
const showRegionLabels = resolved.directives.noRegionLabels !== true;
|
|
48282
|
+
const isCompact = width < COMPACT_WIDTH_PX;
|
|
47021
48283
|
const LABEL_PADX = 6;
|
|
47022
48284
|
const LABEL_PADY = 3;
|
|
47023
|
-
const labelW = (text) => measureLegendText(text,
|
|
47024
|
-
const labelH =
|
|
48285
|
+
const labelW = (text) => measureLegendText(text, FONT2) + 2 * LABEL_PADX;
|
|
48286
|
+
const labelH = FONT2 + 2 * LABEL_PADY;
|
|
47025
48287
|
const pushRegionLabel = (x, y, text, fill2, lineNumber) => {
|
|
47026
|
-
const color =
|
|
47027
|
-
|
|
47028
|
-
|
|
47029
|
-
|
|
48288
|
+
const { color, haloColor } = labelOnFill(fill2);
|
|
48289
|
+
const halfW = measureLegendText(text, FONT2) / 2;
|
|
48290
|
+
const overflows = [y - FONT2 * 0.55, y - FONT2 * 0.1].some(
|
|
48291
|
+
(sy) => fillAt(x - halfW, sy) !== fill2 || fillAt(x + halfW, sy) !== fill2
|
|
47030
48292
|
);
|
|
47031
|
-
const haloColor = color === palette.textOnFillLight ? palette.textOnFillDark : palette.textOnFillLight;
|
|
47032
48293
|
labels.push({
|
|
47033
48294
|
x,
|
|
47034
48295
|
y,
|
|
47035
48296
|
text,
|
|
47036
48297
|
anchor: "middle",
|
|
47037
48298
|
color,
|
|
47038
|
-
halo:
|
|
48299
|
+
halo: overflows,
|
|
47039
48300
|
haloColor,
|
|
47040
48301
|
lineNumber
|
|
47041
48302
|
});
|
|
47042
48303
|
};
|
|
47043
|
-
const
|
|
47044
|
-
|
|
47045
|
-
|
|
48304
|
+
const REGION_LABEL_GAP = 2;
|
|
48305
|
+
const regionLabelRect = (cx, cy, text) => {
|
|
48306
|
+
const w = measureLegendText(text, FONT2) + 2 * REGION_LABEL_GAP;
|
|
48307
|
+
return { x: cx - w / 2, y: cy - FONT2 / 2, w, h: FONT2 };
|
|
47046
48308
|
};
|
|
47047
|
-
if (
|
|
47048
|
-
|
|
47049
|
-
|
|
47050
|
-
const
|
|
47051
|
-
if (!
|
|
48309
|
+
if (showRegionLabels) {
|
|
48310
|
+
const frameContainers = new Set(resolved.poiFrameContainers);
|
|
48311
|
+
const entries = regions.map((r) => {
|
|
48312
|
+
const isContainer = frameContainers.has(r.id);
|
|
48313
|
+
if (r.layer === "base" && !isContainer || r.label === void 0)
|
|
48314
|
+
return null;
|
|
48315
|
+
const isUsState = r.layer === "us-state" || r.id.startsWith("US-");
|
|
48316
|
+
const f = isUsState ? usLayer?.get(r.id) : worldLayer.get(r.id);
|
|
48317
|
+
if (!f) return null;
|
|
47052
48318
|
const [[x0, y0], [x1, y1]] = path.bounds(f);
|
|
47053
|
-
const
|
|
47054
|
-
|
|
47055
|
-
const
|
|
48319
|
+
const boxW = x1 - x0;
|
|
48320
|
+
const boxH = y1 - y0;
|
|
48321
|
+
const abbrev = isUsState ? r.id.replace(/^US-/, "") : void 0;
|
|
48322
|
+
const candidates = abbrev !== void 0 ? isCompact ? [abbrev, r.label] : [r.label, abbrev] : [r.label];
|
|
48323
|
+
const anchor = !isUsState ? WORLD_LABEL_ANCHORS[r.id] : void 0;
|
|
47056
48324
|
const c = anchor ? project(anchor[0], anchor[1]) : path.centroid(f);
|
|
47057
|
-
if (!c || !Number.isFinite(c[0]))
|
|
48325
|
+
if (!c || !Number.isFinite(c[0])) return null;
|
|
48326
|
+
return { r, c, boxW, boxH, area: boxW * boxH, candidates };
|
|
48327
|
+
}).filter((e) => e !== null).sort((a, b) => b.area - a.area || a.r.lineNumber - b.r.lineNumber);
|
|
48328
|
+
const placedRegionRects = [];
|
|
48329
|
+
const POI_LABEL_PAD = 14;
|
|
48330
|
+
const poiObstacles = pois.map((p) => ({
|
|
48331
|
+
x: p.cx - p.r - POI_LABEL_PAD,
|
|
48332
|
+
y: p.cy - p.r - POI_LABEL_PAD,
|
|
48333
|
+
w: 2 * (p.r + POI_LABEL_PAD),
|
|
48334
|
+
h: 2 * (p.r + POI_LABEL_PAD)
|
|
48335
|
+
}));
|
|
48336
|
+
for (const { r, c, boxW, boxH, candidates } of entries) {
|
|
48337
|
+
const text = candidates.find((t) => {
|
|
48338
|
+
if (labelW(t) > boxW || labelH > boxH) return false;
|
|
48339
|
+
const rect = regionLabelRect(c[0], c[1], t);
|
|
48340
|
+
return !placedRegionRects.some((p) => rectsOverlap(rect, p)) && !poiObstacles.some((o) => rectsOverlap(rect, o));
|
|
48341
|
+
});
|
|
48342
|
+
if (text === void 0) continue;
|
|
48343
|
+
placedRegionRects.push(regionLabelRect(c[0], c[1], text));
|
|
47058
48344
|
pushRegionLabel(c[0], c[1], text, r.fill, r.lineNumber);
|
|
47059
48345
|
}
|
|
47060
48346
|
for (const seed of insetLabelSeeds) {
|
|
47061
|
-
const text =
|
|
48347
|
+
const text = isCompact ? seed.iso.replace(/^US-/, "") : seed.name;
|
|
47062
48348
|
const src = regionById.get(seed.iso);
|
|
47063
48349
|
pushRegionLabel(
|
|
47064
48350
|
seed.x,
|
|
@@ -47069,22 +48355,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47069
48355
|
);
|
|
47070
48356
|
}
|
|
47071
48357
|
}
|
|
47072
|
-
|
|
47073
|
-
|
|
47074
|
-
const ordered = [...pois].sort(
|
|
47075
|
-
(a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1)
|
|
47076
|
-
);
|
|
48358
|
+
if (resolved.directives.noPoiLabels !== true) {
|
|
48359
|
+
const ordered = [...pois].filter((p) => p.clusterId === void 0).sort((a, b) => a.lineNumber - b.lineNumber || (a.id < b.id ? -1 : 1));
|
|
47077
48360
|
const poiById = new Map(resolved.pois.map((q) => [q.id, q]));
|
|
47078
48361
|
const labelText = (p) => {
|
|
47079
48362
|
const src = poiById.get(p.id);
|
|
47080
48363
|
return src?.label ?? src?.name ?? p.id;
|
|
47081
48364
|
};
|
|
47082
|
-
const poiLabH =
|
|
48365
|
+
const poiLabH = FONT2 * 1.25;
|
|
47083
48366
|
const labelInfo = (p) => {
|
|
47084
48367
|
const text = labelText(p);
|
|
47085
|
-
return { text, w: measureLegendText(text,
|
|
48368
|
+
return { text, w: measureLegendText(text, FONT2) };
|
|
47086
48369
|
};
|
|
47087
48370
|
const GAP = 3;
|
|
48371
|
+
const clusterMembersById = /* @__PURE__ */ new Map();
|
|
48372
|
+
for (const p of pois) {
|
|
48373
|
+
if (p.clusterId === void 0) continue;
|
|
48374
|
+
const arr = clusterMembersById.get(p.clusterId);
|
|
48375
|
+
if (arr) arr.push(p);
|
|
48376
|
+
else clusterMembersById.set(p.clusterId, [p]);
|
|
48377
|
+
}
|
|
47088
48378
|
const inlineRect = (p, w, side) => {
|
|
47089
48379
|
switch (side) {
|
|
47090
48380
|
case "right":
|
|
@@ -47114,11 +48404,11 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47114
48404
|
const x = side === "right" ? rect.x : side === "left" ? rect.x + w : p.cx;
|
|
47115
48405
|
labels.push({
|
|
47116
48406
|
x,
|
|
47117
|
-
y: rect.y + poiLabH / 2 +
|
|
48407
|
+
y: rect.y + poiLabH / 2 + FONT2 / 3,
|
|
47118
48408
|
text,
|
|
47119
48409
|
anchor,
|
|
47120
48410
|
color: palette.text,
|
|
47121
|
-
halo:
|
|
48411
|
+
halo: false,
|
|
47122
48412
|
haloColor: palette.bg,
|
|
47123
48413
|
poiId: p.id,
|
|
47124
48414
|
lineNumber: p.lineNumber
|
|
@@ -47129,43 +48419,60 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47129
48419
|
return rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect);
|
|
47130
48420
|
};
|
|
47131
48421
|
const GROUP_R = 30;
|
|
47132
|
-
const
|
|
48422
|
+
const groups2 = [];
|
|
47133
48423
|
for (const p of ordered) {
|
|
47134
|
-
const near =
|
|
48424
|
+
const near = groups2.find(
|
|
47135
48425
|
(g) => g.some((q) => Math.hypot(q.cx - p.cx, q.cy - p.cy) < GROUP_R)
|
|
47136
48426
|
);
|
|
47137
48427
|
if (near) near.push(p);
|
|
47138
|
-
else
|
|
48428
|
+
else groups2.push([p]);
|
|
47139
48429
|
}
|
|
47140
48430
|
const ROW_GAP2 = 3;
|
|
47141
48431
|
const step = poiLabH + ROW_GAP2;
|
|
47142
48432
|
const COL_GAP = 16;
|
|
47143
|
-
const
|
|
47144
|
-
|
|
48433
|
+
const makeItems = (group) => group.map((p) => ({ p, ...labelInfo(p) })).sort((a, b) => a.p.cy - b.p.cy || (a.text < b.text ? -1 : 1));
|
|
48434
|
+
const columnRows = (items, side) => {
|
|
47145
48435
|
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
47146
48436
|
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
47147
|
-
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
47148
48437
|
const maxW = Math.max(...items.map((o) => o.w));
|
|
47149
|
-
const
|
|
47150
|
-
const colX = side === "right" ? right + COL_GAP : left - COL_GAP;
|
|
48438
|
+
const cyMid = (Math.min(...items.map((o) => o.p.cy)) + Math.max(...items.map((o) => o.p.cy))) / 2;
|
|
48439
|
+
const colX = side === "right" ? Math.min(right + COL_GAP, width - 2 - maxW) : Math.max(left - COL_GAP, 2 + maxW);
|
|
47151
48440
|
const totalH = items.length * step;
|
|
47152
48441
|
let startY = cyMid - totalH / 2;
|
|
47153
48442
|
startY = Math.max(2, Math.min(startY, height - totalH - 2));
|
|
47154
|
-
items.
|
|
48443
|
+
return items.map((o, i) => {
|
|
47155
48444
|
const rowCy = startY + i * step + step / 2;
|
|
47156
|
-
|
|
47157
|
-
|
|
47158
|
-
|
|
47159
|
-
|
|
47160
|
-
|
|
47161
|
-
|
|
48445
|
+
return {
|
|
48446
|
+
o,
|
|
48447
|
+
colX,
|
|
48448
|
+
rowCy,
|
|
48449
|
+
rect: {
|
|
48450
|
+
x: side === "right" ? colX : colX - o.w,
|
|
48451
|
+
y: rowCy - poiLabH / 2,
|
|
48452
|
+
w: o.w,
|
|
48453
|
+
h: poiLabH
|
|
48454
|
+
}
|
|
48455
|
+
};
|
|
48456
|
+
});
|
|
48457
|
+
};
|
|
48458
|
+
const wouldColumnBeClean = (items, side) => columnRows(items, side).every(
|
|
48459
|
+
({ rect }) => rect.x >= 0 && rect.x + rect.w <= width && rect.y >= 0 && rect.y + rect.h <= height && !collides(rect)
|
|
48460
|
+
);
|
|
48461
|
+
const defaultColumnSide = (items) => {
|
|
48462
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48463
|
+
const maxW = Math.max(...items.map((o) => o.w));
|
|
48464
|
+
return right + COL_GAP + maxW <= width - 2 ? "right" : "left";
|
|
48465
|
+
};
|
|
48466
|
+
const commitColumn = (items, side, clusterId) => {
|
|
48467
|
+
for (const { o, colX, rowCy, rect } of columnRows(items, side)) {
|
|
48468
|
+
obstacles.push(rect);
|
|
47162
48469
|
labels.push({
|
|
47163
48470
|
x: colX,
|
|
47164
|
-
y: rowCy +
|
|
48471
|
+
y: rowCy + FONT2 / 3,
|
|
47165
48472
|
text: o.text,
|
|
47166
48473
|
anchor: side === "right" ? "start" : "end",
|
|
47167
48474
|
color: palette.text,
|
|
47168
|
-
halo:
|
|
48475
|
+
halo: false,
|
|
47169
48476
|
haloColor: palette.bg,
|
|
47170
48477
|
leader: {
|
|
47171
48478
|
x1: o.p.cx,
|
|
@@ -47175,24 +48482,141 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47175
48482
|
},
|
|
47176
48483
|
leaderColor: o.p.fill,
|
|
47177
48484
|
poiId: o.p.id,
|
|
47178
|
-
lineNumber: o.p.lineNumber
|
|
48485
|
+
lineNumber: o.p.lineNumber,
|
|
48486
|
+
...clusterId !== void 0 && { clusterMember: clusterId }
|
|
47179
48487
|
});
|
|
48488
|
+
}
|
|
48489
|
+
};
|
|
48490
|
+
const pushHidden = (p) => {
|
|
48491
|
+
const { text, w } = labelInfo(p);
|
|
48492
|
+
let x = p.cx + p.r + GAP;
|
|
48493
|
+
let anchor = "start";
|
|
48494
|
+
if (x + w > width) {
|
|
48495
|
+
x = p.cx - p.r - GAP - w;
|
|
48496
|
+
anchor = "end";
|
|
48497
|
+
}
|
|
48498
|
+
const y = Math.max(0, Math.min(p.cy - poiLabH / 2, height - poiLabH));
|
|
48499
|
+
labels.push({
|
|
48500
|
+
x: anchor === "start" ? x : x + w,
|
|
48501
|
+
y: y + poiLabH / 2 + FONT2 / 3,
|
|
48502
|
+
text,
|
|
48503
|
+
anchor,
|
|
48504
|
+
color: palette.text,
|
|
48505
|
+
halo: false,
|
|
48506
|
+
haloColor: palette.bg,
|
|
48507
|
+
poiId: p.id,
|
|
48508
|
+
hidden: true,
|
|
48509
|
+
lineNumber: p.lineNumber
|
|
47180
48510
|
});
|
|
47181
48511
|
};
|
|
47182
|
-
for (const
|
|
48512
|
+
for (const [clusterId, members] of clusterMembersById) {
|
|
48513
|
+
if (members.length === 0) continue;
|
|
48514
|
+
const items = makeItems(members);
|
|
48515
|
+
const side = wouldColumnBeClean(items, "right") ? "right" : wouldColumnBeClean(items, "left") ? "left" : defaultColumnSide(items);
|
|
48516
|
+
commitColumn(items, side, clusterId);
|
|
48517
|
+
}
|
|
48518
|
+
const maxExtent = MAX_CLUSTER_EXTENT_FACTOR * Math.min(width, height);
|
|
48519
|
+
const clusterPending = [];
|
|
48520
|
+
for (const g of groups2) {
|
|
48521
|
+
const items = makeItems(g);
|
|
47183
48522
|
if (g.length === 1) {
|
|
47184
|
-
const p =
|
|
47185
|
-
const { text, w } = labelInfo(p);
|
|
48523
|
+
const { p, text, w } = items[0];
|
|
47186
48524
|
const side = ["right", "left", "above", "below"].find(
|
|
47187
48525
|
(s) => inlineFits(p, w, s)
|
|
47188
48526
|
);
|
|
47189
|
-
if (side)
|
|
47190
|
-
|
|
47191
|
-
|
|
48527
|
+
if (side) pushInline(p, text, w, side);
|
|
48528
|
+
else commitColumn(items, defaultColumnSide(items));
|
|
48529
|
+
continue;
|
|
48530
|
+
}
|
|
48531
|
+
const left = Math.min(...items.map((o) => o.p.cx - o.p.r));
|
|
48532
|
+
const right = Math.max(...items.map((o) => o.p.cx + o.p.r));
|
|
48533
|
+
const minCy = Math.min(...items.map((o) => o.p.cy));
|
|
48534
|
+
const maxCy = Math.max(...items.map((o) => o.p.cy));
|
|
48535
|
+
const diag = Math.hypot(right - left, maxCy - minCy);
|
|
48536
|
+
if (diag > maxExtent || items.length > MAX_COLUMN_ROWS) {
|
|
48537
|
+
items.forEach((o) => pushHidden(o.p));
|
|
48538
|
+
} else {
|
|
48539
|
+
clusterPending.push(items);
|
|
48540
|
+
}
|
|
48541
|
+
}
|
|
48542
|
+
for (const items of clusterPending) {
|
|
48543
|
+
const side = ["right", "left"].find(
|
|
48544
|
+
(s) => wouldColumnBeClean(items, s)
|
|
48545
|
+
);
|
|
48546
|
+
if (side) commitColumn(items, side);
|
|
48547
|
+
else items.forEach((o) => pushHidden(o.p));
|
|
48548
|
+
}
|
|
48549
|
+
}
|
|
48550
|
+
if (resolved.directives.noContextLabels !== true) {
|
|
48551
|
+
for (const l of labels) {
|
|
48552
|
+
if (l.hidden) continue;
|
|
48553
|
+
const w = labelW(l.text);
|
|
48554
|
+
const x = l.anchor === "start" ? l.x : l.anchor === "end" ? l.x - w : l.x - w / 2;
|
|
48555
|
+
obstacles.push({ x, y: l.y - labelH / 2, w, h: labelH });
|
|
48556
|
+
}
|
|
48557
|
+
for (const box of insets)
|
|
48558
|
+
obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
|
|
48559
|
+
const countryCandidates = [];
|
|
48560
|
+
for (const f of worldLayer.values()) {
|
|
48561
|
+
const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
|
|
48562
|
+
if (!iso || regionById.has(iso)) continue;
|
|
48563
|
+
let hasReferencedSub = false;
|
|
48564
|
+
for (const k of regionById.keys())
|
|
48565
|
+
if (k.startsWith(iso + "-")) {
|
|
48566
|
+
hasReferencedSub = true;
|
|
48567
|
+
break;
|
|
47192
48568
|
}
|
|
48569
|
+
if (hasReferencedSub) continue;
|
|
48570
|
+
const b = path.bounds(f);
|
|
48571
|
+
const [x0, y0] = b[0];
|
|
48572
|
+
const [x1, y1] = b[1];
|
|
48573
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48574
|
+
const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
|
|
48575
|
+
const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
|
|
48576
|
+
countryCandidates.push({
|
|
48577
|
+
name: f.properties?.name ?? iso,
|
|
48578
|
+
bbox: [x0, y0, x1, y1],
|
|
48579
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48580
|
+
});
|
|
48581
|
+
}
|
|
48582
|
+
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
48583
|
+
(id) => id.startsWith("US-")
|
|
48584
|
+
);
|
|
48585
|
+
if (usLayer && framedStateContainers) {
|
|
48586
|
+
const containerSet = new Set(resolved.poiFrameContainers);
|
|
48587
|
+
for (const [iso, f] of usLayer) {
|
|
48588
|
+
if (containerSet.has(iso) || regionById.has(iso)) continue;
|
|
48589
|
+
const viewF = cullFeatureToView(f);
|
|
48590
|
+
if (!viewF) continue;
|
|
48591
|
+
const b = path.bounds(viewF);
|
|
48592
|
+
const [x0, y0] = b[0];
|
|
48593
|
+
const [x1, y1] = b[1];
|
|
48594
|
+
if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
|
|
48595
|
+
const a = path.centroid(viewF);
|
|
48596
|
+
countryCandidates.push({
|
|
48597
|
+
name: f.properties?.name ?? iso,
|
|
48598
|
+
bbox: [x0, y0, x1, y1],
|
|
48599
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
48600
|
+
});
|
|
47193
48601
|
}
|
|
47194
|
-
placeColumn(g);
|
|
47195
48602
|
}
|
|
48603
|
+
const contextLabels = placeContextLabels({
|
|
48604
|
+
projection: resolved.projection,
|
|
48605
|
+
dLonSpan,
|
|
48606
|
+
dLatSpan,
|
|
48607
|
+
width,
|
|
48608
|
+
height,
|
|
48609
|
+
waterBodies: data.waterBodies,
|
|
48610
|
+
countries: countryCandidates,
|
|
48611
|
+
palette,
|
|
48612
|
+
project,
|
|
48613
|
+
collides,
|
|
48614
|
+
// Water labels must stay over open water — `fillAt` returns the ocean
|
|
48615
|
+
// backdrop colour off-land and a region fill on-land (lakes/states count
|
|
48616
|
+
// as land here, which is the safe side for an ocean name).
|
|
48617
|
+
overLand: (x, y) => fillAt(x, y) !== water
|
|
48618
|
+
});
|
|
48619
|
+
labels.push(...contextLabels);
|
|
47196
48620
|
}
|
|
47197
48621
|
let legend = null;
|
|
47198
48622
|
if (!resolved.directives.noLegend) {
|
|
@@ -47229,60 +48653,104 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47229
48653
|
rivers,
|
|
47230
48654
|
relief,
|
|
47231
48655
|
reliefHatch,
|
|
48656
|
+
coastlineStyle,
|
|
47232
48657
|
legs,
|
|
47233
48658
|
pois,
|
|
48659
|
+
clusters,
|
|
47234
48660
|
labels,
|
|
47235
48661
|
legend,
|
|
47236
48662
|
insets,
|
|
47237
48663
|
insetRegions,
|
|
47238
48664
|
projection,
|
|
47239
|
-
stretch: stretchParams
|
|
48665
|
+
stretch: stretchParams,
|
|
48666
|
+
diagnostics: []
|
|
47240
48667
|
};
|
|
47241
48668
|
}
|
|
47242
|
-
var import_d3_geo2, import_topojson_client2, FIT_PAD,
|
|
48669
|
+
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;
|
|
47243
48670
|
var init_layout15 = __esm({
|
|
47244
48671
|
"src/map/layout.ts"() {
|
|
47245
48672
|
"use strict";
|
|
47246
48673
|
import_d3_geo2 = require("d3-geo");
|
|
47247
48674
|
import_topojson_client2 = require("topojson-client");
|
|
47248
48675
|
init_color_utils();
|
|
48676
|
+
init_geo();
|
|
48677
|
+
init_colorize();
|
|
47249
48678
|
init_colors();
|
|
47250
48679
|
init_label_layout();
|
|
47251
48680
|
init_legend_constants();
|
|
47252
48681
|
init_title_constants();
|
|
48682
|
+
init_context_labels();
|
|
47253
48683
|
FIT_PAD = 24;
|
|
47254
|
-
|
|
48684
|
+
RAMP_FLOOR2 = 15;
|
|
47255
48685
|
R_DEFAULT = 6;
|
|
47256
48686
|
R_MIN = 4;
|
|
47257
48687
|
R_MAX = 22;
|
|
47258
48688
|
W_MIN = 1.25;
|
|
47259
48689
|
W_MAX = 8;
|
|
47260
|
-
|
|
47261
|
-
|
|
48690
|
+
FONT2 = 11;
|
|
48691
|
+
WORLD_LABEL_ANCHORS = {
|
|
48692
|
+
US: [-98.5, 39.5]
|
|
48693
|
+
// CONUS geographic centre (near Lebanon, Kansas)
|
|
48694
|
+
};
|
|
48695
|
+
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
48696
|
+
MAX_COLUMN_ROWS = 7;
|
|
48697
|
+
REGION_LABEL_HALO_RATIO = 4.5;
|
|
47262
48698
|
LAND_TINT_LIGHT = 12;
|
|
47263
48699
|
LAND_TINT_DARK = 24;
|
|
47264
48700
|
TAG_TINT_LIGHT = 60;
|
|
47265
48701
|
TAG_TINT_DARK = 68;
|
|
47266
|
-
WATER_TINT_LIGHT =
|
|
47267
|
-
WATER_TINT_DARK =
|
|
48702
|
+
WATER_TINT_LIGHT = 24;
|
|
48703
|
+
WATER_TINT_DARK = 24;
|
|
47268
48704
|
RIVER_WIDTH = 1.3;
|
|
48705
|
+
COMPACT_WIDTH_PX = 480;
|
|
47269
48706
|
RELIEF_MIN_AREA = 12;
|
|
47270
48707
|
RELIEF_MIN_DIM = 2;
|
|
47271
|
-
RELIEF_HATCH_SPACING =
|
|
47272
|
-
RELIEF_HATCH_WIDTH = 0.
|
|
47273
|
-
RELIEF_HATCH_STRENGTH =
|
|
48708
|
+
RELIEF_HATCH_SPACING = 1.5;
|
|
48709
|
+
RELIEF_HATCH_WIDTH = 0.2;
|
|
48710
|
+
RELIEF_HATCH_STRENGTH = 26;
|
|
48711
|
+
COASTLINE_RING_COUNT = 5;
|
|
48712
|
+
COASTLINE_D0 = 16e-4;
|
|
48713
|
+
COASTLINE_STEP = 28e-4;
|
|
48714
|
+
COASTLINE_THICKNESS = 14e-4;
|
|
48715
|
+
COASTLINE_OPACITY_NEAR = 0.5;
|
|
48716
|
+
COASTLINE_OPACITY_FAR = 0.1;
|
|
48717
|
+
COASTLINE_MIN_EXTENT = 6e-4;
|
|
48718
|
+
COASTLINE_MIN_EXTENT_GLOBAL = 6e-4;
|
|
48719
|
+
COASTLINE_STROKE_MIX = 32;
|
|
47274
48720
|
FOREIGN_TINT_LIGHT = 30;
|
|
47275
48721
|
FOREIGN_TINT_DARK = 62;
|
|
47276
48722
|
MUTED_FOREIGN_LIGHT = 28;
|
|
47277
48723
|
MUTED_FOREIGN_DARK = 16;
|
|
47278
48724
|
COLO_R = 9;
|
|
47279
48725
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
48726
|
+
STACK_OVERLAP = 1;
|
|
48727
|
+
STACK_RING_MAX = 8;
|
|
48728
|
+
STACK_RING_GAP = 4;
|
|
47280
48729
|
FAN_STEP = 16;
|
|
47281
48730
|
ARC_CURVE_FRAC = 0.18;
|
|
48731
|
+
decodeCache = /* @__PURE__ */ new WeakMap();
|
|
47282
48732
|
usConusProjection = () => (0, import_d3_geo2.geoConicEqualArea)().parallels([29.5, 45.5]).rotate([96, 0]);
|
|
47283
48733
|
alaskaProjection = () => (0, import_d3_geo2.geoConicEqualArea)().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]);
|
|
47284
48734
|
hawaiiProjection = () => (0, import_d3_geo2.geoMercator)();
|
|
47285
48735
|
INSET_STATES = /* @__PURE__ */ new Set(["US-AK", "US-HI"]);
|
|
48736
|
+
inAlaska = (lon, lat) => lat >= 51 && (lon <= -129 || lon >= 172);
|
|
48737
|
+
inHawaii = (lon, lat) => lat >= 18 && lat <= 23 && lon >= -161 && lon <= -154;
|
|
48738
|
+
FOREIGN_BORDER = {
|
|
48739
|
+
CA: [
|
|
48740
|
+
"US-AK",
|
|
48741
|
+
"US-WA",
|
|
48742
|
+
"US-ID",
|
|
48743
|
+
"US-MT",
|
|
48744
|
+
"US-ND",
|
|
48745
|
+
"US-MN",
|
|
48746
|
+
"US-MI",
|
|
48747
|
+
"US-NY",
|
|
48748
|
+
"US-VT",
|
|
48749
|
+
"US-NH",
|
|
48750
|
+
"US-ME"
|
|
48751
|
+
],
|
|
48752
|
+
MX: ["US-CA", "US-AZ", "US-NM", "US-TX"]
|
|
48753
|
+
};
|
|
47286
48754
|
US_NON_CONUS = /* @__PURE__ */ new Set([
|
|
47287
48755
|
"US-AK",
|
|
47288
48756
|
"US-HI",
|
|
@@ -47301,6 +48769,98 @@ __export(renderer_exports16, {
|
|
|
47301
48769
|
renderMap: () => renderMap,
|
|
47302
48770
|
renderMapForExport: () => renderMapForExport
|
|
47303
48771
|
});
|
|
48772
|
+
function pointInRing2(px, py, ring) {
|
|
48773
|
+
let inside = false;
|
|
48774
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
48775
|
+
const [xi, yi] = ring[i];
|
|
48776
|
+
const [xj, yj] = ring[j];
|
|
48777
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
|
|
48778
|
+
inside = !inside;
|
|
48779
|
+
}
|
|
48780
|
+
return inside;
|
|
48781
|
+
}
|
|
48782
|
+
function ringToPath(ring) {
|
|
48783
|
+
let d = "";
|
|
48784
|
+
for (let i = 0; i < ring.length; i++)
|
|
48785
|
+
d += (i ? "L" : "M") + ring[i][0] + "," + ring[i][1];
|
|
48786
|
+
return d + "Z";
|
|
48787
|
+
}
|
|
48788
|
+
function polylineToPath(pts) {
|
|
48789
|
+
let d = "";
|
|
48790
|
+
for (let i = 0; i < pts.length; i++)
|
|
48791
|
+
d += (i ? "L" : "M") + pts[i][0] + "," + pts[i][1];
|
|
48792
|
+
return d;
|
|
48793
|
+
}
|
|
48794
|
+
function ringToCoastPaths(ring, frame) {
|
|
48795
|
+
if (!frame) return [ringToPath(ring)];
|
|
48796
|
+
const n = ring.length;
|
|
48797
|
+
const eps = 0.75;
|
|
48798
|
+
const onL = (x) => Math.abs(x) <= eps;
|
|
48799
|
+
const onR = (x) => Math.abs(x - frame.w) <= eps;
|
|
48800
|
+
const onT = (y) => Math.abs(y) <= eps;
|
|
48801
|
+
const onB = (y) => Math.abs(y - frame.h) <= eps;
|
|
48802
|
+
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]);
|
|
48803
|
+
let firstBreak = -1;
|
|
48804
|
+
for (let i = 0; i < n; i++)
|
|
48805
|
+
if (isFrameEdge(ring[i], ring[(i + 1) % n])) {
|
|
48806
|
+
firstBreak = i;
|
|
48807
|
+
break;
|
|
48808
|
+
}
|
|
48809
|
+
if (firstBreak === -1) return [ringToPath(ring)];
|
|
48810
|
+
const paths = [];
|
|
48811
|
+
let cur = [];
|
|
48812
|
+
const start = (firstBreak + 1) % n;
|
|
48813
|
+
for (let k = 0; k < n; k++) {
|
|
48814
|
+
const i = (start + k) % n;
|
|
48815
|
+
const a = ring[i];
|
|
48816
|
+
const b = ring[(i + 1) % n];
|
|
48817
|
+
if (isFrameEdge(a, b)) {
|
|
48818
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48819
|
+
cur = [];
|
|
48820
|
+
continue;
|
|
48821
|
+
}
|
|
48822
|
+
if (cur.length === 0) cur.push(a);
|
|
48823
|
+
cur.push(b);
|
|
48824
|
+
}
|
|
48825
|
+
if (cur.length >= 2) paths.push(polylineToPath(cur));
|
|
48826
|
+
return paths;
|
|
48827
|
+
}
|
|
48828
|
+
function coastlineOuterRings(regions, minExtent, frame) {
|
|
48829
|
+
const paths = [];
|
|
48830
|
+
for (const r of regions) {
|
|
48831
|
+
const rings = parsePathRings(r.d);
|
|
48832
|
+
for (let i = 0; i < rings.length; i++) {
|
|
48833
|
+
const ring = rings[i];
|
|
48834
|
+
if (ring.length < 3) continue;
|
|
48835
|
+
let minX = Infinity;
|
|
48836
|
+
let minY = Infinity;
|
|
48837
|
+
let maxX = -Infinity;
|
|
48838
|
+
let maxY = -Infinity;
|
|
48839
|
+
for (const [x, y] of ring) {
|
|
48840
|
+
if (x < minX) minX = x;
|
|
48841
|
+
if (x > maxX) maxX = x;
|
|
48842
|
+
if (y < minY) minY = y;
|
|
48843
|
+
if (y > maxY) maxY = y;
|
|
48844
|
+
}
|
|
48845
|
+
if (Math.max(maxX - minX, maxY - minY) < minExtent) continue;
|
|
48846
|
+
const [fx, fy] = ring[0];
|
|
48847
|
+
let depth = 0;
|
|
48848
|
+
for (let j = 0; j < rings.length; j++)
|
|
48849
|
+
if (j !== i && pointInRing2(fx, fy, rings[j])) depth++;
|
|
48850
|
+
if (depth % 2 === 1) continue;
|
|
48851
|
+
paths.push(...ringToCoastPaths(ring, frame));
|
|
48852
|
+
}
|
|
48853
|
+
}
|
|
48854
|
+
return paths;
|
|
48855
|
+
}
|
|
48856
|
+
function appendWaterLines(g, outerRings, style, flatWater) {
|
|
48857
|
+
const d = outerRings.join(" ");
|
|
48858
|
+
const linesOuterFirst = [...style.lines].sort((a, b) => b.d - a.d);
|
|
48859
|
+
for (const line12 of linesOuterFirst) {
|
|
48860
|
+
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");
|
|
48861
|
+
g.append("path").attr("d", d).attr("stroke", flatWater).attr("stroke-width", 2 * line12.d).attr("stroke-linejoin", "round").attr("stroke-linecap", "round");
|
|
48862
|
+
}
|
|
48863
|
+
}
|
|
47304
48864
|
function renderMap(container, resolved, data, palette, isDark, onClickItem, exportDims, activeGroupOverride) {
|
|
47305
48865
|
d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
47306
48866
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -47313,6 +48873,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47313
48873
|
{
|
|
47314
48874
|
palette,
|
|
47315
48875
|
isDark,
|
|
48876
|
+
// Export-only: forward the contain-fit request from mapExportDimensions so a
|
|
48877
|
+
// clamped/floored (off-aspect) export canvas letterboxes instead of
|
|
48878
|
+
// stretch-distorting. The in-app preview pane passes no exportDims → unset →
|
|
48879
|
+
// keeps the global stretch-fill.
|
|
48880
|
+
preferContain: exportDims?.preferContain ?? false,
|
|
47316
48881
|
...activeGroupOverride !== void 0 && {
|
|
47317
48882
|
activeGroup: activeGroupOverride
|
|
47318
48883
|
}
|
|
@@ -47326,6 +48891,11 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47326
48891
|
const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
|
|
47327
48892
|
const drawRegion = (g, r, strokeWidth) => {
|
|
47328
48893
|
const p = g.append("path").attr("d", r.d).attr("fill", r.fill).attr("stroke", r.stroke).attr("stroke-width", strokeWidth);
|
|
48894
|
+
if (r.label) p.attr("data-region-name", r.label);
|
|
48895
|
+
if (r.id && r.id !== "lake") p.attr("data-iso", r.id);
|
|
48896
|
+
if (r.labelX !== void 0 && r.labelY !== void 0) {
|
|
48897
|
+
p.attr("data-label-x", r.labelX).attr("data-label-y", r.labelY);
|
|
48898
|
+
}
|
|
47329
48899
|
if (r.layer !== "base") {
|
|
47330
48900
|
p.classed("dgmo-map-region", true).attr("data-region", r.id);
|
|
47331
48901
|
if (r.value !== void 0) p.attr("data-value", r.value);
|
|
@@ -47355,28 +48925,112 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47355
48925
|
const landClip = defs.append("clipPath").attr("id", landClipId);
|
|
47356
48926
|
for (const r of layout.regions)
|
|
47357
48927
|
if (r.id !== "lake") landClip.append("path").attr("d", r.d);
|
|
47358
|
-
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");
|
|
48928
|
+
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");
|
|
47359
48929
|
for (let y = h.spacing; y < height; y += h.spacing) {
|
|
47360
48930
|
gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
|
|
47361
48931
|
}
|
|
47362
48932
|
}
|
|
48933
|
+
if (layout.coastlineStyle) {
|
|
48934
|
+
const cs = layout.coastlineStyle;
|
|
48935
|
+
const maskId = "dgmo-map-water-mask";
|
|
48936
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
48937
|
+
mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
|
|
48938
|
+
const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
|
|
48939
|
+
const lakeD = layout.regions.filter((r) => r.id === "lake").map((r) => r.d).join(" ");
|
|
48940
|
+
if (landD) mask.append("path").attr("d", landD).attr("fill", "black");
|
|
48941
|
+
if (lakeD) mask.append("path").attr("d", lakeD).attr("fill", "white");
|
|
48942
|
+
if (layout.insets.length) {
|
|
48943
|
+
const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
|
|
48944
|
+
for (const box of layout.insets) {
|
|
48945
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48946
|
+
mask.append("path").attr("d", d).attr("fill", "black").attr("stroke", "black").attr("stroke-width", 2 * reach).attr("stroke-linejoin", "round");
|
|
48947
|
+
}
|
|
48948
|
+
}
|
|
48949
|
+
const gWater = svg.append("g").attr("class", "dgmo-map-water-lines").attr("fill", "none").style("pointer-events", "none").attr("mask", `url(#${maskId})`);
|
|
48950
|
+
appendWaterLines(
|
|
48951
|
+
gWater,
|
|
48952
|
+
// Pass the canvas frame so edges collinear with it (the antimeridian on a
|
|
48953
|
+
// world map, regional clipExtent cuts) don't get ringed as fake coast —
|
|
48954
|
+
// land runs cleanly to the render-area edge.
|
|
48955
|
+
coastlineOuterRings(layout.regions, cs.minExtent, {
|
|
48956
|
+
w: width,
|
|
48957
|
+
h: height
|
|
48958
|
+
}),
|
|
48959
|
+
cs,
|
|
48960
|
+
layout.background
|
|
48961
|
+
);
|
|
48962
|
+
const byStroke = /* @__PURE__ */ new Map();
|
|
48963
|
+
for (const r of layout.regions) {
|
|
48964
|
+
const arr = byStroke.get(r.stroke);
|
|
48965
|
+
if (arr) arr.push(r.d);
|
|
48966
|
+
else byStroke.set(r.stroke, [r.d]);
|
|
48967
|
+
}
|
|
48968
|
+
for (const [stroke2, ds] of byStroke)
|
|
48969
|
+
gWater.append("path").attr("d", ds.join(" ")).attr("stroke", stroke2).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
48970
|
+
}
|
|
47363
48971
|
if (layout.rivers.length) {
|
|
47364
|
-
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
48972
|
+
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none").style("pointer-events", "none");
|
|
47365
48973
|
for (const r of layout.rivers) {
|
|
47366
48974
|
gRivers.append("path").attr("d", r.d).attr("stroke", r.color).attr("stroke-width", r.width).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
47367
48975
|
}
|
|
47368
48976
|
}
|
|
47369
48977
|
if (layout.insets.length) {
|
|
47370
48978
|
const insetG = svg.append("g").attr("class", "dgmo-map-insets");
|
|
47371
|
-
|
|
48979
|
+
layout.insets.forEach((box, bi) => {
|
|
47372
48980
|
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
47373
48981
|
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");
|
|
47374
|
-
|
|
48982
|
+
if (box.contextLand) {
|
|
48983
|
+
const clipId = `dgmo-map-inset-clip-${bi}`;
|
|
48984
|
+
defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
|
|
48985
|
+
insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
|
|
48986
|
+
}
|
|
48987
|
+
});
|
|
47375
48988
|
for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
|
|
47376
|
-
|
|
48989
|
+
if (layout.coastlineStyle) {
|
|
48990
|
+
const cs = layout.coastlineStyle;
|
|
48991
|
+
const maskId = "dgmo-map-inset-water-mask";
|
|
48992
|
+
const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
|
48993
|
+
for (const box of layout.insets) {
|
|
48994
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
48995
|
+
mask.append("path").attr("d", d).attr("fill", "white");
|
|
48996
|
+
}
|
|
48997
|
+
layout.insets.forEach((box, bi) => {
|
|
48998
|
+
if (box.contextLand)
|
|
48999
|
+
mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
|
|
49000
|
+
});
|
|
49001
|
+
for (const r of layout.insetRegions)
|
|
49002
|
+
if (r.id !== "lake")
|
|
49003
|
+
mask.append("path").attr("d", r.d).attr("fill", "black");
|
|
49004
|
+
for (const r of layout.insetRegions)
|
|
49005
|
+
if (r.id === "lake")
|
|
49006
|
+
mask.append("path").attr("d", r.d).attr("fill", "white");
|
|
49007
|
+
const clipId = "dgmo-map-inset-water-clip";
|
|
49008
|
+
const clip = defs.append("clipPath").attr("id", clipId);
|
|
49009
|
+
for (const box of layout.insets) {
|
|
49010
|
+
const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
|
|
49011
|
+
clip.append("path").attr("d", d);
|
|
49012
|
+
}
|
|
49013
|
+
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})`);
|
|
49014
|
+
appendWaterLines(
|
|
49015
|
+
gInsetWater,
|
|
49016
|
+
coastlineOuterRings(layout.insetRegions, cs.minExtent),
|
|
49017
|
+
cs,
|
|
49018
|
+
layout.background
|
|
49019
|
+
);
|
|
49020
|
+
for (const r of layout.insetRegions)
|
|
49021
|
+
gInsetWater.append("path").attr("d", r.d).attr("stroke", r.stroke).attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
|
|
49022
|
+
}
|
|
49023
|
+
}
|
|
49024
|
+
const wireSync = (sel, lineNumber) => {
|
|
49025
|
+
if (lineNumber < 1) return;
|
|
49026
|
+
sel.attr("data-line-number", lineNumber);
|
|
49027
|
+
if (onClickItem)
|
|
49028
|
+
sel.style("cursor", "pointer").on("click", () => onClickItem(lineNumber));
|
|
49029
|
+
};
|
|
47377
49030
|
const gLegs = svg.append("g").attr("class", "dgmo-map-legs").attr("fill", "none");
|
|
47378
49031
|
layout.legs.forEach((leg, i) => {
|
|
47379
49032
|
const p = gLegs.append("path").attr("d", leg.d).attr("stroke", leg.color).attr("stroke-width", leg.width).attr("stroke-linecap", "round");
|
|
49033
|
+
wireSync(p, leg.lineNumber);
|
|
47380
49034
|
if (leg.arrow) {
|
|
47381
49035
|
const id = `dgmo-map-arrow-${i}`;
|
|
47382
49036
|
const s = arrowSize(leg.width);
|
|
@@ -47384,25 +49038,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47384
49038
|
p.attr("marker-end", `url(#${id})`);
|
|
47385
49039
|
}
|
|
47386
49040
|
if (leg.label !== void 0 && leg.labelX !== void 0) {
|
|
47387
|
-
emitText(
|
|
49041
|
+
const lt = emitText(
|
|
47388
49042
|
gLegs,
|
|
47389
49043
|
leg.labelX,
|
|
47390
49044
|
leg.labelY ?? 0,
|
|
47391
49045
|
leg.label,
|
|
47392
49046
|
"middle",
|
|
47393
|
-
palette.textMuted,
|
|
47394
|
-
haloColor,
|
|
47395
|
-
true,
|
|
49047
|
+
leg.labelColor ?? palette.textMuted,
|
|
49048
|
+
leg.labelHaloColor ?? haloColor,
|
|
49049
|
+
leg.labelHalo ?? true,
|
|
47396
49050
|
LABEL_FONT - 1
|
|
47397
49051
|
);
|
|
49052
|
+
wireSync(lt, leg.lineNumber);
|
|
47398
49053
|
}
|
|
47399
49054
|
});
|
|
49055
|
+
const gSpider = svg.append("g").attr("class", "dgmo-map-spider");
|
|
49056
|
+
for (const cl of layout.clusters) {
|
|
49057
|
+
if (!exportDims) {
|
|
49058
|
+
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");
|
|
49059
|
+
}
|
|
49060
|
+
for (const leg of cl.legs) {
|
|
49061
|
+
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");
|
|
49062
|
+
}
|
|
49063
|
+
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");
|
|
49064
|
+
}
|
|
47400
49065
|
const gPois = svg.append("g").attr("class", "dgmo-map-pois");
|
|
47401
49066
|
for (const poi of layout.pois) {
|
|
47402
49067
|
if (poi.isOrigin) {
|
|
47403
49068
|
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);
|
|
47404
49069
|
}
|
|
47405
49070
|
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);
|
|
49071
|
+
if (poi.clusterId !== void 0)
|
|
49072
|
+
c.attr("data-cluster-member", poi.clusterId);
|
|
47406
49073
|
if (poi.tags) {
|
|
47407
49074
|
for (const [group, value] of Object.entries(poi.tags)) {
|
|
47408
49075
|
c.attr(`data-tag-${group.toLowerCase()}`, value.toLowerCase());
|
|
@@ -47430,12 +49097,32 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47430
49097
|
}
|
|
47431
49098
|
const gLabels = svg.append("g").attr("class", "dgmo-map-labels");
|
|
47432
49099
|
for (const lab of layout.labels) {
|
|
49100
|
+
if (lab.hidden) {
|
|
49101
|
+
if (exportDims) continue;
|
|
49102
|
+
emitText(
|
|
49103
|
+
gLabels,
|
|
49104
|
+
lab.x,
|
|
49105
|
+
lab.y,
|
|
49106
|
+
lab.text,
|
|
49107
|
+
lab.anchor,
|
|
49108
|
+
lab.color,
|
|
49109
|
+
lab.haloColor,
|
|
49110
|
+
lab.halo,
|
|
49111
|
+
LABEL_FONT,
|
|
49112
|
+
lab.italic,
|
|
49113
|
+
lab.letterSpacing
|
|
49114
|
+
).attr("data-poi", lab.poiId ?? null).attr("data-poi-hidden", "").style("opacity", 0).style("pointer-events", "none");
|
|
49115
|
+
continue;
|
|
49116
|
+
}
|
|
47433
49117
|
if (lab.leader) {
|
|
47434
49118
|
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(
|
|
47435
49119
|
"stroke",
|
|
47436
49120
|
lab.leaderColor ?? mix(palette.textMuted, palette.bg, 60)
|
|
47437
49121
|
).attr("stroke-width", lab.leaderColor ? 1 : 0.75);
|
|
47438
49122
|
if (lab.poiId !== void 0) line12.attr("data-poi", lab.poiId);
|
|
49123
|
+
if (lab.clusterMember !== void 0)
|
|
49124
|
+
line12.attr("data-cluster-member", lab.clusterMember);
|
|
49125
|
+
wireSync(line12, lab.lineNumber);
|
|
47439
49126
|
}
|
|
47440
49127
|
const t = emitText(
|
|
47441
49128
|
gLabels,
|
|
@@ -47446,11 +49133,38 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47446
49133
|
lab.color,
|
|
47447
49134
|
lab.haloColor,
|
|
47448
49135
|
lab.halo,
|
|
47449
|
-
LABEL_FONT
|
|
49136
|
+
LABEL_FONT,
|
|
49137
|
+
lab.italic,
|
|
49138
|
+
lab.letterSpacing,
|
|
49139
|
+
lab.lines
|
|
47450
49140
|
);
|
|
47451
49141
|
if (lab.poiId !== void 0) {
|
|
47452
49142
|
t.attr("data-poi", lab.poiId).style("cursor", "default");
|
|
47453
49143
|
}
|
|
49144
|
+
if (lab.clusterMember !== void 0) {
|
|
49145
|
+
t.attr("data-cluster-member", lab.clusterMember);
|
|
49146
|
+
}
|
|
49147
|
+
wireSync(t, lab.lineNumber);
|
|
49148
|
+
}
|
|
49149
|
+
if (!exportDims && layout.clusters.length) {
|
|
49150
|
+
const gBadge = svg.append("g").attr("class", "dgmo-map-cluster-badges");
|
|
49151
|
+
for (const cl of layout.clusters) {
|
|
49152
|
+
const g = gBadge.append("g").attr("data-cluster", cl.id).style("opacity", 0).style("pointer-events", "none");
|
|
49153
|
+
const R = 9;
|
|
49154
|
+
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);
|
|
49155
|
+
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);
|
|
49156
|
+
emitText(
|
|
49157
|
+
g,
|
|
49158
|
+
cl.cx,
|
|
49159
|
+
cl.cy + 3,
|
|
49160
|
+
String(cl.count),
|
|
49161
|
+
"middle",
|
|
49162
|
+
palette.text,
|
|
49163
|
+
palette.bg,
|
|
49164
|
+
false,
|
|
49165
|
+
LABEL_FONT
|
|
49166
|
+
);
|
|
49167
|
+
}
|
|
47454
49168
|
}
|
|
47455
49169
|
if (layout.legend) {
|
|
47456
49170
|
const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
|
|
@@ -47487,7 +49201,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47487
49201
|
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);
|
|
47488
49202
|
}
|
|
47489
49203
|
if (layout.subtitle) {
|
|
47490
|
-
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);
|
|
49204
|
+
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);
|
|
47491
49205
|
}
|
|
47492
49206
|
if (layout.caption) {
|
|
47493
49207
|
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);
|
|
@@ -47496,10 +49210,21 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47496
49210
|
function renderMapForExport(container, resolved, data, palette, isDark, exportDims) {
|
|
47497
49211
|
renderMap(container, resolved, data, palette, isDark, void 0, exportDims);
|
|
47498
49212
|
}
|
|
47499
|
-
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize) {
|
|
47500
|
-
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color)
|
|
49213
|
+
function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic, letterSpacing, lines) {
|
|
49214
|
+
const t = g.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", fontSize).attr("fill", color);
|
|
49215
|
+
if (lines && lines.length > 1) {
|
|
49216
|
+
const lineHeight = fontSize + 2;
|
|
49217
|
+
const startDy = -((lines.length - 1) / 2) * lineHeight;
|
|
49218
|
+
lines.forEach((ln, i) => {
|
|
49219
|
+
t.append("tspan").attr("x", x).attr("dy", i === 0 ? startDy : lineHeight).text(ln);
|
|
49220
|
+
});
|
|
49221
|
+
} else {
|
|
49222
|
+
t.text(text);
|
|
49223
|
+
}
|
|
49224
|
+
if (italic) t.attr("font-style", "italic");
|
|
49225
|
+
if (letterSpacing) t.attr("letter-spacing", letterSpacing);
|
|
47501
49226
|
if (withHalo) {
|
|
47502
|
-
t.attr("paint-order", "stroke fill").attr("stroke", halo).attr("stroke-width",
|
|
49227
|
+
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);
|
|
47503
49228
|
}
|
|
47504
49229
|
return t;
|
|
47505
49230
|
}
|
|
@@ -47517,6 +49242,56 @@ var init_renderer16 = __esm({
|
|
|
47517
49242
|
}
|
|
47518
49243
|
});
|
|
47519
49244
|
|
|
49245
|
+
// src/map/dimensions.ts
|
|
49246
|
+
var dimensions_exports = {};
|
|
49247
|
+
__export(dimensions_exports, {
|
|
49248
|
+
mapContentAspect: () => mapContentAspect,
|
|
49249
|
+
mapExportDimensions: () => mapExportDimensions
|
|
49250
|
+
});
|
|
49251
|
+
function mapContentAspect(resolved, data, ref = REF) {
|
|
49252
|
+
const { projection, fitTarget } = buildMapProjection(resolved, data);
|
|
49253
|
+
projection.fitSize([ref, ref], fitTarget);
|
|
49254
|
+
const b = (0, import_d3_geo3.geoPath)(projection).bounds(fitTarget);
|
|
49255
|
+
const w = b[1][0] - b[0][0];
|
|
49256
|
+
const h = b[1][1] - b[0][1];
|
|
49257
|
+
const aspect = w / h;
|
|
49258
|
+
return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
|
|
49259
|
+
}
|
|
49260
|
+
function mapExportDimensions(resolved, data, baseWidth = 1200) {
|
|
49261
|
+
const raw = mapContentAspect(resolved, data);
|
|
49262
|
+
const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
|
|
49263
|
+
const width = baseWidth;
|
|
49264
|
+
let height = Math.round(width / clamped);
|
|
49265
|
+
let chromeReserve = 0;
|
|
49266
|
+
if (resolved.title && resolved.pois.length > 0) {
|
|
49267
|
+
const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
|
|
49268
|
+
chromeReserve += Math.max(FIT_PAD2, bannerBottom + TITLE_GAP) - FIT_PAD2;
|
|
49269
|
+
}
|
|
49270
|
+
let floored = false;
|
|
49271
|
+
if (height - chromeReserve < MIN_MAP_BAND) {
|
|
49272
|
+
height = Math.round(chromeReserve + MIN_MAP_BAND);
|
|
49273
|
+
floored = true;
|
|
49274
|
+
}
|
|
49275
|
+
const preferContain = clamped !== raw || floored;
|
|
49276
|
+
return { width, height, preferContain };
|
|
49277
|
+
}
|
|
49278
|
+
var import_d3_geo3, FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
|
|
49279
|
+
var init_dimensions = __esm({
|
|
49280
|
+
"src/map/dimensions.ts"() {
|
|
49281
|
+
"use strict";
|
|
49282
|
+
import_d3_geo3 = require("d3-geo");
|
|
49283
|
+
init_title_constants();
|
|
49284
|
+
init_layout15();
|
|
49285
|
+
FIT_PAD2 = 24;
|
|
49286
|
+
TITLE_GAP = 16;
|
|
49287
|
+
ASPECT_MAX = 3;
|
|
49288
|
+
ASPECT_MIN = 0.9;
|
|
49289
|
+
MIN_MAP_BAND = 200;
|
|
49290
|
+
FALLBACK_ASPECT = 1.5;
|
|
49291
|
+
REF = 1e3;
|
|
49292
|
+
}
|
|
49293
|
+
});
|
|
49294
|
+
|
|
47520
49295
|
// src/map/load-data.ts
|
|
47521
49296
|
var load_data_exports = {};
|
|
47522
49297
|
__export(load_data_exports, {
|
|
@@ -47575,12 +49350,17 @@ function loadMapData() {
|
|
|
47575
49350
|
mountainRanges,
|
|
47576
49351
|
naLand,
|
|
47577
49352
|
naLakes,
|
|
49353
|
+
waterBodies,
|
|
47578
49354
|
gazetteer
|
|
47579
49355
|
] = await Promise.all([
|
|
49356
|
+
// worldCoarse (110m) is LOAD-BEARING but NOT a render source: the world
|
|
49357
|
+
// basemap renders from worldDetail (50m) at all scales (resolver pins
|
|
49358
|
+
// basemaps.world = 'detail'). Coarse stays as the authoritative region
|
|
49359
|
+
// name index + dominant-landmass bbox source in resolver.ts. Do not drop it.
|
|
47580
49360
|
readJson(nb, dir, FILES.worldCoarse),
|
|
47581
49361
|
readJson(nb, dir, FILES.worldDetail),
|
|
47582
49362
|
readJson(nb, dir, FILES.usStates),
|
|
47583
|
-
// Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
|
|
49363
|
+
// Lakes/rivers/mountain/NA/water assets are optional — older bundles may predate them.
|
|
47584
49364
|
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
47585
49365
|
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
47586
49366
|
readJson(nb, dir, FILES.mountainRanges).catch(
|
|
@@ -47588,6 +49368,7 @@ function loadMapData() {
|
|
|
47588
49368
|
),
|
|
47589
49369
|
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
47590
49370
|
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
49371
|
+
readJson(nb, dir, FILES.waterBodies).catch(() => void 0),
|
|
47591
49372
|
readJson(nb, dir, FILES.gazetteer)
|
|
47592
49373
|
]);
|
|
47593
49374
|
return validate({
|
|
@@ -47599,7 +49380,8 @@ function loadMapData() {
|
|
|
47599
49380
|
...rivers && { rivers },
|
|
47600
49381
|
...mountainRanges && { mountainRanges },
|
|
47601
49382
|
...naLand && { naLand },
|
|
47602
|
-
...naLakes && { naLakes }
|
|
49383
|
+
...naLakes && { naLakes },
|
|
49384
|
+
...waterBodies && { waterBodies }
|
|
47603
49385
|
});
|
|
47604
49386
|
})().catch((e) => {
|
|
47605
49387
|
cache = void 0;
|
|
@@ -47621,6 +49403,7 @@ var init_load_data = __esm({
|
|
|
47621
49403
|
mountainRanges: "mountain-ranges.json",
|
|
47622
49404
|
naLand: "na-land.json",
|
|
47623
49405
|
naLakes: "na-lakes.json",
|
|
49406
|
+
waterBodies: "water-bodies.json",
|
|
47624
49407
|
gazetteer: "gazetteer.json"
|
|
47625
49408
|
};
|
|
47626
49409
|
CANDIDATE_DIRS = [
|
|
@@ -49633,8 +51416,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
49633
51416
|
const lines = splitParticipantLabel(p.label, LABEL_MAX_CHARS);
|
|
49634
51417
|
if (lines.length === 0) continue;
|
|
49635
51418
|
const widest = Math.max(...lines.map((l) => l.length));
|
|
49636
|
-
const
|
|
49637
|
-
uniformBoxWidth = Math.max(uniformBoxWidth,
|
|
51419
|
+
const labelWidth2 = widest * LABEL_CHAR_WIDTH + 10;
|
|
51420
|
+
uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
|
|
49638
51421
|
}
|
|
49639
51422
|
uniformBoxWidth = Math.min(MAX_BOX_WIDTH, uniformBoxWidth);
|
|
49640
51423
|
const effectiveGap = Math.max(PARTICIPANT_GAP, uniformBoxWidth + 30);
|
|
@@ -52329,15 +54112,15 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
52329
54112
|
textColor,
|
|
52330
54113
|
onClickItem
|
|
52331
54114
|
);
|
|
52332
|
-
const
|
|
52333
|
-
for (const node of nodes)
|
|
54115
|
+
const neighbors2 = /* @__PURE__ */ new Map();
|
|
54116
|
+
for (const node of nodes) neighbors2.set(node, /* @__PURE__ */ new Set());
|
|
52334
54117
|
for (const link of links) {
|
|
52335
|
-
|
|
52336
|
-
|
|
54118
|
+
neighbors2.get(link.source).add(link.target);
|
|
54119
|
+
neighbors2.get(link.target).add(link.source);
|
|
52337
54120
|
}
|
|
52338
54121
|
const FADE_OPACITY3 = 0.1;
|
|
52339
54122
|
function handleMouseEnter(hovered) {
|
|
52340
|
-
const connected =
|
|
54123
|
+
const connected = neighbors2.get(hovered);
|
|
52341
54124
|
g.selectAll(".arc-link").each(function() {
|
|
52342
54125
|
const el = d3Selection23.select(this);
|
|
52343
54126
|
const src = el.attr("data-source");
|
|
@@ -53245,10 +55028,12 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
|
|
|
53245
55028
|
const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
|
|
53246
55029
|
const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
|
|
53247
55030
|
const innerWidth = width - margin.left - margin.right;
|
|
53248
|
-
const
|
|
53249
|
-
const rowH = Math.min(ctx.structural(28),
|
|
55031
|
+
const availInnerHeight = height - margin.top - margin.bottom;
|
|
55032
|
+
const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
|
|
55033
|
+
const innerHeight = rowH * sorted.length;
|
|
55034
|
+
const usedHeight = margin.top + innerHeight + margin.bottom;
|
|
53250
55035
|
const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
53251
|
-
const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height",
|
|
55036
|
+
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);
|
|
53252
55037
|
if (ctx.isBelowFloor) {
|
|
53253
55038
|
svg.attr("width", "100%");
|
|
53254
55039
|
}
|
|
@@ -54272,7 +56057,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54272
56057
|
8,
|
|
54273
56058
|
Math.floor(OVERLAP_WRAP_TARGET_W / OVERLAP_CH_W)
|
|
54274
56059
|
);
|
|
54275
|
-
function
|
|
56060
|
+
function wrapLabel3(text, maxChars) {
|
|
54276
56061
|
const words = text.split(/\s+/).filter(Boolean);
|
|
54277
56062
|
const lines = [];
|
|
54278
56063
|
let cur = "";
|
|
@@ -54318,7 +56103,7 @@ function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims
|
|
|
54318
56103
|
if (!ov.label) continue;
|
|
54319
56104
|
const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
|
|
54320
56105
|
if (idxs.some((idx) => idx < 0)) continue;
|
|
54321
|
-
const lines =
|
|
56106
|
+
const lines = wrapLabel3(ov.label, MAX_WRAP_CHARS);
|
|
54322
56107
|
wrappedOverlapLabels.set(ov, lines);
|
|
54323
56108
|
const dir = predictOverlapDirRaw(idxs);
|
|
54324
56109
|
const longest = lines.reduce((m, l) => Math.max(m, l.length), 0);
|
|
@@ -55756,6 +57541,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55756
57541
|
const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
|
|
55757
57542
|
const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
|
|
55758
57543
|
const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
|
|
57544
|
+
const { mapExportDimensions: mapExportDimensions2 } = await Promise.resolve().then(() => (init_dimensions(), dimensions_exports));
|
|
55759
57545
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
55760
57546
|
const mapParsed = parseMap2(content);
|
|
55761
57547
|
let mapData = options?.mapData;
|
|
@@ -55768,14 +57554,15 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55768
57554
|
}
|
|
55769
57555
|
}
|
|
55770
57556
|
const mapResolved = resolveMap2(mapParsed, mapData);
|
|
55771
|
-
const
|
|
57557
|
+
const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
|
|
57558
|
+
const container2 = createExportContainer(dims2.width, dims2.height);
|
|
55772
57559
|
renderMapForExport2(
|
|
55773
57560
|
container2,
|
|
55774
57561
|
mapResolved,
|
|
55775
57562
|
mapData,
|
|
55776
57563
|
effectivePalette2,
|
|
55777
57564
|
theme === "dark",
|
|
55778
|
-
|
|
57565
|
+
dims2
|
|
55779
57566
|
);
|
|
55780
57567
|
return finalizeSvgExport(container2, theme, effectivePalette2);
|
|
55781
57568
|
}
|
|
@@ -56639,7 +58426,8 @@ async function render(content, options) {
|
|
|
56639
58426
|
...options?.c4Container !== void 0 && {
|
|
56640
58427
|
c4Container: options.c4Container
|
|
56641
58428
|
},
|
|
56642
|
-
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup }
|
|
58429
|
+
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
|
|
58430
|
+
...options?.mapData !== void 0 && { mapData: options.mapData }
|
|
56643
58431
|
});
|
|
56644
58432
|
if (chartType === "map") {
|
|
56645
58433
|
try {
|
|
@@ -56650,7 +58438,7 @@ async function render(content, options) {
|
|
|
56650
58438
|
Promise.resolve().then(() => (init_load_data(), load_data_exports))
|
|
56651
58439
|
]
|
|
56652
58440
|
);
|
|
56653
|
-
const data = await loadMapData2();
|
|
58441
|
+
const data = options?.mapData ?? await loadMapData2();
|
|
56654
58442
|
diagnostics = [...resolveMap2(parseMap2(content), data).diagnostics];
|
|
56655
58443
|
} catch {
|
|
56656
58444
|
}
|