@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.js
CHANGED
|
@@ -15940,10 +15940,13 @@ function parseMap(content) {
|
|
|
15940
15940
|
);
|
|
15941
15941
|
d.projection = value;
|
|
15942
15942
|
break;
|
|
15943
|
-
case "region-metric":
|
|
15943
|
+
case "region-metric": {
|
|
15944
15944
|
dup(d.regionMetric);
|
|
15945
|
-
|
|
15945
|
+
const { label: rmLabel, colorName: rmColor } = peelTrailingColorName(value);
|
|
15946
|
+
d.regionMetric = rmLabel;
|
|
15947
|
+
if (rmColor) d.regionMetricColor = rmColor;
|
|
15946
15948
|
break;
|
|
15949
|
+
}
|
|
15947
15950
|
case "poi-metric":
|
|
15948
15951
|
dup(d.poiMetric);
|
|
15949
15952
|
d.poiMetric = value;
|
|
@@ -15992,6 +15995,12 @@ function parseMap(content) {
|
|
|
15992
15995
|
case "no-legend":
|
|
15993
15996
|
d.noLegend = true;
|
|
15994
15997
|
break;
|
|
15998
|
+
case "no-insets":
|
|
15999
|
+
d.noInsets = true;
|
|
16000
|
+
break;
|
|
16001
|
+
case "relief":
|
|
16002
|
+
d.relief = true;
|
|
16003
|
+
break;
|
|
15995
16004
|
case "muted":
|
|
15996
16005
|
case "natural":
|
|
15997
16006
|
if (d.basemapStyle !== void 0 && d.basemapStyle !== key)
|
|
@@ -16104,6 +16113,7 @@ function parseMap(content) {
|
|
|
16104
16113
|
};
|
|
16105
16114
|
if (regionScope !== void 0) region.scope = regionScope;
|
|
16106
16115
|
if (valueNum !== void 0) region.value = valueNum;
|
|
16116
|
+
if (split.color) region.color = split.color;
|
|
16107
16117
|
regions.push(region);
|
|
16108
16118
|
}
|
|
16109
16119
|
function handlePoi(rest, line12, indent) {
|
|
@@ -16128,6 +16138,7 @@ function parseMap(content) {
|
|
|
16128
16138
|
const poi = { pos, tags, meta, lineNumber: line12 };
|
|
16129
16139
|
if (split.alias) poi.alias = split.alias;
|
|
16130
16140
|
if (label !== void 0) poi.label = label;
|
|
16141
|
+
if (split.color) poi.color = split.color;
|
|
16131
16142
|
pois.push(poi);
|
|
16132
16143
|
open.poi = { poi, indent };
|
|
16133
16144
|
}
|
|
@@ -16332,6 +16343,8 @@ var init_parser12 = __esm({
|
|
|
16332
16343
|
"default-state",
|
|
16333
16344
|
"active-tag",
|
|
16334
16345
|
"no-legend",
|
|
16346
|
+
"no-insets",
|
|
16347
|
+
"relief",
|
|
16335
16348
|
"subtitle",
|
|
16336
16349
|
"caption"
|
|
16337
16350
|
]);
|
|
@@ -45843,7 +45856,7 @@ var init_renderer15 = __esm({
|
|
|
45843
45856
|
|
|
45844
45857
|
// src/map/geo.ts
|
|
45845
45858
|
import { feature } from "topojson-client";
|
|
45846
|
-
import { geoBounds } from "d3-geo";
|
|
45859
|
+
import { geoBounds, geoArea } from "d3-geo";
|
|
45847
45860
|
function geomObject(topo) {
|
|
45848
45861
|
const key = Object.keys(topo.objects)[0];
|
|
45849
45862
|
return topo.objects[key];
|
|
@@ -45860,6 +45873,84 @@ function featureIndex(topo) {
|
|
|
45860
45873
|
}
|
|
45861
45874
|
return idx;
|
|
45862
45875
|
}
|
|
45876
|
+
function decodeFeatures(topo) {
|
|
45877
|
+
return geomObject(topo).geometries.map((g) => {
|
|
45878
|
+
const f = feature(topo, g);
|
|
45879
|
+
return {
|
|
45880
|
+
type: "Feature",
|
|
45881
|
+
id: g.id,
|
|
45882
|
+
properties: g.properties,
|
|
45883
|
+
geometry: f.geometry
|
|
45884
|
+
};
|
|
45885
|
+
});
|
|
45886
|
+
}
|
|
45887
|
+
function pointInRing(lon, lat, ring) {
|
|
45888
|
+
let inside = false;
|
|
45889
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
45890
|
+
const xi = ring[i][0];
|
|
45891
|
+
const yi = ring[i][1];
|
|
45892
|
+
const xj = ring[j][0];
|
|
45893
|
+
const yj = ring[j][1];
|
|
45894
|
+
const intersect = yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi;
|
|
45895
|
+
if (intersect) inside = !inside;
|
|
45896
|
+
}
|
|
45897
|
+
return inside;
|
|
45898
|
+
}
|
|
45899
|
+
function pointOnRingEdge(lon, lat, ring) {
|
|
45900
|
+
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
45901
|
+
const xi = ring[i][0];
|
|
45902
|
+
const yi = ring[i][1];
|
|
45903
|
+
const xj = ring[j][0];
|
|
45904
|
+
const yj = ring[j][1];
|
|
45905
|
+
if (lon < Math.min(xi, xj) - EDGE_EPS || lon > Math.max(xi, xj) + EDGE_EPS)
|
|
45906
|
+
continue;
|
|
45907
|
+
if (lat < Math.min(yi, yj) - EDGE_EPS || lat > Math.max(yi, yj) + EDGE_EPS)
|
|
45908
|
+
continue;
|
|
45909
|
+
const cross = (xj - xi) * (lat - yi) - (yj - yi) * (lon - xi);
|
|
45910
|
+
if (Math.abs(cross) <= EDGE_EPS) return true;
|
|
45911
|
+
}
|
|
45912
|
+
return false;
|
|
45913
|
+
}
|
|
45914
|
+
function pointInGeometry(geometry, lon, lat) {
|
|
45915
|
+
const g = geometry;
|
|
45916
|
+
if (!g) return false;
|
|
45917
|
+
const polys = g.type === "Polygon" ? [g.coordinates] : g.type === "MultiPolygon" ? g.coordinates : [];
|
|
45918
|
+
for (const rings of polys) {
|
|
45919
|
+
if (!rings.length) continue;
|
|
45920
|
+
if (pointOnRingEdge(lon, lat, rings[0])) return true;
|
|
45921
|
+
if (!pointInRing(lon, lat, rings[0])) continue;
|
|
45922
|
+
let inHole = false;
|
|
45923
|
+
for (let h = 1; h < rings.length; h++) {
|
|
45924
|
+
if (pointInRing(lon, lat, rings[h]) && !pointOnRingEdge(lon, lat, rings[h])) {
|
|
45925
|
+
inHole = true;
|
|
45926
|
+
break;
|
|
45927
|
+
}
|
|
45928
|
+
}
|
|
45929
|
+
if (!inHole) return true;
|
|
45930
|
+
}
|
|
45931
|
+
return false;
|
|
45932
|
+
}
|
|
45933
|
+
function regionAt(lonLat, countries, states) {
|
|
45934
|
+
const lon = lonLat[0];
|
|
45935
|
+
const lat = lonLat[1];
|
|
45936
|
+
let country = null;
|
|
45937
|
+
for (const f of countries) {
|
|
45938
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
45939
|
+
country = { iso: f.id, name: f.properties.name };
|
|
45940
|
+
break;
|
|
45941
|
+
}
|
|
45942
|
+
}
|
|
45943
|
+
let state = null;
|
|
45944
|
+
if (country?.iso === "US" && states) {
|
|
45945
|
+
for (const f of states) {
|
|
45946
|
+
if (pointInGeometry(f.geometry, lon, lat)) {
|
|
45947
|
+
state = { iso: f.id, name: f.properties.name };
|
|
45948
|
+
break;
|
|
45949
|
+
}
|
|
45950
|
+
}
|
|
45951
|
+
}
|
|
45952
|
+
return { country, state };
|
|
45953
|
+
}
|
|
45863
45954
|
function featureBbox(topo, geomId) {
|
|
45864
45955
|
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45865
45956
|
if (!geom) return null;
|
|
@@ -45871,6 +45962,74 @@ function featureBbox(topo, geomId) {
|
|
|
45871
45962
|
[b[1][0], b[1][1]]
|
|
45872
45963
|
];
|
|
45873
45964
|
}
|
|
45965
|
+
function explodePolygons(gj) {
|
|
45966
|
+
const g = gj.geometry ?? gj;
|
|
45967
|
+
const t = g.type;
|
|
45968
|
+
const coords = g.coordinates;
|
|
45969
|
+
if (t === "Polygon") {
|
|
45970
|
+
return [
|
|
45971
|
+
{ type: "Feature", geometry: { type: "Polygon", coordinates: coords } }
|
|
45972
|
+
];
|
|
45973
|
+
}
|
|
45974
|
+
if (t === "MultiPolygon") {
|
|
45975
|
+
return coords.map((rings) => ({
|
|
45976
|
+
type: "Feature",
|
|
45977
|
+
geometry: { type: "Polygon", coordinates: rings }
|
|
45978
|
+
}));
|
|
45979
|
+
}
|
|
45980
|
+
return [];
|
|
45981
|
+
}
|
|
45982
|
+
function bboxGap(a, b) {
|
|
45983
|
+
const lonGap = Math.max(0, a[0][0] - b[1][0], b[0][0] - a[1][0]);
|
|
45984
|
+
const latGap = Math.max(0, a[0][1] - b[1][1], b[0][1] - a[1][1]);
|
|
45985
|
+
return Math.max(lonGap, latGap);
|
|
45986
|
+
}
|
|
45987
|
+
function featureBboxPrimary(topo, geomId) {
|
|
45988
|
+
const geom = geomObject(topo).geometries.find((g) => g.id === geomId);
|
|
45989
|
+
if (!geom) return null;
|
|
45990
|
+
const gj = feature(topo, geom);
|
|
45991
|
+
const parts = explodePolygons(gj);
|
|
45992
|
+
if (parts.length <= 1) return featureBbox(topo, geomId);
|
|
45993
|
+
const polys = parts.map((p) => {
|
|
45994
|
+
const b = geoBounds(p);
|
|
45995
|
+
if (!b || !Number.isFinite(b[0][0])) return null;
|
|
45996
|
+
const wraps = b[1][0] < b[0][0];
|
|
45997
|
+
const bbox = [
|
|
45998
|
+
[b[0][0], b[0][1]],
|
|
45999
|
+
[b[1][0], b[1][1]]
|
|
46000
|
+
];
|
|
46001
|
+
return { bbox, area: geoArea(p), wraps };
|
|
46002
|
+
}).filter(
|
|
46003
|
+
(p) => p !== null
|
|
46004
|
+
);
|
|
46005
|
+
if (polys.length <= 1 || polys.some((p) => p.wraps))
|
|
46006
|
+
return featureBbox(topo, geomId);
|
|
46007
|
+
const maxArea = Math.max(...polys.map((p) => p.area));
|
|
46008
|
+
const anchor = polys.find((p) => p.area === maxArea);
|
|
46009
|
+
const cluster = [
|
|
46010
|
+
[anchor.bbox[0][0], anchor.bbox[0][1]],
|
|
46011
|
+
[anchor.bbox[1][0], anchor.bbox[1][1]]
|
|
46012
|
+
];
|
|
46013
|
+
const remaining = polys.filter((p) => p !== anchor);
|
|
46014
|
+
let added = true;
|
|
46015
|
+
while (added) {
|
|
46016
|
+
added = false;
|
|
46017
|
+
for (let i = remaining.length - 1; i >= 0; i--) {
|
|
46018
|
+
const p = remaining[i];
|
|
46019
|
+
const near = bboxGap(p.bbox, cluster) <= DETACH_GAP_DEG;
|
|
46020
|
+
const large = p.area >= DETACH_AREA_FRAC * maxArea;
|
|
46021
|
+
if (near || large) {
|
|
46022
|
+
cluster[0][0] = Math.min(cluster[0][0], p.bbox[0][0]);
|
|
46023
|
+
cluster[0][1] = Math.min(cluster[0][1], p.bbox[0][1]);
|
|
46024
|
+
cluster[1][0] = Math.max(cluster[1][0], p.bbox[1][0]);
|
|
46025
|
+
cluster[1][1] = Math.max(cluster[1][1], p.bbox[1][1]);
|
|
46026
|
+
remaining.splice(i, 1);
|
|
46027
|
+
added = true;
|
|
46028
|
+
}
|
|
46029
|
+
}
|
|
46030
|
+
}
|
|
46031
|
+
return cluster;
|
|
46032
|
+
}
|
|
45874
46033
|
function unionExtent(boxes, points) {
|
|
45875
46034
|
const lats = [];
|
|
45876
46035
|
const lons = [];
|
|
@@ -45909,11 +46068,14 @@ function unionLongitudes(lons) {
|
|
|
45909
46068
|
}
|
|
45910
46069
|
return { west: pts[gapIdx], east: pts[gapIdx - 1] + 360 };
|
|
45911
46070
|
}
|
|
45912
|
-
var fold;
|
|
46071
|
+
var fold, EDGE_EPS, DETACH_GAP_DEG, DETACH_AREA_FRAC;
|
|
45913
46072
|
var init_geo = __esm({
|
|
45914
46073
|
"src/map/geo.ts"() {
|
|
45915
46074
|
"use strict";
|
|
45916
46075
|
fold = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
46076
|
+
EDGE_EPS = 1e-9;
|
|
46077
|
+
DETACH_GAP_DEG = 10;
|
|
46078
|
+
DETACH_AREA_FRAC = 0.25;
|
|
45917
46079
|
}
|
|
45918
46080
|
});
|
|
45919
46081
|
|
|
@@ -46017,12 +46179,12 @@ function resolveMap(parsed, data) {
|
|
|
46017
46179
|
chosen = { ...inState, layer: "us-state" };
|
|
46018
46180
|
} else {
|
|
46019
46181
|
chosen = { ...inCountry, layer: "country" };
|
|
46182
|
+
warn(
|
|
46183
|
+
r.lineNumber,
|
|
46184
|
+
`"${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}").`,
|
|
46185
|
+
"W_MAP_REGION_AMBIGUOUS"
|
|
46186
|
+
);
|
|
46020
46187
|
}
|
|
46021
|
-
warn(
|
|
46022
|
-
r.lineNumber,
|
|
46023
|
-
`"${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}").`,
|
|
46024
|
-
"W_MAP_REGION_AMBIGUOUS"
|
|
46025
|
-
);
|
|
46026
46188
|
} else if (inState) {
|
|
46027
46189
|
chosen = { ...inState, layer: "us-state" };
|
|
46028
46190
|
} else if (inCountry) {
|
|
@@ -46046,6 +46208,7 @@ function resolveMap(parsed, data) {
|
|
|
46046
46208
|
name: chosen.name,
|
|
46047
46209
|
layer: chosen.layer,
|
|
46048
46210
|
...r.value !== void 0 && { value: r.value },
|
|
46211
|
+
...r.color !== void 0 && { color: r.color },
|
|
46049
46212
|
tags: r.tags,
|
|
46050
46213
|
meta: r.meta,
|
|
46051
46214
|
lineNumber: r.lineNumber
|
|
@@ -46187,6 +46350,7 @@ function resolveMap(parsed, data) {
|
|
|
46187
46350
|
lat,
|
|
46188
46351
|
lon,
|
|
46189
46352
|
...p.label !== void 0 && { label: p.label },
|
|
46353
|
+
...p.color !== void 0 && { color: p.color },
|
|
46190
46354
|
tags: p.tags,
|
|
46191
46355
|
meta: p.meta,
|
|
46192
46356
|
lineNumber: p.lineNumber
|
|
@@ -46329,7 +46493,7 @@ function resolveMap(parsed, data) {
|
|
|
46329
46493
|
}
|
|
46330
46494
|
for (const r of regions) {
|
|
46331
46495
|
if (r.layer === "country") {
|
|
46332
|
-
const bb =
|
|
46496
|
+
const bb = featureBboxPrimary(data.worldCoarse, r.iso);
|
|
46333
46497
|
if (bb) regionBoxes.push(bb);
|
|
46334
46498
|
}
|
|
46335
46499
|
}
|
|
@@ -46343,6 +46507,7 @@ function resolveMap(parsed, data) {
|
|
|
46343
46507
|
const lonSpan = extent2[1][0] - extent2[0][0];
|
|
46344
46508
|
const latSpan = extent2[1][1] - extent2[0][1];
|
|
46345
46509
|
const span = Math.max(lonSpan, latSpan);
|
|
46510
|
+
const maxAbsLat = Math.max(Math.abs(extent2[0][1]), Math.abs(extent2[1][1]));
|
|
46346
46511
|
const usDominant = (subdivisions.includes("us-states") || regions.some((r) => r.layer === "us-state")) && !regions.some((r) => r.layer === "country" && r.iso !== "US") && !anyNonUsPoi;
|
|
46347
46512
|
let projection;
|
|
46348
46513
|
const override = parsed.directives.projection;
|
|
@@ -46350,12 +46515,10 @@ function resolveMap(parsed, data) {
|
|
|
46350
46515
|
projection = override;
|
|
46351
46516
|
} else if (usDominant) {
|
|
46352
46517
|
projection = "albers-usa";
|
|
46353
|
-
} else if (span > WORLD_SPAN) {
|
|
46518
|
+
} else if (span > WORLD_SPAN || maxAbsLat > MERCATOR_MAX_LAT) {
|
|
46354
46519
|
projection = "equirectangular";
|
|
46355
|
-
} else if (span < MERCATOR_MAX_SPAN) {
|
|
46356
|
-
projection = "mercator";
|
|
46357
46520
|
} else {
|
|
46358
|
-
projection = "
|
|
46521
|
+
projection = "mercator";
|
|
46359
46522
|
}
|
|
46360
46523
|
if (lonSpan >= 180) {
|
|
46361
46524
|
extent2 = [
|
|
@@ -46409,14 +46572,14 @@ function firstError(diags) {
|
|
|
46409
46572
|
const e = diags.find((d) => d.severity === "error");
|
|
46410
46573
|
return e ? formatDgmoError(e) : null;
|
|
46411
46574
|
}
|
|
46412
|
-
var WORLD_SPAN,
|
|
46575
|
+
var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, REGION_ALIASES, US_STATE_POSTAL;
|
|
46413
46576
|
var init_resolver2 = __esm({
|
|
46414
46577
|
"src/map/resolver.ts"() {
|
|
46415
46578
|
"use strict";
|
|
46416
46579
|
init_diagnostics();
|
|
46417
46580
|
init_geo();
|
|
46418
46581
|
WORLD_SPAN = 90;
|
|
46419
|
-
|
|
46582
|
+
MERCATOR_MAX_LAT = 80;
|
|
46420
46583
|
PAD_FRACTION = 0.05;
|
|
46421
46584
|
WORLD_LAT_SOUTH = -58;
|
|
46422
46585
|
WORLD_LAT_NORTH = 78;
|
|
@@ -46497,114 +46660,6 @@ var init_resolver2 = __esm({
|
|
|
46497
46660
|
}
|
|
46498
46661
|
});
|
|
46499
46662
|
|
|
46500
|
-
// src/map/load-data.ts
|
|
46501
|
-
var load_data_exports = {};
|
|
46502
|
-
__export(load_data_exports, {
|
|
46503
|
-
loadMapData: () => loadMapData
|
|
46504
|
-
});
|
|
46505
|
-
async function loadNodeBuiltins() {
|
|
46506
|
-
const [{ readFile }, { fileURLToPath }, { dirname: dirname2, resolve }] = await Promise.all([
|
|
46507
|
-
import("fs/promises"),
|
|
46508
|
-
import("url"),
|
|
46509
|
-
import("path")
|
|
46510
|
-
]);
|
|
46511
|
-
return { readFile, fileURLToPath, dirname: dirname2, resolve };
|
|
46512
|
-
}
|
|
46513
|
-
async function readJson(nb, dir, name) {
|
|
46514
|
-
return JSON.parse(await nb.readFile(nb.resolve(dir, name), "utf8"));
|
|
46515
|
-
}
|
|
46516
|
-
async function firstExistingDir(nb, baseDir) {
|
|
46517
|
-
for (const rel of CANDIDATE_DIRS) {
|
|
46518
|
-
const dir = nb.resolve(baseDir, rel);
|
|
46519
|
-
try {
|
|
46520
|
-
await nb.readFile(nb.resolve(dir, FILES.gazetteer), "utf8");
|
|
46521
|
-
return dir;
|
|
46522
|
-
} catch {
|
|
46523
|
-
}
|
|
46524
|
-
}
|
|
46525
|
-
throw new Error(
|
|
46526
|
-
`map data assets not found near ${baseDir} (looked in ${CANDIDATE_DIRS.join(", ")}). Run \`pnpm build:map-data\` and \`pnpm build\`.`
|
|
46527
|
-
);
|
|
46528
|
-
}
|
|
46529
|
-
function validate(data) {
|
|
46530
|
-
const topoOk = (t) => !!t && t.type === "Topology" && !!t.objects;
|
|
46531
|
-
if (!topoOk(data.worldCoarse) || !topoOk(data.worldDetail) || !topoOk(data.usStates) || !data.gazetteer || !Array.isArray(data.gazetteer.cities) || !data.gazetteer.byName) {
|
|
46532
|
-
throw new Error("map data assets are malformed (failed shape validation)");
|
|
46533
|
-
}
|
|
46534
|
-
return data;
|
|
46535
|
-
}
|
|
46536
|
-
function moduleBaseDir(nb) {
|
|
46537
|
-
try {
|
|
46538
|
-
const url = import.meta.url;
|
|
46539
|
-
if (url) return nb.dirname(nb.fileURLToPath(url));
|
|
46540
|
-
} catch {
|
|
46541
|
-
}
|
|
46542
|
-
if (typeof __dirname !== "undefined") return __dirname;
|
|
46543
|
-
return process.cwd();
|
|
46544
|
-
}
|
|
46545
|
-
function loadMapData() {
|
|
46546
|
-
cache ??= (async () => {
|
|
46547
|
-
const nb = await loadNodeBuiltins();
|
|
46548
|
-
const dir = await firstExistingDir(nb, moduleBaseDir(nb));
|
|
46549
|
-
const [
|
|
46550
|
-
worldCoarse,
|
|
46551
|
-
worldDetail,
|
|
46552
|
-
usStates,
|
|
46553
|
-
lakes,
|
|
46554
|
-
rivers,
|
|
46555
|
-
naLand,
|
|
46556
|
-
naLakes,
|
|
46557
|
-
gazetteer
|
|
46558
|
-
] = await Promise.all([
|
|
46559
|
-
readJson(nb, dir, FILES.worldCoarse),
|
|
46560
|
-
readJson(nb, dir, FILES.worldDetail),
|
|
46561
|
-
readJson(nb, dir, FILES.usStates),
|
|
46562
|
-
// Lakes/rivers/NA assets are optional — older bundles may predate them.
|
|
46563
|
-
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
46564
|
-
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
46565
|
-
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
46566
|
-
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
46567
|
-
readJson(nb, dir, FILES.gazetteer)
|
|
46568
|
-
]);
|
|
46569
|
-
return validate({
|
|
46570
|
-
worldCoarse,
|
|
46571
|
-
worldDetail,
|
|
46572
|
-
usStates,
|
|
46573
|
-
gazetteer,
|
|
46574
|
-
...lakes && { lakes },
|
|
46575
|
-
...rivers && { rivers },
|
|
46576
|
-
...naLand && { naLand },
|
|
46577
|
-
...naLakes && { naLakes }
|
|
46578
|
-
});
|
|
46579
|
-
})().catch((e) => {
|
|
46580
|
-
cache = void 0;
|
|
46581
|
-
throw e;
|
|
46582
|
-
});
|
|
46583
|
-
return cache;
|
|
46584
|
-
}
|
|
46585
|
-
var FILES, CANDIDATE_DIRS, cache;
|
|
46586
|
-
var init_load_data = __esm({
|
|
46587
|
-
"src/map/load-data.ts"() {
|
|
46588
|
-
"use strict";
|
|
46589
|
-
FILES = {
|
|
46590
|
-
worldCoarse: "world-coarse.json",
|
|
46591
|
-
worldDetail: "world-detail.json",
|
|
46592
|
-
usStates: "us-states.json",
|
|
46593
|
-
lakes: "lakes.json",
|
|
46594
|
-
rivers: "rivers.json",
|
|
46595
|
-
naLand: "na-land.json",
|
|
46596
|
-
naLakes: "na-lakes.json",
|
|
46597
|
-
gazetteer: "gazetteer.json"
|
|
46598
|
-
};
|
|
46599
|
-
CANDIDATE_DIRS = [
|
|
46600
|
-
"./data",
|
|
46601
|
-
"./map-data",
|
|
46602
|
-
"../map-data",
|
|
46603
|
-
"../src/map/data"
|
|
46604
|
-
];
|
|
46605
|
-
}
|
|
46606
|
-
});
|
|
46607
|
-
|
|
46608
46663
|
// src/map/layout.ts
|
|
46609
46664
|
import {
|
|
46610
46665
|
geoPath,
|
|
@@ -46641,18 +46696,14 @@ function projectionFor(family) {
|
|
|
46641
46696
|
return geoEquirectangular();
|
|
46642
46697
|
}
|
|
46643
46698
|
}
|
|
46644
|
-
function mapBackgroundColor(palette, isDark = false,
|
|
46645
|
-
|
|
46646
|
-
|
|
46647
|
-
|
|
46648
|
-
|
|
46649
|
-
|
|
46650
|
-
);
|
|
46651
|
-
return mix(palette.colors.blue, palette.bg, WATER_TINT);
|
|
46699
|
+
function mapBackgroundColor(palette, isDark = false, _dataActive = false) {
|
|
46700
|
+
return mix(
|
|
46701
|
+
palette.colors.blue,
|
|
46702
|
+
palette.bg,
|
|
46703
|
+
isDark ? WATER_TINT_DARK : WATER_TINT_LIGHT
|
|
46704
|
+
);
|
|
46652
46705
|
}
|
|
46653
|
-
function mapNeutralLandColor(palette, isDark,
|
|
46654
|
-
if (dataActive)
|
|
46655
|
-
return isDark ? mix(palette.colors.gray, palette.bg, MUTED_LAND_DARK) : palette.bg;
|
|
46706
|
+
function mapNeutralLandColor(palette, isDark, _dataActive = false) {
|
|
46656
46707
|
return mix(
|
|
46657
46708
|
palette.colors.green,
|
|
46658
46709
|
palette.bg,
|
|
@@ -46684,7 +46735,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46684
46735
|
const scaleOverride = resolved.directives.scale;
|
|
46685
46736
|
const rampMin = scaleOverride ? scaleOverride.min : Math.min(...values);
|
|
46686
46737
|
const rampMax = scaleOverride ? scaleOverride.max : Math.max(...values);
|
|
46687
|
-
const rampHue = palette.colors.red;
|
|
46738
|
+
const rampHue = resolveColor(resolved.directives.regionMetricColor ?? "", palette) ?? palette.colors.red;
|
|
46688
46739
|
const hasRamp = values.length > 0;
|
|
46689
46740
|
const VALUE_NAME = hasRamp ? resolved.directives.regionMetric?.trim() || "Value" : null;
|
|
46690
46741
|
const matchColorGroup = (v) => {
|
|
@@ -46707,6 +46758,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46707
46758
|
const mutedBasemap = resolved.directives.basemapStyle === "muted" ? true : resolved.directives.basemapStyle === "natural" ? false : activeGroup !== null;
|
|
46708
46759
|
const neutralFill = mapNeutralLandColor(palette, isDark, mutedBasemap);
|
|
46709
46760
|
const water = mapBackgroundColor(palette, isDark, mutedBasemap);
|
|
46761
|
+
const lakeStroke = mix(regionStroke, water, 45);
|
|
46710
46762
|
const foreignFill = mix(
|
|
46711
46763
|
palette.colors.gray,
|
|
46712
46764
|
palette.bg,
|
|
@@ -46736,7 +46788,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46736
46788
|
isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT
|
|
46737
46789
|
);
|
|
46738
46790
|
};
|
|
46791
|
+
const directFill = (name) => {
|
|
46792
|
+
const hex = name ? resolveColor(name, palette) : null;
|
|
46793
|
+
if (!hex) return null;
|
|
46794
|
+
return mix(hex, palette.bg, isDark ? TAG_TINT_DARK : TAG_TINT_LIGHT);
|
|
46795
|
+
};
|
|
46739
46796
|
const regionFill = (r) => {
|
|
46797
|
+
const direct = directFill(r.color);
|
|
46798
|
+
if (direct) return direct;
|
|
46740
46799
|
if (activeIsScore) {
|
|
46741
46800
|
return r.value !== void 0 ? fillForValue(r.value) : neutralFill;
|
|
46742
46801
|
}
|
|
@@ -46790,6 +46849,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46790
46849
|
const fitIsGlobal = fitGB[1][0] - fitGB[0][0] >= 270 || fitGB[1][1] - fitGB[0][1] >= 130;
|
|
46791
46850
|
let path;
|
|
46792
46851
|
let project;
|
|
46852
|
+
let stretchParams = null;
|
|
46793
46853
|
if (fitIsGlobal) {
|
|
46794
46854
|
const cb = geoPath(projection).bounds(fitTarget);
|
|
46795
46855
|
const bx0 = cb[0][0];
|
|
@@ -46800,6 +46860,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46800
46860
|
const oy = fitBox[0][1];
|
|
46801
46861
|
const sx = cw > 0 ? (fitBox[1][0] - ox) / cw : 1;
|
|
46802
46862
|
const sy = ch > 0 ? (fitBox[1][1] - oy) / ch : 1;
|
|
46863
|
+
stretchParams = { sx, sy, ox, oy, bx0, by0 };
|
|
46803
46864
|
const stretch = (x, y) => [
|
|
46804
46865
|
ox + (x - bx0) * sx,
|
|
46805
46866
|
oy + (y - by0) * sy
|
|
@@ -46831,7 +46892,7 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46831
46892
|
const insets = [];
|
|
46832
46893
|
const insetRegions = [];
|
|
46833
46894
|
const insetLabelSeeds = [];
|
|
46834
|
-
if (resolved.projection === "albers-usa" && usLayer) {
|
|
46895
|
+
if (resolved.projection === "albers-usa" && usLayer && !resolved.directives.noInsets) {
|
|
46835
46896
|
const PAD = 8;
|
|
46836
46897
|
const GAP = 12;
|
|
46837
46898
|
const yB = height - FIT_PAD;
|
|
@@ -46862,38 +46923,14 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46862
46923
|
}
|
|
46863
46924
|
return y;
|
|
46864
46925
|
};
|
|
46865
|
-
const
|
|
46926
|
+
const coastFloor = (x0, xr) => {
|
|
46866
46927
|
const n = 24;
|
|
46867
|
-
const pts = [];
|
|
46868
46928
|
let maxY = -Infinity;
|
|
46869
46929
|
for (let i = 0; i <= n; i++) {
|
|
46870
|
-
const
|
|
46871
|
-
|
|
46872
|
-
|
|
46873
|
-
|
|
46874
|
-
if (y > maxY) maxY = y;
|
|
46875
|
-
}
|
|
46876
|
-
}
|
|
46877
|
-
if (pts.length === 0) return () => yB - height * 0.42;
|
|
46878
|
-
let m = 0;
|
|
46879
|
-
if (pts.length >= 2) {
|
|
46880
|
-
let sx = 0, sy = 0, sxx = 0, sxy = 0;
|
|
46881
|
-
for (const [x, y] of pts) {
|
|
46882
|
-
sx += x;
|
|
46883
|
-
sy += y;
|
|
46884
|
-
sxx += x * x;
|
|
46885
|
-
sxy += x * y;
|
|
46886
|
-
}
|
|
46887
|
-
const den = pts.length * sxx - sx * sx;
|
|
46888
|
-
if (den !== 0) m = (pts.length * sxy - sx * sy) / den;
|
|
46889
|
-
}
|
|
46890
|
-
m = Math.max(-0.35, Math.min(0.35, m));
|
|
46891
|
-
let c = -Infinity;
|
|
46892
|
-
for (const [x, y] of pts) {
|
|
46893
|
-
const need = y - m * x + GAP;
|
|
46894
|
-
if (need > c) c = need;
|
|
46895
|
-
}
|
|
46896
|
-
return (x) => m * x + c;
|
|
46930
|
+
const y = at(x0 + (xr - x0) * i / n);
|
|
46931
|
+
if (y > maxY) maxY = y;
|
|
46932
|
+
}
|
|
46933
|
+
return maxY;
|
|
46897
46934
|
};
|
|
46898
46935
|
const placeInset = (iso, proj, boxX, iwReq) => {
|
|
46899
46936
|
const f = usLayer.get(iso);
|
|
@@ -46902,19 +46939,15 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46902
46939
|
const iw = Math.min(iwReq, width - FIT_PAD - x0 - 2 * PAD);
|
|
46903
46940
|
if (iw < 24) return boxX;
|
|
46904
46941
|
const xr = x0 + iw + 2 * PAD;
|
|
46905
|
-
const
|
|
46906
|
-
const
|
|
46907
|
-
const yR = top(xr);
|
|
46942
|
+
const floor = coastFloor(x0, xr);
|
|
46943
|
+
const topGuess = floor > -Infinity ? floor + GAP : yB - height * 0.42;
|
|
46908
46944
|
proj.fitWidth(iw, f);
|
|
46909
46945
|
const bb = geoPath(proj).bounds(f);
|
|
46910
46946
|
const sh = Number.isFinite(bb[0][0]) ? bb[1][1] - bb[0][1] : iw;
|
|
46911
46947
|
const needH = sh + 2 * PAD;
|
|
46912
|
-
let topFit =
|
|
46948
|
+
let topFit = topGuess;
|
|
46913
46949
|
const bottom = Math.min(topFit + needH, yB);
|
|
46914
46950
|
if (bottom - topFit < needH) topFit = bottom - needH;
|
|
46915
|
-
const lift = topFit - Math.max(yL, yR);
|
|
46916
|
-
const topL = yL + lift;
|
|
46917
|
-
const topR = yR + lift;
|
|
46918
46951
|
proj.fitExtent(
|
|
46919
46952
|
[
|
|
46920
46953
|
[x0 + PAD, topFit + PAD],
|
|
@@ -46933,15 +46966,18 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
46933
46966
|
}
|
|
46934
46967
|
insets.push({
|
|
46935
46968
|
x: x0,
|
|
46936
|
-
y:
|
|
46969
|
+
y: topFit,
|
|
46937
46970
|
w: xr - x0,
|
|
46938
|
-
h: bottom -
|
|
46971
|
+
h: bottom - topFit,
|
|
46939
46972
|
points: [
|
|
46940
|
-
[x0,
|
|
46941
|
-
[xr,
|
|
46973
|
+
[x0, topFit],
|
|
46974
|
+
[xr, topFit],
|
|
46942
46975
|
[xr, bottom],
|
|
46943
46976
|
[x0, bottom]
|
|
46944
|
-
]
|
|
46977
|
+
],
|
|
46978
|
+
// The FITTED inset projection (just fit to this box) — captured so the
|
|
46979
|
+
// geo-query can invert pixels inside the frame back to AK/HI coords.
|
|
46980
|
+
projection: proj
|
|
46945
46981
|
});
|
|
46946
46982
|
insetRegions.push({
|
|
46947
46983
|
id: iso,
|
|
@@ -47111,13 +47147,40 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47111
47147
|
id: "lake",
|
|
47112
47148
|
d,
|
|
47113
47149
|
fill: water,
|
|
47114
|
-
stroke:
|
|
47150
|
+
stroke: lakeStroke,
|
|
47115
47151
|
lineNumber: -1,
|
|
47116
47152
|
layer: "base"
|
|
47117
47153
|
});
|
|
47118
47154
|
}
|
|
47119
47155
|
}
|
|
47120
|
-
const
|
|
47156
|
+
const relief = [];
|
|
47157
|
+
let reliefHatch = null;
|
|
47158
|
+
if (resolved.directives.relief === true && data.mountainRanges) {
|
|
47159
|
+
for (const [, f] of decodeLayer(data.mountainRanges)) {
|
|
47160
|
+
const viewF = isGlobalView ? dropFrameFillers(f) : cullFeatureToView(f);
|
|
47161
|
+
if (!viewF) continue;
|
|
47162
|
+
const area2 = path.area(viewF);
|
|
47163
|
+
if (!Number.isFinite(area2) || area2 < RELIEF_MIN_AREA) continue;
|
|
47164
|
+
const box = path.bounds(viewF);
|
|
47165
|
+
if (box[1][0] - box[0][0] < RELIEF_MIN_DIM || box[1][1] - box[0][1] < RELIEF_MIN_DIM)
|
|
47166
|
+
continue;
|
|
47167
|
+
const d = path(viewF) ?? "";
|
|
47168
|
+
if (!d) continue;
|
|
47169
|
+
relief.push({ d });
|
|
47170
|
+
}
|
|
47171
|
+
if (relief.length) {
|
|
47172
|
+
const darkTone = isDark ? palette.bg : palette.text;
|
|
47173
|
+
const lightTone = isDark ? palette.text : palette.bg;
|
|
47174
|
+
const landLum = relativeLuminance(neutralFill);
|
|
47175
|
+
const tone = Math.abs(landLum - relativeLuminance(darkTone)) > 0.04 ? darkTone : lightTone;
|
|
47176
|
+
reliefHatch = {
|
|
47177
|
+
color: mix(tone, neutralFill, RELIEF_HATCH_STRENGTH),
|
|
47178
|
+
spacing: RELIEF_HATCH_SPACING,
|
|
47179
|
+
width: RELIEF_HATCH_WIDTH
|
|
47180
|
+
};
|
|
47181
|
+
}
|
|
47182
|
+
}
|
|
47183
|
+
const riverColor = mix(water, regionStroke, 16);
|
|
47121
47184
|
const rivers = [];
|
|
47122
47185
|
if (data.rivers) {
|
|
47123
47186
|
for (const [, f] of decodeLayer(data.rivers)) {
|
|
@@ -47138,6 +47201,9 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47138
47201
|
return R_MIN + Math.max(0, Math.min(1, t)) * (R_MAX - R_MIN);
|
|
47139
47202
|
};
|
|
47140
47203
|
const poiFill = (p) => {
|
|
47204
|
+
const directHex = p.color ? resolveColor(p.color, palette) : null;
|
|
47205
|
+
if (directHex)
|
|
47206
|
+
return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
|
|
47141
47207
|
for (const group of resolved.tagGroups) {
|
|
47142
47208
|
const val = p.tags[group.name.toLowerCase()];
|
|
47143
47209
|
if (!val) continue;
|
|
@@ -47553,19 +47619,24 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
47553
47619
|
...resolved.caption !== void 0 && { caption: resolved.caption },
|
|
47554
47620
|
regions,
|
|
47555
47621
|
rivers,
|
|
47622
|
+
relief,
|
|
47623
|
+
reliefHatch,
|
|
47556
47624
|
legs,
|
|
47557
47625
|
pois,
|
|
47558
47626
|
labels,
|
|
47559
47627
|
legend,
|
|
47560
47628
|
insets,
|
|
47561
|
-
insetRegions
|
|
47629
|
+
insetRegions,
|
|
47630
|
+
projection,
|
|
47631
|
+
stretch: stretchParams
|
|
47562
47632
|
};
|
|
47563
47633
|
}
|
|
47564
|
-
var FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK,
|
|
47634
|
+
var FIT_PAD, RAMP_FLOOR, R_DEFAULT, R_MIN, R_MAX, W_MIN, W_MAX, FONT, COLO_EPS, LAND_TINT_LIGHT, LAND_TINT_DARK, TAG_TINT_LIGHT, TAG_TINT_DARK, WATER_TINT_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;
|
|
47565
47635
|
var init_layout15 = __esm({
|
|
47566
47636
|
"src/map/layout.ts"() {
|
|
47567
47637
|
"use strict";
|
|
47568
47638
|
init_color_utils();
|
|
47639
|
+
init_colors();
|
|
47569
47640
|
init_label_layout();
|
|
47570
47641
|
init_legend_constants();
|
|
47571
47642
|
init_title_constants();
|
|
@@ -47578,19 +47649,22 @@ var init_layout15 = __esm({
|
|
|
47578
47649
|
W_MAX = 8;
|
|
47579
47650
|
FONT = 11;
|
|
47580
47651
|
COLO_EPS = 1.5;
|
|
47581
|
-
LAND_TINT_LIGHT =
|
|
47582
|
-
LAND_TINT_DARK =
|
|
47652
|
+
LAND_TINT_LIGHT = 12;
|
|
47653
|
+
LAND_TINT_DARK = 24;
|
|
47583
47654
|
TAG_TINT_LIGHT = 60;
|
|
47584
47655
|
TAG_TINT_DARK = 68;
|
|
47585
|
-
|
|
47656
|
+
WATER_TINT_LIGHT = 13;
|
|
47657
|
+
WATER_TINT_DARK = 14;
|
|
47586
47658
|
RIVER_WIDTH = 1.3;
|
|
47659
|
+
RELIEF_MIN_AREA = 12;
|
|
47660
|
+
RELIEF_MIN_DIM = 2;
|
|
47661
|
+
RELIEF_HATCH_SPACING = 3;
|
|
47662
|
+
RELIEF_HATCH_WIDTH = 0.25;
|
|
47663
|
+
RELIEF_HATCH_STRENGTH = 32;
|
|
47587
47664
|
FOREIGN_TINT_LIGHT = 30;
|
|
47588
47665
|
FOREIGN_TINT_DARK = 62;
|
|
47589
|
-
MUTED_WATER_LIGHT = 14;
|
|
47590
|
-
MUTED_WATER_DARK = 10;
|
|
47591
47666
|
MUTED_FOREIGN_LIGHT = 28;
|
|
47592
47667
|
MUTED_FOREIGN_DARK = 16;
|
|
47593
|
-
MUTED_LAND_DARK = 24;
|
|
47594
47668
|
COLO_R = 9;
|
|
47595
47669
|
GOLDEN_ANGLE = 2.399963229728653;
|
|
47596
47670
|
FAN_STEP = 16;
|
|
@@ -47663,6 +47737,20 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47663
47737
|
}
|
|
47664
47738
|
};
|
|
47665
47739
|
for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
|
|
47740
|
+
if (layout.relief.length && layout.reliefHatch) {
|
|
47741
|
+
const h = layout.reliefHatch;
|
|
47742
|
+
const rangeClipId = "dgmo-relief-clip";
|
|
47743
|
+
const landClipId = "dgmo-relief-land";
|
|
47744
|
+
const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
|
|
47745
|
+
for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
|
|
47746
|
+
const landClip = defs.append("clipPath").attr("id", landClipId);
|
|
47747
|
+
for (const r of layout.regions)
|
|
47748
|
+
if (r.id !== "lake") landClip.append("path").attr("d", r.d);
|
|
47749
|
+
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");
|
|
47750
|
+
for (let y = h.spacing; y < height; y += h.spacing) {
|
|
47751
|
+
gRelief.append("line").attr("x1", 0).attr("y1", y).attr("x2", width).attr("y2", y);
|
|
47752
|
+
}
|
|
47753
|
+
}
|
|
47666
47754
|
if (layout.rivers.length) {
|
|
47667
47755
|
const gRivers = svg.append("g").attr("class", "dgmo-map-rivers").attr("fill", "none");
|
|
47668
47756
|
for (const r of layout.rivers) {
|
|
@@ -47787,7 +47875,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
|
|
|
47787
47875
|
}
|
|
47788
47876
|
}
|
|
47789
47877
|
if (layout.title) {
|
|
47790
|
-
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);
|
|
47878
|
+
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);
|
|
47791
47879
|
}
|
|
47792
47880
|
if (layout.subtitle) {
|
|
47793
47881
|
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);
|
|
@@ -47819,6 +47907,120 @@ var init_renderer16 = __esm({
|
|
|
47819
47907
|
}
|
|
47820
47908
|
});
|
|
47821
47909
|
|
|
47910
|
+
// src/map/load-data.ts
|
|
47911
|
+
var load_data_exports = {};
|
|
47912
|
+
__export(load_data_exports, {
|
|
47913
|
+
loadMapData: () => loadMapData
|
|
47914
|
+
});
|
|
47915
|
+
async function loadNodeBuiltins() {
|
|
47916
|
+
const [{ readFile }, { fileURLToPath }, { dirname: dirname2, resolve }] = await Promise.all([
|
|
47917
|
+
import("fs/promises"),
|
|
47918
|
+
import("url"),
|
|
47919
|
+
import("path")
|
|
47920
|
+
]);
|
|
47921
|
+
return { readFile, fileURLToPath, dirname: dirname2, resolve };
|
|
47922
|
+
}
|
|
47923
|
+
async function readJson(nb, dir, name) {
|
|
47924
|
+
return JSON.parse(await nb.readFile(nb.resolve(dir, name), "utf8"));
|
|
47925
|
+
}
|
|
47926
|
+
async function firstExistingDir(nb, baseDir) {
|
|
47927
|
+
for (const rel of CANDIDATE_DIRS) {
|
|
47928
|
+
const dir = nb.resolve(baseDir, rel);
|
|
47929
|
+
try {
|
|
47930
|
+
await nb.readFile(nb.resolve(dir, FILES.gazetteer), "utf8");
|
|
47931
|
+
return dir;
|
|
47932
|
+
} catch {
|
|
47933
|
+
}
|
|
47934
|
+
}
|
|
47935
|
+
throw new Error(
|
|
47936
|
+
`map data assets not found near ${baseDir} (looked in ${CANDIDATE_DIRS.join(", ")}). Run \`pnpm build:map-data\` and \`pnpm build\`.`
|
|
47937
|
+
);
|
|
47938
|
+
}
|
|
47939
|
+
function validate(data) {
|
|
47940
|
+
const topoOk = (t) => !!t && t.type === "Topology" && !!t.objects;
|
|
47941
|
+
if (!topoOk(data.worldCoarse) || !topoOk(data.worldDetail) || !topoOk(data.usStates) || !data.gazetteer || !Array.isArray(data.gazetteer.cities) || !data.gazetteer.byName) {
|
|
47942
|
+
throw new Error("map data assets are malformed (failed shape validation)");
|
|
47943
|
+
}
|
|
47944
|
+
return data;
|
|
47945
|
+
}
|
|
47946
|
+
function moduleBaseDir(nb) {
|
|
47947
|
+
try {
|
|
47948
|
+
const url = import.meta.url;
|
|
47949
|
+
if (url) return nb.dirname(nb.fileURLToPath(url));
|
|
47950
|
+
} catch {
|
|
47951
|
+
}
|
|
47952
|
+
if (typeof __dirname !== "undefined") return __dirname;
|
|
47953
|
+
return process.cwd();
|
|
47954
|
+
}
|
|
47955
|
+
function loadMapData() {
|
|
47956
|
+
cache ??= (async () => {
|
|
47957
|
+
const nb = await loadNodeBuiltins();
|
|
47958
|
+
const dir = await firstExistingDir(nb, moduleBaseDir(nb));
|
|
47959
|
+
const [
|
|
47960
|
+
worldCoarse,
|
|
47961
|
+
worldDetail,
|
|
47962
|
+
usStates,
|
|
47963
|
+
lakes,
|
|
47964
|
+
rivers,
|
|
47965
|
+
mountainRanges,
|
|
47966
|
+
naLand,
|
|
47967
|
+
naLakes,
|
|
47968
|
+
gazetteer
|
|
47969
|
+
] = await Promise.all([
|
|
47970
|
+
readJson(nb, dir, FILES.worldCoarse),
|
|
47971
|
+
readJson(nb, dir, FILES.worldDetail),
|
|
47972
|
+
readJson(nb, dir, FILES.usStates),
|
|
47973
|
+
// Lakes/rivers/mountain/NA assets are optional — older bundles may predate them.
|
|
47974
|
+
readJson(nb, dir, FILES.lakes).catch(() => void 0),
|
|
47975
|
+
readJson(nb, dir, FILES.rivers).catch(() => void 0),
|
|
47976
|
+
readJson(nb, dir, FILES.mountainRanges).catch(
|
|
47977
|
+
() => void 0
|
|
47978
|
+
),
|
|
47979
|
+
readJson(nb, dir, FILES.naLand).catch(() => void 0),
|
|
47980
|
+
readJson(nb, dir, FILES.naLakes).catch(() => void 0),
|
|
47981
|
+
readJson(nb, dir, FILES.gazetteer)
|
|
47982
|
+
]);
|
|
47983
|
+
return validate({
|
|
47984
|
+
worldCoarse,
|
|
47985
|
+
worldDetail,
|
|
47986
|
+
usStates,
|
|
47987
|
+
gazetteer,
|
|
47988
|
+
...lakes && { lakes },
|
|
47989
|
+
...rivers && { rivers },
|
|
47990
|
+
...mountainRanges && { mountainRanges },
|
|
47991
|
+
...naLand && { naLand },
|
|
47992
|
+
...naLakes && { naLakes }
|
|
47993
|
+
});
|
|
47994
|
+
})().catch((e) => {
|
|
47995
|
+
cache = void 0;
|
|
47996
|
+
throw e;
|
|
47997
|
+
});
|
|
47998
|
+
return cache;
|
|
47999
|
+
}
|
|
48000
|
+
var FILES, CANDIDATE_DIRS, cache;
|
|
48001
|
+
var init_load_data = __esm({
|
|
48002
|
+
"src/map/load-data.ts"() {
|
|
48003
|
+
"use strict";
|
|
48004
|
+
FILES = {
|
|
48005
|
+
worldCoarse: "world-coarse.json",
|
|
48006
|
+
worldDetail: "world-detail.json",
|
|
48007
|
+
usStates: "us-states.json",
|
|
48008
|
+
lakes: "lakes.json",
|
|
48009
|
+
rivers: "rivers.json",
|
|
48010
|
+
mountainRanges: "mountain-ranges.json",
|
|
48011
|
+
naLand: "na-land.json",
|
|
48012
|
+
naLakes: "na-lakes.json",
|
|
48013
|
+
gazetteer: "gazetteer.json"
|
|
48014
|
+
};
|
|
48015
|
+
CANDIDATE_DIRS = [
|
|
48016
|
+
"./data",
|
|
48017
|
+
"./map-data",
|
|
48018
|
+
"../map-data",
|
|
48019
|
+
"../src/map/data"
|
|
48020
|
+
];
|
|
48021
|
+
}
|
|
48022
|
+
});
|
|
48023
|
+
|
|
47822
48024
|
// src/pyramid/renderer.ts
|
|
47823
48025
|
var renderer_exports17 = {};
|
|
47824
48026
|
__export(renderer_exports17, {
|
|
@@ -56003,15 +56205,17 @@ async function renderForExport(content, theme, palette, viewState, options) {
|
|
|
56003
56205
|
if (detectedType === "map") {
|
|
56004
56206
|
const { parseMap: parseMap2 } = await Promise.resolve().then(() => (init_parser12(), parser_exports11));
|
|
56005
56207
|
const { resolveMap: resolveMap2 } = await Promise.resolve().then(() => (init_resolver2(), resolver_exports));
|
|
56006
|
-
const { loadMapData: loadMapData2 } = await Promise.resolve().then(() => (init_load_data(), load_data_exports));
|
|
56007
56208
|
const { renderMapForExport: renderMapForExport2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
|
|
56008
56209
|
const effectivePalette2 = await resolveExportPalette(theme, palette);
|
|
56009
56210
|
const mapParsed = parseMap2(content);
|
|
56010
|
-
let mapData;
|
|
56011
|
-
|
|
56012
|
-
|
|
56013
|
-
|
|
56014
|
-
|
|
56211
|
+
let mapData = options?.mapData;
|
|
56212
|
+
if (!mapData) {
|
|
56213
|
+
const { loadMapData: loadMapData2 } = await Promise.resolve().then(() => (init_load_data(), load_data_exports));
|
|
56214
|
+
try {
|
|
56215
|
+
mapData = await loadMapData2();
|
|
56216
|
+
} catch {
|
|
56217
|
+
return "";
|
|
56218
|
+
}
|
|
56015
56219
|
}
|
|
56016
56220
|
const mapResolved = resolveMap2(mapParsed, mapData);
|
|
56017
56221
|
const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
@@ -57305,6 +57509,160 @@ init_load_data();
|
|
|
57305
57509
|
init_layout15();
|
|
57306
57510
|
init_renderer16();
|
|
57307
57511
|
|
|
57512
|
+
// src/map/geo-query.ts
|
|
57513
|
+
init_parser12();
|
|
57514
|
+
init_resolver2();
|
|
57515
|
+
init_layout15();
|
|
57516
|
+
init_geo();
|
|
57517
|
+
|
|
57518
|
+
// src/map/invert.ts
|
|
57519
|
+
function inInsetFrame(inset, px, py) {
|
|
57520
|
+
return px >= inset.x && px <= inset.x + inset.w && py >= inset.y && py <= inset.y + inset.h;
|
|
57521
|
+
}
|
|
57522
|
+
function unstretch(layout, px, py) {
|
|
57523
|
+
const s = layout.stretch;
|
|
57524
|
+
return [
|
|
57525
|
+
s.bx0 + (s.sx !== 0 ? (px - s.ox) / s.sx : 0),
|
|
57526
|
+
s.by0 + (s.sy !== 0 ? (py - s.oy) / s.sy : 0)
|
|
57527
|
+
];
|
|
57528
|
+
}
|
|
57529
|
+
function applyStretch(layout, x, y) {
|
|
57530
|
+
const s = layout.stretch;
|
|
57531
|
+
return [s.ox + (x - s.bx0) * s.sx, s.oy + (y - s.by0) * s.sy];
|
|
57532
|
+
}
|
|
57533
|
+
function pixelToLonLat(layout, px, py) {
|
|
57534
|
+
for (const inset of layout.insets) {
|
|
57535
|
+
if (inInsetFrame(inset, px, py)) {
|
|
57536
|
+
const ll2 = inset.projection.invert?.([px, py]);
|
|
57537
|
+
return ll2 && Number.isFinite(ll2[0]) && Number.isFinite(ll2[1]) ? [ll2[0], ll2[1]] : null;
|
|
57538
|
+
}
|
|
57539
|
+
}
|
|
57540
|
+
const [x, y] = layout.stretch ? unstretch(layout, px, py) : [px, py];
|
|
57541
|
+
const ll = layout.projection.invert?.([x, y]);
|
|
57542
|
+
return ll && Number.isFinite(ll[0]) && Number.isFinite(ll[1]) ? [ll[0], ll[1]] : null;
|
|
57543
|
+
}
|
|
57544
|
+
function lonLatToPixel(layout, lonLat) {
|
|
57545
|
+
const pt = [lonLat[0], lonLat[1]];
|
|
57546
|
+
const main = layout.projection(pt);
|
|
57547
|
+
const mainPx = main && Number.isFinite(main[0]) && Number.isFinite(main[1]) ? layout.stretch ? applyStretch(layout, main[0], main[1]) : [main[0], main[1]] : null;
|
|
57548
|
+
const onCanvas = !!mainPx && mainPx[0] >= 0 && mainPx[0] <= layout.width && mainPx[1] >= 0 && mainPx[1] <= layout.height;
|
|
57549
|
+
if (onCanvas) return mainPx;
|
|
57550
|
+
for (const inset of layout.insets) {
|
|
57551
|
+
const p = inset.projection(pt);
|
|
57552
|
+
if (p && Number.isFinite(p[0]) && Number.isFinite(p[1]) && inInsetFrame(inset, p[0], p[1]))
|
|
57553
|
+
return [p[0], p[1]];
|
|
57554
|
+
}
|
|
57555
|
+
return mainPx;
|
|
57556
|
+
}
|
|
57557
|
+
|
|
57558
|
+
// src/map/geo-query.ts
|
|
57559
|
+
var EARTH_R_KM = 6371;
|
|
57560
|
+
var DEG = Math.PI / 180;
|
|
57561
|
+
function haversineKm(lat1, lon1, lat2, lon2) {
|
|
57562
|
+
const dLat = (lat2 - lat1) * DEG;
|
|
57563
|
+
const dLon = (lon2 - lon1) * DEG;
|
|
57564
|
+
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * DEG) * Math.cos(lat2 * DEG) * Math.sin(dLon / 2) ** 2;
|
|
57565
|
+
return 2 * EARTH_R_KM * Math.asin(Math.min(1, Math.sqrt(a)));
|
|
57566
|
+
}
|
|
57567
|
+
var POP_PULL_KM = 12;
|
|
57568
|
+
function nearestCity(lonLat, gazetteer) {
|
|
57569
|
+
const [lon, lat] = lonLat;
|
|
57570
|
+
let best = null;
|
|
57571
|
+
const cities = gazetteer.cities;
|
|
57572
|
+
for (let i = 0; i < cities.length; i++) {
|
|
57573
|
+
const c2 = cities[i];
|
|
57574
|
+
const dist = haversineKm(lat, lon, c2[0], c2[1]);
|
|
57575
|
+
const score = dist - POP_PULL_KM * Math.log10((c2[3] || 0) + 1);
|
|
57576
|
+
if (!best || score < best.score) best = { score, idx: i, dist };
|
|
57577
|
+
}
|
|
57578
|
+
if (!best) return null;
|
|
57579
|
+
const c = cities[best.idx];
|
|
57580
|
+
return {
|
|
57581
|
+
name: c[4],
|
|
57582
|
+
iso: c[2],
|
|
57583
|
+
...c[5] !== void 0 && { sub: c[5] },
|
|
57584
|
+
distanceKm: best.dist
|
|
57585
|
+
};
|
|
57586
|
+
}
|
|
57587
|
+
function roundCoord(n) {
|
|
57588
|
+
return Number(n.toFixed(2));
|
|
57589
|
+
}
|
|
57590
|
+
function buildTokens(lonLat, region, city) {
|
|
57591
|
+
const coordPoiLine = `poi ${roundCoord(lonLat[1])} ${roundCoord(lonLat[0])}`;
|
|
57592
|
+
let stateTok = null;
|
|
57593
|
+
if (region.state) {
|
|
57594
|
+
const { iso, name } = region.state;
|
|
57595
|
+
stateTok = { primary: `${name} ${iso}`, alternates: [iso, name] };
|
|
57596
|
+
}
|
|
57597
|
+
let countryTok = null;
|
|
57598
|
+
if (region.country) {
|
|
57599
|
+
const { iso, name } = region.country;
|
|
57600
|
+
countryTok = { primary: name, alternates: [iso] };
|
|
57601
|
+
}
|
|
57602
|
+
let cityTok = null;
|
|
57603
|
+
if (city) {
|
|
57604
|
+
const scope = city.sub ?? (city.iso || "");
|
|
57605
|
+
cityTok = scope ? { token: `poi ${city.name} ${scope}`, ambiguous: false } : { token: `poi ${city.name}`, ambiguous: true };
|
|
57606
|
+
}
|
|
57607
|
+
return { coordPoiLine, state: stateTok, country: countryTok, city: cityTok };
|
|
57608
|
+
}
|
|
57609
|
+
var MAX_CITY_DOTS = 250;
|
|
57610
|
+
function createMapGeoQuery(opts) {
|
|
57611
|
+
const { content, width, height, data, palette, isDark } = opts;
|
|
57612
|
+
const resolved = resolveMap(parseMap(content), data);
|
|
57613
|
+
const layout = layoutMap(
|
|
57614
|
+
resolved,
|
|
57615
|
+
data,
|
|
57616
|
+
{ width, height },
|
|
57617
|
+
{ palette, isDark }
|
|
57618
|
+
);
|
|
57619
|
+
const countries = decodeFeatures(data.worldDetail);
|
|
57620
|
+
const states = decodeFeatures(data.usStates);
|
|
57621
|
+
const gazetteer = data.gazetteer;
|
|
57622
|
+
const invert = (px, py) => pixelToLonLat(layout, px, py);
|
|
57623
|
+
const project = (lonLat) => lonLatToPixel(layout, lonLat);
|
|
57624
|
+
const locate = (px, py) => {
|
|
57625
|
+
const lonLat = invert(px, py);
|
|
57626
|
+
if (!lonLat) return null;
|
|
57627
|
+
const region = regionAt(lonLat, countries, states);
|
|
57628
|
+
const city = nearestCity(lonLat, gazetteer);
|
|
57629
|
+
return {
|
|
57630
|
+
lonLat,
|
|
57631
|
+
country: region.country,
|
|
57632
|
+
state: region.state,
|
|
57633
|
+
nearestCity: city,
|
|
57634
|
+
tokens: buildTokens(lonLat, region, city)
|
|
57635
|
+
};
|
|
57636
|
+
};
|
|
57637
|
+
const cities = (extent2) => {
|
|
57638
|
+
const sorted = [...gazetteer.cities].sort((a, b) => b[3] - a[3]);
|
|
57639
|
+
const out = [];
|
|
57640
|
+
for (const c of sorted) {
|
|
57641
|
+
const [lat, lon, iso, pop, name, sub] = c;
|
|
57642
|
+
if (extent2) {
|
|
57643
|
+
const [[w, s], [e, n]] = extent2;
|
|
57644
|
+
if (lon < w || lon > e || lat < s || lat > n) continue;
|
|
57645
|
+
}
|
|
57646
|
+
const p = project([lon, lat]);
|
|
57647
|
+
if (!p) continue;
|
|
57648
|
+
if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
|
|
57649
|
+
out.push({
|
|
57650
|
+
name,
|
|
57651
|
+
iso,
|
|
57652
|
+
...sub !== void 0 && { sub },
|
|
57653
|
+
lon,
|
|
57654
|
+
lat,
|
|
57655
|
+
px: p[0],
|
|
57656
|
+
py: p[1],
|
|
57657
|
+
pop
|
|
57658
|
+
});
|
|
57659
|
+
if (out.length >= MAX_CITY_DOTS) break;
|
|
57660
|
+
}
|
|
57661
|
+
return out;
|
|
57662
|
+
};
|
|
57663
|
+
return { invert, project, locate, cities };
|
|
57664
|
+
}
|
|
57665
|
+
|
|
57308
57666
|
// src/map/completion.ts
|
|
57309
57667
|
var fold2 = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
57310
57668
|
var groupThousands = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
@@ -58457,6 +58815,7 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
|
|
|
58457
58815
|
"default-country": { description: "ISO scope for bare city resolution" },
|
|
58458
58816
|
"default-state": { description: "ISO subdivision scope" },
|
|
58459
58817
|
"no-legend": { description: "Suppress the legend" },
|
|
58818
|
+
relief: { description: "Subtle mountain-range relief shading" },
|
|
58460
58819
|
subtitle: { description: "Subtitle line" },
|
|
58461
58820
|
caption: { description: "Caption line" }
|
|
58462
58821
|
})
|
|
@@ -59967,6 +60326,7 @@ export {
|
|
|
59967
60326
|
computeTimeTicks,
|
|
59968
60327
|
contrastText,
|
|
59969
60328
|
controlsGroupCapsuleWidth,
|
|
60329
|
+
createMapGeoQuery,
|
|
59970
60330
|
decodeDiagramUrl,
|
|
59971
60331
|
decodeViewState,
|
|
59972
60332
|
displayName,
|