@diagrammo/dgmo 0.21.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +556 -195
- package/dist/advanced.js +555 -195
- package/dist/auto.cjs +322 -196
- package/dist/auto.js +113 -113
- package/dist/auto.mjs +322 -196
- package/dist/cli.cjs +156 -156
- package/dist/editor.cjs +1 -0
- package/dist/editor.js +1 -0
- package/dist/highlight.cjs +1 -0
- package/dist/highlight.js +1 -0
- package/dist/index.cjs +320 -195
- package/dist/index.js +320 -195
- package/dist/internal.cjs +556 -195
- package/dist/internal.js +555 -195
- package/dist/map-data/PROVENANCE.json +1 -1
- package/dist/map-data/mountain-ranges.json +1 -0
- package/docs/language-reference.md +27 -25
- package/gallery/fixtures/map-direct-color.dgmo +10 -0
- package/package.json +1 -1
- package/src/advanced.ts +14 -0
- package/src/completion.ts +1 -0
- package/src/d3.ts +15 -9
- package/src/editor/keywords.ts +1 -0
- package/src/map/data/PROVENANCE.json +1 -1
- package/src/map/data/mountain-ranges.json +1 -0
- package/src/map/geo-query.ts +277 -0
- package/src/map/geo.ts +258 -1
- package/src/map/invert.ts +111 -0
- package/src/map/layout.ts +233 -113
- package/src/map/load-data.ts +7 -1
- package/src/map/parser.ts +22 -2
- package/src/map/renderer.ts +44 -0
- package/src/map/resolved-types.ts +8 -0
- package/src/map/resolver.ts +40 -19
- package/src/map/types.ts +18 -0
- package/dist/advanced.d.cts +0 -5331
- package/dist/advanced.d.ts +0 -5331
- package/dist/auto.d.cts +0 -39
- package/dist/auto.d.ts +0 -39
- package/dist/index.d.cts +0 -336
- package/dist/index.d.ts +0 -336
- package/dist/internal.d.cts +0 -5331
- package/dist/internal.d.ts +0 -5331
package/dist/advanced.cjs
CHANGED
|
@@ -15924,10 +15924,13 @@ function parseMap(content) {
|
|
|
15924
15924
|
);
|
|
15925
15925
|
d.projection = value;
|
|
15926
15926
|
break;
|
|
15927
|
-
case "region-metric":
|
|
15927
|
+
case "region-metric": {
|
|
15928
15928
|
dup(d.regionMetric);
|
|
15929
|
-
|
|
15929
|
+
const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
|
|
15930
|
+
d.regionMetric = rmLabel;
|
|
15931
|
+
if (rmColor) d.regionMetricColor = rmColor;
|
|
15930
15932
|
break;
|
|
15933
|
+
}
|
|
15931
15934
|
case "poi-metric":
|
|
15932
15935
|
dup(d.poiMetric);
|
|
15933
15936
|
d.poiMetric = value;
|
|
@@ -15976,6 +15979,12 @@ function parseMap(content) {
|
|
|
15976
15979
|
case "no-legend":
|
|
15977
15980
|
d.noLegend = true;
|
|
15978
15981
|
break;
|
|
15982
|
+
case "no-insets":
|
|
15983
|
+
d.noInsets = true;
|
|
15984
|
+
break;
|
|
15985
|
+
case "relief":
|
|
15986
|
+
d.relief = true;
|
|
15987
|
+
break;
|
|
15979
15988
|
case "muted":
|
|
15980
15989
|
case "natural":
|
|
15981
15990
|
if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
|
|
@@ -16088,6 +16097,7 @@ function parseMap(content) {
|
|
|
16088
16097
|
};
|
|
16089
16098
|
if (regionScope !== void 0) region.scope = regionScope;
|
|
16090
16099
|
if (valueNum !== void 0) region.value = valueNum;
|
|
16100
|
+
if (split.color) region.color = split.color;
|
|
16091
16101
|
regions.push(region);
|
|
16092
16102
|
}
|
|
16093
16103
|
function handlePoi(rest, line12, indent) {
|
|
@@ -16112,6 +16122,7 @@ function parseMap(content) {
|
|
|
16112
16122
|
const poi = { pos, tags, meta, lineNumber: line12 };
|
|
16113
16123
|
if (split.alias) poi.alias = split.alias;
|
|
16114
16124
|
if (label !== void 0) poi.label = label;
|
|
16125
|
+
if (split.color) poi.color = split.color;
|
|
16115
16126
|
pois.push(poi);
|
|
16116
16127
|
open.poi = { poi, indent };
|
|
16117
16128
|
}
|
|
@@ -16316,6 +16327,8 @@ var init_parser12 = __esm({
|
|
|
16316
16327
|
"default-state",
|
|
16317
16328
|
"active-tag",
|
|
16318
16329
|
"no-legend",
|
|
16330
|
+
"no-insets",
|
|
16331
|
+
"relief",
|
|
16319
16332
|
"subtitle",
|
|
16320
16333
|
"caption"
|
|
16321
16334
|
]);
|
|
@@ -45842,6 +45855,84 @@ function featureIndex(topo) {
|
|
|
45842
45855
|
}
|
|
45843
45856
|
return idx;
|
|
45844
45857
|
}
|
|
45858
|
+
function decodeFeatures(topo) {
|
|
45859
|
+
return geomObject(topo).geometries.map((g) => {
|
|
45860
|
+
const f = (0, import_topojson_client.feature)(topo, g);
|
|
45861
|
+
return {
|
|
45862
|
+
type: "Feature",
|
|
45863
|
+
id: g.id,
|
|
45864
|
+
properties: g.properties,
|
|
45865
|
+
geometry: f.geometry
|
|
45866
|
+
};
|
|
45867
|
+
});
|
|
45868
|
+
}
|
|
45869
|
+
function pointInRing(lon, lat, ring) {
|
|
45870
|
+
let inside = false;
|
|
45871
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
45872
|
+
const xi = ring[i][0];
|
|
45873
|
+
const yi = ring[i][1];
|
|
45874
|
+
const xj = ring[j][0];
|
|
45875
|
+
const yj = ring[j][1];
|
|
45876
|
+
const intersect = yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi;
|
|
45877
|
+
if (intersect) inside = !inside;
|
|
45878
|
+
}
|
|
45879
|
+
return inside;
|
|
45880
|
+
}
|
|
45881
|
+
function pointOnRingEdge(lon, lat, ring) {
|
|
45882
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
45883
|
+
const xi = ring[i][0];
|
|
45884
|
+
const yi = ring[i][1];
|
|
45885
|
+
const xj = ring[j][0];
|
|
45886
|
+
const yj = ring[j][1];
|
|
45887
|
+
if (lon < Math.min(xi, xj) - EDGE_EPS || lon > Math.max(xi, xj) + EDGE_EPS)
|
|
45888
|
+
continue;
|
|
45889
|
+
if (lat < Math.min(yi, yj) - EDGE_EPS || lat > Math.max(yi, yj) + EDGE_EPS)
|
|
45890
|
+
continue;
|
|
45891
|
+
const cross = (xj - xi) * (lat - yi) - (yj - yi) * (lon - xi);
|
|
45892
|
+
if (Math.abs(cross) <= EDGE_EPS) return true;
|
|
45893
|
+
}
|
|
45894
|
+
return false;
|
|
45895
|
+
}
|
|
45896
|
+
function pointInGeometry(geometry, lon, lat) {
|
|
45897
|
+
const g = geometry;
|
|
45898
|
+
if (!g) return false;
|
|
45899
|
+
const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
|
|
45900
|
+
for (const rings of polys) {
|
|
45901
|
+
if (!rings.length) continue;
|
|
45902
|
+
if (pointOnRingEdge(lon, lat, rings[0])) return true;
|
|
45903
|
+
if (!pointInRing(lon, lat, rings[0])) continue;
|
|
45904
|
+
let inHole = false;
|
|
45905
|
+
for (let h = 1; h < rings.length; h++) {
|
|
45906
|
+
if (pointInRing(lon, lat, rings[h]) && !pointOnRingEdge(lon, lat, rings[h])) {
|
|
45907
|
+
inHole = true;
|
|
45908
|
+
break;
|
|
45909
|
+
}
|
|
45910
|
+
}
|
|
45911
|
+
if (!inHole) return true;
|
|
45912
|
+
}
|
|
45913
|
+
return false;
|
|
45914
|
+
}
|
|
45915
|
+
function regionAt(lonLat, countries, states) {
|
|
45916
|
+
const lon = lonLat[0];
|
|
45917
|
+
const lat = lonLat[1];
|
|
45918
|
+
let country = null;
|
|
45919
|
+
for (const f of countries) {
|
|
45920
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
45921
|
+
country = { iso: f.id, name: f.properties.name };
|
|
45922
|
+
break;
|
|
45923
|
+
}
|
|
45924
|
+
}
|
|
45925
|
+
let state = null;
|
|
45926
|
+
if (country?.iso === "US" && states) {
|
|
45927
|
+
for (const f of states) {
|
|
45928
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
45929
|
+
state = { iso: f.id, name: f.properties.name };
|
|
45930
|
+
break;
|
|
45931
|
+
}
|
|
45932
|
+
}
|
|
45933
|
+
}
|
|
45934
|
+
return { country, state };
|
|
45935
|
+
}
|
|
45845
45936
|
function featureBbox(topo, geomId) {
|
|
45846
45937
|
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45847
45938
|
if (!geom) return null;
|
|
@@ -45853,6 +45944,74 @@ function featureBbox(topo, geomId) {
|
|
|
45853
45944
|
[b[1][0], b[1][1]]
|
|
45854
45945
|
];
|
|
45855
45946
|
}
|
|
45947
|
+
function explodePolygons(gj) {
|
|
45948
|
+
const g = gj.geometry ?? gj;
|
|
45949
|
+
const t = g.type;
|
|
45950
|
+
const coords = g.coordinates;
|
|
45951
|
+
if (t === "Polygon") {
|
|
45952
|
+
return [
|
|
45953
|
+
{ type: "Feature", geometry: { type: "Polygon", coordinates: coords } }
|
|
45954
|
+
];
|
|
45955
|
+
}
|
|
45956
|
+
if (t === "MultiPolygon") {
|
|
45957
|
+
return coords.map((rings) => ({
|
|
45958
|
+
type: "Feature",
|
|
45959
|
+
geometry: { type: "Polygon", coordinates: rings }
|
|
45960
|
+
}));
|
|
45961
|
+
}
|
|
45962
|
+
return [];
|
|
45963
|
+
}
|
|
45964
|
+
function bboxGap(a, b) {
|
|
45965
|
+
const lonGap = Math.max(0, a[0][0] - b[1][0], b[0][0] - a[1][0]);
|
|
45966
|
+
const latGap = Math.max(0, a[0][1] - b[1][1], b[0][1] - a[1][1]);
|
|
45967
|
+
return Math.max(lonGap, latGap);
|
|
45968
|
+
}
|
|
45969
|
+
function featureBboxPrimary(topo, geomId) {
|
|
45970
|
+
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45971
|
+
if (!geom) return null;
|
|
45972
|
+
const gj = (0, import_topojson_client.feature)(topo, geom);
|
|
45973
|
+
const parts = explodePolygons(gj);
|
|
45974
|
+
if (parts.length <= 1) return featureBbox(topo, geomId);
|
|
45975
|
+
const polys = parts.map((p) => {
|
|
45976
|
+
const b = (0, import_d3_geo.geoBounds)(p);
|
|
45977
|
+
if (!b || !Number.isFinite(b[0][0])) return null;
|
|
45978
|
+
const wraps = b[1][0] < b[0][0];
|
|
45979
|
+
const bbox = [
|
|
45980
|
+
[b[0][0], b[0][1]],
|
|
45981
|
+
[b[1][0], b[1][1]]
|
|
45982
|
+
];
|
|
45983
|
+
return { bbox, area: (0, import_d3_geo.geoArea)(p), wraps };
|
|
45984
|
+
}).filter(
|
|
45985
|
+
(p) => p !== null
|
|
45986
|
+
);
|
|
45987
|
+
if (polys.length <= 1 || polys.some((p) => p.wraps))
|
|
45988
|
+
return featureBbox(topo, geomId);
|
|
45989
|
+
const maxArea = Math.max(...polys.map((p) => p.area));
|
|
45990
|
+
const anchor = polys.find((p) => p.area === maxArea);
|
|
45991
|
+
const cluster = [
|
|
45992
|
+
[anchor.bbox[0][0], anchor.bbox[0][1]],
|
|
45993
|
+
[anchor.bbox[1][0], anchor.bbox[1][1]]
|
|
45994
|
+
];
|
|
45995
|
+
const remaining = polys.filter((p) => p !== anchor);
|
|
45996
|
+
let added = true;
|
|
45997
|
+
while (added) {
|
|
45998
|
+
added = false;
|
|
45999
|
+
for (let i = remaining.length - 1; i >= 0; i--) {
|
|
46000
|
+
const p = remaining[i];
|
|
46001
|
+
const near = bboxGap(p.bbox, cluster) <= DETACH_GAP_DEG;
|
|
46002
|
+
const large = p.area >= DETACH_AREA_FRAC * maxArea;
|
|
46003
|
+
if (near || large) {
|
|
46004
|
+
cluster[0][0] = Math.min(cluster[0][0], p.bbox[0][0]);
|
|
46005
|
+
cluster[0][1] = Math.min(cluster[0][1], p.bbox[0][1]);
|
|
46006
|
+
cluster[1][0] = Math.max(cluster[1][0], p.bbox[1][0]);
|
|
46007
|
+
cluster[1][1] = Math.max(cluster[1][1], p.bbox[1][1]);
|
|
46008
|
+
remaining.splice(i, 1);
|
|
46009
|
+
added = true;
|
|
46010
|
+
}
|
|
46011
|
+
}
|
|
46012
|
+
}
|
|
46013
|
+
return cluster;
|
|
46014
|
+
}
|
|
45856
46015
|
function unionExtent(boxes, points) {
|
|
45857
46016
|
const lats = [];
|
|
45858
46017
|
const lons = [];
|
|
@@ -45891,13 +46050,16 @@ function unionLongitudes(lons) {
|
|
|
45891
46050
|
}
|
|
45892
46051
|
return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
|
|
45893
46052
|
}
|
|
45894
|
-
var import_topojson_client, import_d3_geo, fold;
|
|
46053
|
+
var import_topojson_client, import_d3_geo, fold, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
45895
46054
|
var init_geo = __esm({
|
|
45896
46055
|
"src/map/geo.ts"() {
|
|
45897
46056
|
"use strict";
|
|
45898
46057
|
import_topojson_client = require("topojson-client");
|
|
45899
46058
|
import_d3_geo = require("d3-geo");
|
|
45900
46059
|
fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
46060
|
+
EDGE_EPS = 1e-9;
|
|
46061
|
+
DETACH_GAP_DEG = 10;
|
|
46062
|
+
DETACH_AREA_FRAC = 0.25;
|
|
45901
46063
|
}
|
|
45902
46064
|
});
|
|
45903
46065
|
|
|
@@ -46001,12 +46163,12 @@ function resolveMap(parsed, data) {
|
|
|
46001
46163
|
chosen = { ...inState, layer: "us-state" };
|
|
46002
46164
|
} else {
|
|
46003
46165
|
chosen = { ...inCountry, layer: "country" };
|
|
46166
|
+
warn(
|
|
46167
|
+
r.lineNumber,
|
|
46168
|
+
`"${r.name}" is both a country and a US state \u2014 resolved as ${chosen.layer} (${chosen.id}). Pin it with an ISO code (${inState.id} / ${inCountry.id}) or name + scope ("${r.name} US" / "${r.name} ${inCountry.id}").`,
|
|
46169
|
+
"W_MAP_REGION_AMBIGUOUS"
|
|
46170
|
+
);
|
|
46004
46171
|
}
|
|
46005
|
-
warn(
|
|
46006
|
-
r.lineNumber,
|
|
46007
|
-
`"${r.name}" is both a country and a US state \u2014 resolved as ${chosen.layer} (${chosen.id}). Pin it with an ISO code (${inState.id} / ${inCountry.id}) or name + scope ("${r.name} US" / "${r.name} ${inCountry.id}").`,
|
|
46008
|
-
"W_MAP_REGION_AMBIGUOUS"
|
|
46009
|
-
);
|
|
46010
46172
|
} else if (inState) {
|
|
46011
46173
|
chosen = { ...inState, layer: "us-state" };
|
|
46012
46174
|
} else if (inCountry) {
|
|
@@ -46030,6 +46192,7 @@ function resolveMap(parsed, data) {
|
|
|
46030
46192
|
name: chosen.name,
|
|
46031
46193
|
layer: chosen.layer,
|
|
46032
46194
|
...r.value !== void 0 && { value: r.value },
|
|
46195
|
+
...r.color !== void 0 && { color: r.color },
|
|
46033
46196
|
tags: r.tags,
|
|
46034
46197
|
meta: r.meta,
|
|
46035
46198
|
lineNumber: r.lineNumber
|
|
@@ -46171,6 +46334,7 @@ function resolveMap(parsed, data) {
|
|
|
46171
46334
|
lat,
|
|
46172
46335
|
lon,
|
|
46173
46336
|
...p.label !== void 0 && { label: p.label },
|
|
46337
|
+
...p.color !== void 0 && { color: p.color },
|
|
46174
46338
|
tags: p.tags,
|
|
46175
46339
|
meta: p.meta,
|
|
46176
46340
|
lineNumber: p.lineNumber
|
|
@@ -46313,7 +46477,7 @@ function resolveMap(parsed, data) {
|
|
|
46313
46477
|
}
|
|
46314
46478
|
for (const r of regions) {
|
|
46315
46479
|
if (r.layer === "country") {
|
|
46316
|
-
const bb =
|
|
46480
|
+
const bb = featureBboxPrimary(data.worldCoarse, r.iso);
|
|
46317
46481
|
if (bb) regionBoxes.push(bb);
|
|
46318
46482
|
}
|
|
46319
46483
|
}
|
|
@@ -46327,6 +46491,7 @@ function resolveMap(parsed, data) {
|
|
|
46327
46491
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
46328
46492
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
46329
46493
|
const span = Math.max(lonSpan, latSpan);
|
|
46494
|
+
const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
|
|
46330
46495
|
const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
46331
46496
|
let projection;
|
|
46332
46497
|
const override = parsed.directives.projection;
|
|
@@ -46334,12 +46499,10 @@ function resolveMap(parsed, data) {
|
|
|
46334
46499
|
projection = override;
|
|
46335
46500
|
} else if (usDominant) {
|
|
46336
46501
|
projection = "albers-usa";
|
|
46337
|
-
} else if (span > WORLD_SPAN) {
|
|
46502
|
+
} else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
|
|
46338
46503
|
projection = "equirectangular";
|
|
46339
|
-
} else if (span < MERCATOR_MAX_SPAN) {
|
|
46340
|
-
projection = "mercator";
|
|
46341
46504
|
} else {
|
|
46342
|
-
projection = "
|
|
46505
|
+
projection = "mercator";
|
|
46343
46506
|
}
|
|
46344
46507
|
if (lonSpan >= 180) {
|
|
46345
46508
|
extent2 = [
|
|
@@ -46393,14 +46556,14 @@ function firstError(diags) {
|
|
|
46393
46556
|
const e = diags.find((d) => d.severity === "error");
|
|
46394
46557
|
return e ? formatDgmoError(e) : null;
|
|
46395
46558
|
}
|
|
46396
|
-
var WORLD_SPAN,
|
|
46559
|
+
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
|
|
46397
46560
|
var init_resolver2 = __esm({
|
|
46398
46561
|
"src/map/resolver.ts"() {
|
|
46399
46562
|
"use strict";
|
|
46400
46563
|
init_diagnostics();
|
|
46401
46564
|
init_geo();
|
|
46402
46565
|
WORLD_SPAN = 90;
|
|
46403
|
-
|
|
46566
|
+
MERCATOR_MAX_LAT = 80;
|
|
46404
46567
|
PAD_FRACTION = 0.05;
|
|
46405
46568
|
WORLD_LAT_SOUTH = -58;
|
|
46406
46569
|
WORLD_LAT_NORTH = 78;
|
|
@@ -46481,115 +46644,6 @@ var init_resolver2 = __esm({
|
|
|
46481
46644
|
}
|
|
46482
46645
|
});
|
|
46483
46646
|
|
|
46484
|
-
// src/map/load-data.ts
|
|
46485
|
-
var load_data_exports = {};
|
|
46486
|
-
__export(load_data_exports, {
|
|
46487
|
-
loadMapData: () => loadMapData
|
|
46488
|
-
});
|
|
46489
|
-
async function loadNodeBuiltins() {
|
|
46490
|
-
const [{ readFile }, { fileURLToPath }, { dirname: dirname2, resolve }] = await Promise.all([
|
|
46491
|
-
import("fs/promises"),
|
|
46492
|
-
import("url"),
|
|
46493
|
-
import("path")
|
|
46494
|
-
]);
|
|
46495
|
-
return { readFile, fileURLToPath, dirname: dirname2, resolve };
|
|
46496
|
-
}
|
|
46497
|
-
async function readJson(nb, dir, name) {
|
|
46498
|
-
return JSON.parse(await nb.readFile(nb.resolve(dir, name), "utf8"));
|
|
46499
|
-
}
|
|
46500
|
-
async function firstExistingDir(nb, baseDir) {
|
|
46501
|
-
for (const rel of CANDIDATE_DIRS) {
|
|
46502
|
-
const dir = nb.resolve(baseDir, rel);
|
|
46503
|
-
try {
|
|
46504
|
-
await nb.readFile(nb.resolve(dir, FILES.gazetteer), "utf8");
|
|
46505
|
-
return dir;
|
|
46506
|
-
} catch {
|
|
46507
|
-
}
|
|
46508
|
-
}
|
|
46509
|
-
throw new Error(
|
|
46510
|
-
`map data assets not found near ${baseDir} (looked in ${CANDIDATE_DIRS.join(", ")}). Run \`pnpm build:map-data\` and \`pnpm build\`.`
|
|
46511
|
-
);
|
|
46512
|
-
}
|
|
46513
|
-
function validate(data) {
|
|
46514
|
-
const topoOk = (t) => !!t && t.type === "Topology" && !!t.objects;
|
|
46515
|
-
if (!topoOk(data.worldCoarse) || !topoOk(data.worldDetail) || !topoOk(data.usStates) || !data.gazetteer || !Array.isArray(data.gazetteer.cities) || !data.gazetteer.byName) {
|
|
46516
|
-
throw new Error("map data assets are malformed (failed shape validation)");
|
|
46517
|
-
}
|
|
46518
|
-
return data;
|
|
46519
|
-
}
|
|
46520
|
-
function moduleBaseDir(nb) {
|
|
46521
|
-
try {
|
|
46522
|
-
const url = import_meta.url;
|
|
46523
|
-
if (url) return nb.dirname(nb.fileURLToPath(url));
|
|
46524
|
-
} catch {
|
|
46525
|
-
}
|
|
46526
|
-
if (typeof __dirname !== "undefined") return __dirname;
|
|
46527
|
-
return process.cwd();
|
|
46528
|
-
}
|
|
46529
|
-
function loadMapData() {
|
|
46530
|
-
cache ??= (async () => {
|
|
46531
|
-
const nb = await loadNodeBuiltins();
|
|
46532
|
-
const dir = await firstExistingDir(nb, moduleBaseDir(nb));
|
|
46533
|
-
const [
|
|
46534
|
-
worldCoarse,
|
|
46535
|
-
worldDetail,
|
|
46536
|
-
usStates,
|
|
46537
|
-
lakes,
|
|
46538
|
-
rivers,
|
|
46539
|
-
naLand,
|
|
46540
|
-
naLakes,
|
|
46541
|
-
gazetteer
|
|
46542
|
-
] = await Promise.all([
|
|
46543
|
-
readJson(nb, dir, FILES.worldCoarse),
|
|
46544
|
-
readJson(nb, dir, FILES.worldDetail),
|
|
46545
|
-
readJson(nb, dir, FILES.usStates),
|
|
46546
|
-
// Lakes/rivers/NA assets are optional — older bundles may predate them.
|
|
46547
|
-
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
46548
|
-
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
46549
|
-
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
46550
|
-
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
46551
|
-
readJson(nb, dir, FILES.gazetteer)
|
|
46552
|
-
]);
|
|
46553
|
-
return validate({
|
|
46554
|
-
worldCoarse,
|
|
46555
|
-
worldDetail,
|
|
46556
|
-
usStates,
|
|
46557
|
-
gazetteer,
|
|
46558
|
-
...lakes && { lakes },
|
|
46559
|
-
...rivers && { rivers },
|
|
46560
|
-
...naLand && { naLand },
|
|
46561
|
-
...naLakes && { naLakes }
|
|
46562
|
-
});
|
|
46563
|
-
})().catch((e) => {
|
|
46564
|
-
cache = void 0;
|
|
46565
|
-
throw e;
|
|
46566
|
-
});
|
|
46567
|
-
return cache;
|
|
46568
|
-
}
|
|
46569
|
-
var import_meta, FILES, CANDIDATE_DIRS, cache;
|
|
46570
|
-
var init_load_data = __esm({
|
|
46571
|
-
"src/map/load-data.ts"() {
|
|
46572
|
-
"use strict";
|
|
46573
|
-
import_meta = {};
|
|
46574
|
-
FILES = {
|
|
46575
|
-
worldCoarse: "world-coarse.json",
|
|
46576
|
-
worldDetail: "world-detail.json",
|
|
46577
|
-
usStates: "us-states.json",
|
|
46578
|
-
lakes: "lakes.json",
|
|
46579
|
-
rivers: "rivers.json",
|
|
46580
|
-
naLand: "na-land.json",
|
|
46581
|
-
naLakes: "na-lakes.json",
|
|
46582
|
-
gazetteer: "gazetteer.json"
|
|
46583
|
-
};
|
|
46584
|
-
CANDIDATE_DIRS = [
|
|
46585
|
-
"./data",
|
|
46586
|
-
"./map-data",
|
|
46587
|
-
"../map-data",
|
|
46588
|
-
"../src/map/data"
|
|
46589
|
-
];
|
|
46590
|
-
}
|
|
46591
|
-
});
|
|
46592
|
-
|
|
46593
46647
|
// src/map/layout.ts
|
|
46594
46648
|
function geomObject2(topo) {
|
|
46595
46649
|
const key = Object.keys(topo.objects)[0];
|
|
@@ -46616,18 +46670,14 @@ function projectionFor(family) {
|
|
|
46616
46670
|
return (0, import_d3_geo2.geoEquirectangular)();
|
|
46617
46671
|
}
|
|
46618
46672
|
}
|
|
46619
|
-
function mapBackgroundColor(palette, isDark = false,
|
|
46620
|
-
|
|
46621
|
-
|
|
46622
|
-
|
|
46623
|
-
|
|
46624
|
-
|
|
46625
|
-
);
|
|
46626
|
-
return mix(palette.colors.blue, palette.bg, WATER_TINT);
|
|
46673
|
+
function mapBackgroundColor(palette, isDark = false, _dataActive = false) {
|
|
46674
|
+
return mix(
|
|
46675
|
+
palette.colors.blue,
|
|
46676
|
+
palette.bg,
|
|
46677
|
+
isDark ? WATER_TINT_DARK : WATER_TINT_LIGHT
|
|
46678
|
+
);
|
|
46627
46679
|
}
|
|
46628
|
-
function mapNeutralLandColor(palette, isDark,
|
|
46629
|
-
if (dataActive)
|
|
46630
|
-
return isDark ? mix(palette.colors.gray, palette.bg, MUTED_LAND_DARK) : palette.bg;
|
|
46680
|
+
function mapNeutralLandColor(palette, isDark, _dataActive = false) {
|
|
46631
46681
|
return mix(
|
|
46632
46682
|
palette.colors.green,
|
|
46633
46683
|
palette.bg,
|
|
@@ -46659,7 +46709,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46659
46709
|
const scaleOverride = resolved.directives.scale;
|
|
46660
46710
|
const rampMin = scaleOverride ? scaleOverride.min : Math.min(...values);
|
|
46661
46711
|
const rampMax = scaleOverride ? scaleOverride.max : Math.max(...values);
|
|
46662
|
-
const rampHue = palette.colors.red;
|
|
46712
|
+
const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
|
|
46663
46713
|
const hasRamp = values.length > 0;
|
|
46664
46714
|
const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
|
|
46665
46715
|
const matchColorGroup = (v) => {
|
|
@@ -46682,6 +46732,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46682
46732
|
const mutedBasemap = resolved.directives.basemapStyle === "muted" ? true : resolved.directives.basemapStyle === "natural" ? false : activeGroup !== null;
|
|
46683
46733
|
const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
|
|
46684
46734
|
const water = mapBackgroundColor(palette, isDark, mutedBasemap);
|
|
46735
|
+
const lakeStroke = mix(regionStroke, water, 45);
|
|
46685
46736
|
const foreignFill = mix(
|
|
46686
46737
|
palette.colors.gray,
|
|
46687
46738
|
palette.bg,
|
|
@@ -46711,7 +46762,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46711
46762
|
isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT
|
|
46712
46763
|
);
|
|
46713
46764
|
};
|
|
46765
|
+
const directFill = (name) => {
|
|
46766
|
+
const hex = name ? resolveColor(name, palette) : null;
|
|
46767
|
+
if (!hex) return null;
|
|
46768
|
+
return mix(hex, palette.bg, isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT);
|
|
46769
|
+
};
|
|
46714
46770
|
const regionFill = (r) => {
|
|
46771
|
+
const direct = directFill(r.color);
|
|
46772
|
+
if (direct) return direct;
|
|
46715
46773
|
if (activeIsScore) {
|
|
46716
46774
|
return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
|
|
46717
46775
|
}
|
|
@@ -46765,6 +46823,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46765
46823
|
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46766
46824
|
let path;
|
|
46767
46825
|
let project;
|
|
46826
|
+
let stretchParams = null;
|
|
46768
46827
|
if (fitIsGlobal) {
|
|
46769
46828
|
const cb = (0, import_d3_geo2.geoPath)(projection).bounds(fitTarget);
|
|
46770
46829
|
const bx0 = cb[0][0];
|
|
@@ -46775,6 +46834,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46775
46834
|
const oy = fitBox[0][1];
|
|
46776
46835
|
const sx = cw > 0 ? (fitBox[1][0] - ox) / cw : 1;
|
|
46777
46836
|
const sy = ch > 0 ? (fitBox[1][1] - oy) / ch : 1;
|
|
46837
|
+
stretchParams = { sx, sy, ox, oy, bx0, by0 };
|
|
46778
46838
|
const stretch = (x, y) => [
|
|
46779
46839
|
ox + (x - bx0) * sx,
|
|
46780
46840
|
oy + (y - by0) * sy
|
|
@@ -46806,7 +46866,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46806
46866
|
const insets = [];
|
|
46807
46867
|
const insetRegions = [];
|
|
46808
46868
|
const insetLabelSeeds = [];
|
|
46809
|
-
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46869
|
+
if (resolved.projection === "albers-usa" && usLayer && !resolved.directives.noInsets) {
|
|
46810
46870
|
const PAD = 8;
|
|
46811
46871
|
const GAP = 12;
|
|
46812
46872
|
const yB = height - FIT_PAD;
|
|
@@ -46837,38 +46897,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46837
46897
|
}
|
|
46838
46898
|
return y;
|
|
46839
46899
|
};
|
|
46840
|
-
const
|
|
46900
|
+
const coastFloor = (x0, xr) => {
|
|
46841
46901
|
const n = 24;
|
|
46842
|
-
const pts = [];
|
|
46843
46902
|
let maxY = -Infinity;
|
|
46844
46903
|
for (let i = 0; i <= n; i++) {
|
|
46845
|
-
const
|
|
46846
|
-
|
|
46847
|
-
|
|
46848
|
-
|
|
46849
|
-
if (y > maxY) maxY = y;
|
|
46850
|
-
}
|
|
46851
|
-
}
|
|
46852
|
-
if (pts.length === 0) return () => yB - height * 0.42;
|
|
46853
|
-
let m = 0;
|
|
46854
|
-
if (pts.length >= 2) {
|
|
46855
|
-
let sx = 0, sy = 0, sxx = 0, sxy = 0;
|
|
46856
|
-
for (const [x, y] of pts) {
|
|
46857
|
-
sx += x;
|
|
46858
|
-
sy += y;
|
|
46859
|
-
sxx += x * x;
|
|
46860
|
-
sxy += x * y;
|
|
46861
|
-
}
|
|
46862
|
-
const den = pts.length * sxx - sx * sx;
|
|
46863
|
-
if (den !== 0) m = (pts.length * sxy - sx * sy) / den;
|
|
46864
|
-
}
|
|
46865
|
-
m = Math.max(-0.35, Math.min(0.35, m));
|
|
46866
|
-
let c = -Infinity;
|
|
46867
|
-
for (const [x, y] of pts) {
|
|
46868
|
-
const need = y - m * x + GAP;
|
|
46869
|
-
if (need > c) c = need;
|
|
46870
|
-
}
|
|
46871
|
-
return (x) => m * x + c;
|
|
46904
|
+
const y = at(x0 + (xr - x0) * i / n);
|
|
46905
|
+
if (y > maxY) maxY = y;
|
|
46906
|
+
}
|
|
46907
|
+
return maxY;
|
|
46872
46908
|
};
|
|
46873
46909
|
const placeInset = (iso, proj, boxX, iwReq) => {
|
|
46874
46910
|
const f = usLayer.get(iso);
|
|
@@ -46877,19 +46913,15 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46877
46913
|
const iw = Math.min(iwReq, width - FIT_PAD - x0 - 2 * PAD);
|
|
46878
46914
|
if (iw < 24) return boxX;
|
|
46879
46915
|
const xr = x0 + iw + 2 * PAD;
|
|
46880
|
-
const
|
|
46881
|
-
const
|
|
46882
|
-
const yR = top(xr);
|
|
46916
|
+
const floor = coastFloor(x0, xr);
|
|
46917
|
+
const topGuess = floor > -Infinity ? floor + GAP : yB - height * 0.42;
|
|
46883
46918
|
proj.fitWidth(iw, f);
|
|
46884
46919
|
const bb = (0, import_d3_geo2.geoPath)(proj).bounds(f);
|
|
46885
46920
|
const sh = Number.isFinite(bb[0][0]) ? bb[1][1] - bb[0][1] : iw;
|
|
46886
46921
|
const needH = sh + 2 * PAD;
|
|
46887
|
-
let topFit =
|
|
46922
|
+
let topFit = topGuess;
|
|
46888
46923
|
const bottom = Math.min(topFit + needH, yB);
|
|
46889
46924
|
if (bottom - topFit < needH) topFit = bottom - needH;
|
|
46890
|
-
const lift = topFit - Math.max(yL, yR);
|
|
46891
|
-
const topL = yL + lift;
|
|
46892
|
-
const topR = yR + lift;
|
|
46893
46925
|
proj.fitExtent(
|
|
46894
46926
|
[
|
|
46895
46927
|
[x0 + PAD, topFit + PAD],
|
|
@@ -46908,15 +46940,18 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46908
46940
|
}
|
|
46909
46941
|
insets.push({
|
|
46910
46942
|
x: x0,
|
|
46911
|
-
y:
|
|
46943
|
+
y: topFit,
|
|
46912
46944
|
w: xr - x0,
|
|
46913
|
-
h: bottom -
|
|
46945
|
+
h: bottom - topFit,
|
|
46914
46946
|
points: [
|
|
46915
|
-
[x0,
|
|
46916
|
-
[xr,
|
|
46947
|
+
[x0, topFit],
|
|
46948
|
+
[xr, topFit],
|
|
46917
46949
|
[xr, bottom],
|
|
46918
46950
|
[x0, bottom]
|
|
46919
|
-
]
|
|
46951
|
+
],
|
|
46952
|
+
// The FITTED inset projection (just fit to this box) — captured so the
|
|
46953
|
+
// geo-query can invert pixels inside the frame back to AK/HI coords.
|
|
46954
|
+
projection: proj
|
|
46920
46955
|
});
|
|
46921
46956
|
insetRegions.push({
|
|
46922
46957
|
id: iso,
|
|
@@ -47086,13 +47121,40 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47086
47121
|
id: "lake",
|
|
47087
47122
|
d,
|
|
47088
47123
|
fill: water,
|
|
47089
|
-
stroke:
|
|
47124
|
+
stroke: lakeStroke,
|
|
47090
47125
|
lineNumber: -1,
|
|
47091
47126
|
layer: "base"
|
|
47092
47127
|
});
|
|
47093
47128
|
}
|
|
47094
47129
|
}
|
|
47095
|
-
const
|
|
47130
|
+
const relief = [];
|
|
47131
|
+
let reliefHatch = null;
|
|
47132
|
+
if (resolved.directives.relief === true && data.mountainRanges) {
|
|
47133
|
+
for (const [, f] of decodeLayer(data.mountainRanges)) {
|
|
47134
|
+
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
47135
|
+
if (!viewF) continue;
|
|
47136
|
+
const area2 = path.area(viewF);
|
|
47137
|
+
if (!Number.isFinite(area2) || area2 < RELIEF_MIN_AREA) continue;
|
|
47138
|
+
const box = path.bounds(viewF);
|
|
47139
|
+
if (box[1][0] - box[0][0] < RELIEF_MIN_DIM || box[1][1] - box[0][1] < RELIEF_MIN_DIM)
|
|
47140
|
+
continue;
|
|
47141
|
+
const d = path(viewF) ?? "";
|
|
47142
|
+
if (!d) continue;
|
|
47143
|
+
relief.push({ d });
|
|
47144
|
+
}
|
|
47145
|
+
if (relief.length) {
|
|
47146
|
+
const darkTone = isDark ? palette.bg : palette.text;
|
|
47147
|
+
const lightTone = isDark ? palette.text : palette.bg;
|
|
47148
|
+
const landLum = relativeLuminance(neutralFill);
|
|
47149
|
+
const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
|
|
47150
|
+
reliefHatch = {
|
|
47151
|
+
color: mix(tone, neutralFill, RELIEF_HATCH_STRENGTH),
|
|
47152
|
+
spacing: RELIEF_HATCH_SPACING,
|
|
47153
|
+
width: RELIEF_HATCH_WIDTH
|
|
47154
|
+
};
|
|
47155
|
+
}
|
|
47156
|
+
}
|
|
47157
|
+
const riverColor = mix(water, regionStroke, 16);
|
|
47096
47158
|
const rivers = [];
|
|
47097
47159
|
if (data.rivers) {
|
|
47098
47160
|
for (const [, f] of decodeLayer(data.rivers)) {
|
|
@@ -47113,6 +47175,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47113
47175
|
return R_MIN + Math.max(0, Math.min(1, t)) * (R_MAX - R_MIN);
|
|
47114
47176
|
};
|
|
47115
47177
|
const poiFill = (p) => {
|
|
47178
|
+
const directHex = p.color ? resolveColor(p.color, palette) : null;
|
|
47179
|
+
if (directHex)
|
|
47180
|
+
return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
|
|
47116
47181
|
for (const group of resolved.tagGroups) {
|
|
47117
47182
|
const val = p.tags[group.name.toLowerCase()];
|
|
47118
47183
|
if (!val) continue;
|
|
@@ -47528,21 +47593,26 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47528
47593
|
...resolved.caption !== void 0 && { caption: resolved.caption },
|
|
47529
47594
|
regions,
|
|
47530
47595
|
rivers,
|
|
47596
|
+
relief,
|
|
47597
|
+
reliefHatch,
|
|
47531
47598
|
legs,
|
|
47532
47599
|
pois,
|
|
47533
47600
|
labels,
|
|
47534
47601
|
legend,
|
|
47535
47602
|
insets,
|
|
47536
|
-
insetRegions
|
|
47603
|
+
insetRegions,
|
|
47604
|
+
projection,
|
|
47605
|
+
stretch: stretchParams
|
|
47537
47606
|
};
|
|
47538
47607
|
}
|
|
47539
|
-
var import_d3_geo2, import_topojson_client2, FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK,
|
|
47608
|
+
var import_d3_geo2, import_topojson_client2, FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT_LIGHT, WATER_TINT_DARK, RIVER_WIDTH, RELIEF_MIN_AREA, RELIEF_MIN_DIM, RELIEF_HATCH_SPACING, RELIEF_HATCH_WIDTH, RELIEF_HATCH_STRENGTH, FOREIGN_TINT_LIGHT, FOREIGN_TINT_DARK, MUTED_FOREIGN_LIGHT, MUTED_FOREIGN_DARK, COLO_R, GOLDEN_ANGLE, FAN_STEP, ARC_CURVE_FRAC, usConusProjection, alaskaProjection, hawaiiProjection, INSET_STATES, US_NON_CONUS;
|
|
47540
47609
|
var init_layout15 = __esm({
|
|
47541
47610
|
"src/map/layout.ts"() {
|
|
47542
47611
|
"use strict";
|
|
47543
47612
|
import_d3_geo2 = require("d3-geo");
|
|
47544
47613
|
import_topojson_client2 = require("topojson-client");
|
|
47545
47614
|
init_color_utils();
|
|
47615
|
+
init_colors();
|
|
47546
47616
|
init_label_layout();
|
|
47547
47617
|
init_legend_constants();
|
|
47548
47618
|
init_title_constants();
|
|
@@ -47555,19 +47625,22 @@ var init_layout15 = __esm({
|
|
|
47555
47625
|
W_MAX = 8;
|
|
47556
47626
|
FONT = 11;
|
|
47557
47627
|
COLO_EPS = 1.5;
|
|
47558
|
-
LAND_TINT_LIGHT =
|
|
47559
|
-
LAND_TINT_DARK =
|
|
47628
|
+
LAND_TINT_LIGHT = 12;
|
|
47629
|
+
LAND_TINT_DARK = 24;
|
|
47560
47630
|
TAG_TINT_LIGHT = 60;
|
|
47561
47631
|
TAG_TINT_DARK = 68;
|
|
47562
|
-
|
|
47632
|
+
WATER_TINT_LIGHT = 13;
|
|
47633
|
+
WATER_TINT_DARK = 14;
|
|
47563
47634
|
RIVER_WIDTH = 1.3;
|
|
47635
|
+
RELIEF_MIN_AREA = 12;
|
|
47636
|
+
RELIEF_MIN_DIM = 2;
|
|
47637
|
+
RELIEF_HATCH_SPACING = 3;
|
|
47638
|
+
RELIEF_HATCH_WIDTH = 0.25;
|
|
47639
|
+
RELIEF_HATCH_STRENGTH = 32;
|
|
47564
47640
|
FOREIGN_TINT_LIGHT = 30;
|
|
47565
47641
|
FOREIGN_TINT_DARK = 62;
|
|
47566
|
-
MUTED_WATER_LIGHT = 14;
|
|
47567
|
-
MUTED_WATER_DARK = 10;
|
|
47568
47642
|
MUTED_FOREIGN_LIGHT = 28;
|
|
47569
47643
|
MUTED_FOREIGN_DARK = 16;
|
|
47570
|
-
MUTED_LAND_DARK = 24;
|
|
47571
47644
|
COLO_R = 9;
|
|
47572
47645
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
47573
47646
|
FAN_STEP = 16;
|
|
@@ -47639,6 +47712,20 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47639
47712
|
}
|
|
47640
47713
|
};
|
|
47641
47714
|
for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
|
|
47715
|
+
if (layout.relief.length && layout.reliefHatch) {
|
|
47716
|
+
const h = layout.reliefHatch;
|
|
47717
|
+
const rangeClipId = "dgmo-relief-clip";
|
|
47718
|
+
const landClipId = "dgmo-relief-land";
|
|
47719
|
+
const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
|
|
47720
|
+
for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
|
|
47721
|
+
const landClip = defs.append("clipPath").attr("id", landClipId);
|
|
47722
|
+
for (const r of layout.regions)
|
|
47723
|
+
if (r.id !== "lake") landClip.append("path").attr("d", r.d);
|
|
47724
|
+
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");
|
|
47725
|
+
for (let y = h.spacing; y < height; y += h.spacing) {
|
|
47726
|
+
gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
|
|
47727
|
+
}
|
|
47728
|
+
}
|
|
47642
47729
|
if (layout.rivers.length) {
|
|
47643
47730
|
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
47644
47731
|
for (const r of layout.rivers) {
|
|
@@ -47763,7 +47850,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47763
47850
|
}
|
|
47764
47851
|
}
|
|
47765
47852
|
if (layout.title) {
|
|
47766
|
-
svg.append("text").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("fill", palette.text).attr("paint-order", "stroke fill").attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linejoin", "round").attr("stroke-opacity", 0.7).text(layout.title);
|
|
47853
|
+
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);
|
|
47767
47854
|
}
|
|
47768
47855
|
if (layout.subtitle) {
|
|
47769
47856
|
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);
|
|
@@ -47796,6 +47883,121 @@ var init_renderer16 = __esm({
|
|
|
47796
47883
|
}
|
|
47797
47884
|
});
|
|
47798
47885
|
|
|
47886
|
+
// src/map/load-data.ts
|
|
47887
|
+
var load_data_exports = {};
|
|
47888
|
+
__export(load_data_exports, {
|
|
47889
|
+
loadMapData: () => loadMapData
|
|
47890
|
+
});
|
|
47891
|
+
async function loadNodeBuiltins() {
|
|
47892
|
+
const [{ readFile }, { fileURLToPath }, { dirname: dirname2, resolve }] = await Promise.all([
|
|
47893
|
+
import("fs/promises"),
|
|
47894
|
+
import("url"),
|
|
47895
|
+
import("path")
|
|
47896
|
+
]);
|
|
47897
|
+
return { readFile, fileURLToPath, dirname: dirname2, resolve };
|
|
47898
|
+
}
|
|
47899
|
+
async function readJson(nb, dir, name) {
|
|
47900
|
+
return JSON.parse(await nb.readFile(nb.resolve(dir, name), "utf8"));
|
|
47901
|
+
}
|
|
47902
|
+
async function firstExistingDir(nb, baseDir) {
|
|
47903
|
+
for (const rel of CANDIDATE_DIRS) {
|
|
47904
|
+
const dir = nb.resolve(baseDir, rel);
|
|
47905
|
+
try {
|
|
47906
|
+
await nb.readFile(nb.resolve(dir, FILES.gazetteer), "utf8");
|
|
47907
|
+
return dir;
|
|
47908
|
+
} catch {
|
|
47909
|
+
}
|
|
47910
|
+
}
|
|
47911
|
+
throw new Error(
|
|
47912
|
+
`map data assets not found near ${baseDir} (looked in ${CANDIDATE_DIRS.join(", ")}). Run \`pnpm build:map-data\` and \`pnpm build\`.`
|
|
47913
|
+
);
|
|
47914
|
+
}
|
|
47915
|
+
function validate(data) {
|
|
47916
|
+
const topoOk = (t) => !!t && t.type === "Topology" && !!t.objects;
|
|
47917
|
+
if (!topoOk(data.worldCoarse) || !topoOk(data.worldDetail) || !topoOk(data.usStates) || !data.gazetteer || !Array.isArray(data.gazetteer.cities) || !data.gazetteer.byName) {
|
|
47918
|
+
throw new Error("map data assets are malformed (failed shape validation)");
|
|
47919
|
+
}
|
|
47920
|
+
return data;
|
|
47921
|
+
}
|
|
47922
|
+
function moduleBaseDir(nb) {
|
|
47923
|
+
try {
|
|
47924
|
+
const url = import_meta.url;
|
|
47925
|
+
if (url) return nb.dirname(nb.fileURLToPath(url));
|
|
47926
|
+
} catch {
|
|
47927
|
+
}
|
|
47928
|
+
if (typeof __dirname !== "undefined") return __dirname;
|
|
47929
|
+
return process.cwd();
|
|
47930
|
+
}
|
|
47931
|
+
function loadMapData() {
|
|
47932
|
+
cache ??= (async () => {
|
|
47933
|
+
const nb = await loadNodeBuiltins();
|
|
47934
|
+
const dir = await firstExistingDir(nb, moduleBaseDir(nb));
|
|
47935
|
+
const [
|
|
47936
|
+
worldCoarse,
|
|
47937
|
+
worldDetail,
|
|
47938
|
+
usStates,
|
|
47939
|
+
lakes,
|
|
47940
|
+
rivers,
|
|
47941
|
+
mountainRanges,
|
|
47942
|
+
naLand,
|
|
47943
|
+
naLakes,
|
|
47944
|
+
gazetteer
|
|
47945
|
+
] = await Promise.all([
|
|
47946
|
+
readJson(nb, dir, FILES.worldCoarse),
|
|
47947
|
+
readJson(nb, dir, FILES.worldDetail),
|
|
47948
|
+
readJson(nb, dir, FILES.usStates),
|
|
47949
|
+
// Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
|
|
47950
|
+
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
47951
|
+
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
47952
|
+
readJson(nb, dir, FILES.mountainRanges).catch(
|
|
47953
|
+
() => void 0
|
|
47954
|
+
),
|
|
47955
|
+
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
47956
|
+
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
47957
|
+
readJson(nb, dir, FILES.gazetteer)
|
|
47958
|
+
]);
|
|
47959
|
+
return validate({
|
|
47960
|
+
worldCoarse,
|
|
47961
|
+
worldDetail,
|
|
47962
|
+
usStates,
|
|
47963
|
+
gazetteer,
|
|
47964
|
+
...lakes && { lakes },
|
|
47965
|
+
...rivers && { rivers },
|
|
47966
|
+
...mountainRanges && { mountainRanges },
|
|
47967
|
+
...naLand && { naLand },
|
|
47968
|
+
...naLakes && { naLakes }
|
|
47969
|
+
});
|
|
47970
|
+
})().catch((e) => {
|
|
47971
|
+
cache = void 0;
|
|
47972
|
+
throw e;
|
|
47973
|
+
});
|
|
47974
|
+
return cache;
|
|
47975
|
+
}
|
|
47976
|
+
var import_meta, FILES, CANDIDATE_DIRS, cache;
|
|
47977
|
+
var init_load_data = __esm({
|
|
47978
|
+
"src/map/load-data.ts"() {
|
|
47979
|
+
"use strict";
|
|
47980
|
+
import_meta = {};
|
|
47981
|
+
FILES = {
|
|
47982
|
+
worldCoarse: "world-coarse.json",
|
|
47983
|
+
worldDetail: "world-detail.json",
|
|
47984
|
+
usStates: "us-states.json",
|
|
47985
|
+
lakes: "lakes.json",
|
|
47986
|
+
rivers: "rivers.json",
|
|
47987
|
+
mountainRanges: "mountain-ranges.json",
|
|
47988
|
+
naLand: "na-land.json",
|
|
47989
|
+
naLakes: "na-lakes.json",
|
|
47990
|
+
gazetteer: "gazetteer.json"
|
|
47991
|
+
};
|
|
47992
|
+
CANDIDATE_DIRS = [
|
|
47993
|
+
"./data",
|
|
47994
|
+
"./map-data",
|
|
47995
|
+
"../map-data",
|
|
47996
|
+
"../src/map/data"
|
|
47997
|
+
];
|
|
47998
|
+
}
|
|
47999
|
+
});
|
|
48000
|
+
|
|
47799
48001
|
// src/pyramid/renderer.ts
|
|
47800
48002
|
var renderer_exports17 = {};
|
|
47801
48003
|
__export(renderer_exports17, {
|
|
@@ -55975,15 +56177,17 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
55975
56177
|
if (detectedType === "map") {
|
|
55976
56178
|
const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
|
|
55977
56179
|
const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
|
|
55978
|
-
const { loadMapData: loadMapData2 } = await Promise.resolve().then(() => (init_load_data(), load_data_exports));
|
|
55979
56180
|
const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
|
|
55980
56181
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
55981
56182
|
const mapParsed = parseMap2(content);
|
|
55982
|
-
let mapData;
|
|
55983
|
-
|
|
55984
|
-
|
|
55985
|
-
|
|
55986
|
-
|
|
56183
|
+
let mapData = options?.mapData;
|
|
56184
|
+
if (!mapData) {
|
|
56185
|
+
const { loadMapData: loadMapData2 } = await Promise.resolve().then(() => (init_load_data(), load_data_exports));
|
|
56186
|
+
try {
|
|
56187
|
+
mapData = await loadMapData2();
|
|
56188
|
+
} catch {
|
|
56189
|
+
return "";
|
|
56190
|
+
}
|
|
55987
56191
|
}
|
|
55988
56192
|
const mapResolved = resolveMap2(mapParsed, mapData);
|
|
55989
56193
|
const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
@@ -56834,6 +57038,7 @@ __export(advanced_exports, {
|
|
|
56834
57038
|
computeTimeTicks: () => computeTimeTicks,
|
|
56835
57039
|
contrastText: () => contrastText,
|
|
56836
57040
|
controlsGroupCapsuleWidth: () => controlsGroupCapsuleWidth,
|
|
57041
|
+
createMapGeoQuery: () => createMapGeoQuery,
|
|
56837
57042
|
decodeDiagramUrl: () => decodeDiagramUrl,
|
|
56838
57043
|
decodeViewState: () => decodeViewState,
|
|
56839
57044
|
displayName: () => displayName,
|
|
@@ -57560,6 +57765,160 @@ init_load_data();
|
|
|
57560
57765
|
init_layout15();
|
|
57561
57766
|
init_renderer16();
|
|
57562
57767
|
|
|
57768
|
+
// src/map/geo-query.ts
|
|
57769
|
+
init_parser12();
|
|
57770
|
+
init_resolver2();
|
|
57771
|
+
init_layout15();
|
|
57772
|
+
init_geo();
|
|
57773
|
+
|
|
57774
|
+
// src/map/invert.ts
|
|
57775
|
+
function inInsetFrame(inset, px, py) {
|
|
57776
|
+
return px >= inset.x && px <= inset.x + inset.w && py >= inset.y && py <= inset.y + inset.h;
|
|
57777
|
+
}
|
|
57778
|
+
function unstretch(layout, px, py) {
|
|
57779
|
+
const s = layout.stretch;
|
|
57780
|
+
return [
|
|
57781
|
+
s.bx0 + (s.sx !== 0 ? (px - s.ox) / s.sx : 0),
|
|
57782
|
+
s.by0 + (s.sy !== 0 ? (py - s.oy) / s.sy : 0)
|
|
57783
|
+
];
|
|
57784
|
+
}
|
|
57785
|
+
function applyStretch(layout, x, y) {
|
|
57786
|
+
const s = layout.stretch;
|
|
57787
|
+
return [s.ox + (x - s.bx0) * s.sx, s.oy + (y - s.by0) * s.sy];
|
|
57788
|
+
}
|
|
57789
|
+
function pixelToLonLat(layout, px, py) {
|
|
57790
|
+
for (const inset of layout.insets) {
|
|
57791
|
+
if (inInsetFrame(inset, px, py)) {
|
|
57792
|
+
const ll2 = inset.projection.invert?.([px, py]);
|
|
57793
|
+
return ll2 && Number.isFinite(ll2[0]) && Number.isFinite(ll2[1]) ? [ll2[0], ll2[1]] : null;
|
|
57794
|
+
}
|
|
57795
|
+
}
|
|
57796
|
+
const [x, y] = layout.stretch ? unstretch(layout, px, py) : [px, py];
|
|
57797
|
+
const ll = layout.projection.invert?.([x, y]);
|
|
57798
|
+
return ll && Number.isFinite(ll[0]) && Number.isFinite(ll[1]) ? [ll[0], ll[1]] : null;
|
|
57799
|
+
}
|
|
57800
|
+
function lonLatToPixel(layout, lonLat) {
|
|
57801
|
+
const pt = [lonLat[0], lonLat[1]];
|
|
57802
|
+
const main = layout.projection(pt);
|
|
57803
|
+
const mainPx = main && Number.isFinite(main[0]) && Number.isFinite(main[1]) ? layout.stretch ? applyStretch(layout, main[0], main[1]) : [main[0], main[1]] : null;
|
|
57804
|
+
const onCanvas = !!mainPx && mainPx[0] >= 0 && mainPx[0] <= layout.width && mainPx[1] >= 0 && mainPx[1] <= layout.height;
|
|
57805
|
+
if (onCanvas) return mainPx;
|
|
57806
|
+
for (const inset of layout.insets) {
|
|
57807
|
+
const p = inset.projection(pt);
|
|
57808
|
+
if (p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && inInsetFrame(inset, p[0], p[1]))
|
|
57809
|
+
return [p[0], p[1]];
|
|
57810
|
+
}
|
|
57811
|
+
return mainPx;
|
|
57812
|
+
}
|
|
57813
|
+
|
|
57814
|
+
// src/map/geo-query.ts
|
|
57815
|
+
var EARTH_R_KM = 6371;
|
|
57816
|
+
var DEG = Math.PI / 180;
|
|
57817
|
+
function haversineKm(lat1, lon1, lat2, lon2) {
|
|
57818
|
+
const dLat = (lat2 - lat1) * DEG;
|
|
57819
|
+
const dLon = (lon2 - lon1) * DEG;
|
|
57820
|
+
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * DEG) * Math.cos(lat2 * DEG) * Math.sin(dLon / 2) ** 2;
|
|
57821
|
+
return 2 * EARTH_R_KM * Math.asin(Math.min(1, Math.sqrt(a)));
|
|
57822
|
+
}
|
|
57823
|
+
var POP_PULL_KM = 12;
|
|
57824
|
+
function nearestCity(lonLat, gazetteer) {
|
|
57825
|
+
const [lon, lat] = lonLat;
|
|
57826
|
+
let best = null;
|
|
57827
|
+
const cities = gazetteer.cities;
|
|
57828
|
+
for (let i = 0; i < cities.length; i++) {
|
|
57829
|
+
const c2 = cities[i];
|
|
57830
|
+
const dist = haversineKm(lat, lon, c2[0], c2[1]);
|
|
57831
|
+
const score = dist - POP_PULL_KM * Math.log10((c2[3] || 0) + 1);
|
|
57832
|
+
if (!best || score < best.score) best = { score, idx: i, dist };
|
|
57833
|
+
}
|
|
57834
|
+
if (!best) return null;
|
|
57835
|
+
const c = cities[best.idx];
|
|
57836
|
+
return {
|
|
57837
|
+
name: c[4],
|
|
57838
|
+
iso: c[2],
|
|
57839
|
+
...c[5] !== void 0 && { sub: c[5] },
|
|
57840
|
+
distanceKm: best.dist
|
|
57841
|
+
};
|
|
57842
|
+
}
|
|
57843
|
+
function roundCoord(n) {
|
|
57844
|
+
return Number(n.toFixed(2));
|
|
57845
|
+
}
|
|
57846
|
+
function buildTokens(lonLat, region, city) {
|
|
57847
|
+
const coordPoiLine = `poi ${roundCoord(lonLat[1])} ${roundCoord(lonLat[0])}`;
|
|
57848
|
+
let stateTok = null;
|
|
57849
|
+
if (region.state) {
|
|
57850
|
+
const { iso, name } = region.state;
|
|
57851
|
+
stateTok = { primary: `${name} ${iso}`, alternates: [iso, name] };
|
|
57852
|
+
}
|
|
57853
|
+
let countryTok = null;
|
|
57854
|
+
if (region.country) {
|
|
57855
|
+
const { iso, name } = region.country;
|
|
57856
|
+
countryTok = { primary: name, alternates: [iso] };
|
|
57857
|
+
}
|
|
57858
|
+
let cityTok = null;
|
|
57859
|
+
if (city) {
|
|
57860
|
+
const scope = city.sub ?? (city.iso || "");
|
|
57861
|
+
cityTok = scope ? { token: `poi ${city.name} ${scope}`, ambiguous: false } : { token: `poi ${city.name}`, ambiguous: true };
|
|
57862
|
+
}
|
|
57863
|
+
return { coordPoiLine, state: stateTok, country: countryTok, city: cityTok };
|
|
57864
|
+
}
|
|
57865
|
+
var MAX_CITY_DOTS = 250;
|
|
57866
|
+
function createMapGeoQuery(opts) {
|
|
57867
|
+
const { content, width, height, data, palette, isDark } = opts;
|
|
57868
|
+
const resolved = resolveMap(parseMap(content), data);
|
|
57869
|
+
const layout = layoutMap(
|
|
57870
|
+
resolved,
|
|
57871
|
+
data,
|
|
57872
|
+
{ width, height },
|
|
57873
|
+
{ palette, isDark }
|
|
57874
|
+
);
|
|
57875
|
+
const countries = decodeFeatures(data.worldDetail);
|
|
57876
|
+
const states = decodeFeatures(data.usStates);
|
|
57877
|
+
const gazetteer = data.gazetteer;
|
|
57878
|
+
const invert = (px, py) => pixelToLonLat(layout, px, py);
|
|
57879
|
+
const project = (lonLat) => lonLatToPixel(layout, lonLat);
|
|
57880
|
+
const locate = (px, py) => {
|
|
57881
|
+
const lonLat = invert(px, py);
|
|
57882
|
+
if (!lonLat) return null;
|
|
57883
|
+
const region = regionAt(lonLat, countries, states);
|
|
57884
|
+
const city = nearestCity(lonLat, gazetteer);
|
|
57885
|
+
return {
|
|
57886
|
+
lonLat,
|
|
57887
|
+
country: region.country,
|
|
57888
|
+
state: region.state,
|
|
57889
|
+
nearestCity: city,
|
|
57890
|
+
tokens: buildTokens(lonLat, region, city)
|
|
57891
|
+
};
|
|
57892
|
+
};
|
|
57893
|
+
const cities = (extent2) => {
|
|
57894
|
+
const sorted = [...gazetteer.cities].sort((a, b) => b[3] - a[3]);
|
|
57895
|
+
const out = [];
|
|
57896
|
+
for (const c of sorted) {
|
|
57897
|
+
const [lat, lon, iso, pop, name, sub] = c;
|
|
57898
|
+
if (extent2) {
|
|
57899
|
+
const [[w, s], [e, n]] = extent2;
|
|
57900
|
+
if (lon < w || lon > e || lat < s || lat > n) continue;
|
|
57901
|
+
}
|
|
57902
|
+
const p = project([lon, lat]);
|
|
57903
|
+
if (!p) continue;
|
|
57904
|
+
if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
|
|
57905
|
+
out.push({
|
|
57906
|
+
name,
|
|
57907
|
+
iso,
|
|
57908
|
+
...sub !== void 0 && { sub },
|
|
57909
|
+
lon,
|
|
57910
|
+
lat,
|
|
57911
|
+
px: p[0],
|
|
57912
|
+
py: p[1],
|
|
57913
|
+
pop
|
|
57914
|
+
});
|
|
57915
|
+
if (out.length >= MAX_CITY_DOTS) break;
|
|
57916
|
+
}
|
|
57917
|
+
return out;
|
|
57918
|
+
};
|
|
57919
|
+
return { invert, project, locate, cities };
|
|
57920
|
+
}
|
|
57921
|
+
|
|
57563
57922
|
// src/map/completion.ts
|
|
57564
57923
|
var fold2 = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
57565
57924
|
var groupThousands = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
@@ -58712,6 +59071,7 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
|
|
|
58712
59071
|
"default-country": { description: "ISO scope for bare city resolution" },
|
|
58713
59072
|
"default-state": { description: "ISO subdivision scope" },
|
|
58714
59073
|
"no-legend": { description: "Suppress the legend" },
|
|
59074
|
+
relief: { description: "Subtle mountain-range relief shading" },
|
|
58715
59075
|
subtitle: { description: "Subtitle line" },
|
|
58716
59076
|
caption: { description: "Caption line" }
|
|
58717
59077
|
})
|
|
@@ -60217,6 +60577,7 @@ function formatLineDiff(path, original, migrated) {
|
|
|
60217
60577
|
computeTimeTicks,
|
|
60218
60578
|
contrastText,
|
|
60219
60579
|
controlsGroupCapsuleWidth,
|
|
60580
|
+
createMapGeoQuery,
|
|
60220
60581
|
decodeDiagramUrl,
|
|
60221
60582
|
decodeViewState,
|
|
60222
60583
|
displayName,
|