aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/viewer/js/riverlod.js
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
committergodosa <godosa@godosa.eu>2026-10-07 00:14:38 +0200
commit3443c1c65e9f1753e1e656b35d08416c1fa298f2 (patch)
tree4e43236f460145a4d75d1b4616dcb7aa6ef08f51 /viewer/js/riverlod.js
downloadworldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.tar.gz
worldmap-viewer-3443c1c65e9f1753e1e656b35d08416c1fa298f2.zip
worldmap-viewer: initial public history
Diffstat (limited to 'viewer/js/riverlod.js')
-rw-r--r--viewer/js/riverlod.js32
1 files changed, 32 insertions, 0 deletions
diff --git a/viewer/js/riverlod.js b/viewer/js/riverlod.js
new file mode 100644
index 0000000..0581f1d
--- /dev/null
+++ b/viewer/js/riverlod.js
@@ -0,0 +1,32 @@
+// riverlod.js — which river orders the rivers overlay draws at a scale: those whose rivers lie far enough apart on
+// screen not to merge into a blue sheet. First-order channels lie about one cell step apart; order k's are as much
+// farther apart as their total length is shorter (rivers are lines: spacing ≈ area / length).
+import { distanceKm } from './geo.js';
+
+export const MIN_GAP_PX = 8;
+
+export function cellSpacingKm(res, radiusKm) { // mean distance between H3 cell centres at a resolution
+ return Math.sqrt((4 * Math.PI * radiusKm ** 2) / (2 + 120 * 7 ** res));
+}
+
+export function riverStats(fc) { // {length: {order: total km}} from rivers.geojson
+ const length = {};
+ for (const f of fc.features ?? []) {
+ const o = f.properties?.order ?? 1, c = f.geometry?.coordinates ?? [];
+ let km = 0;
+ for (let i = 1; i < c.length; i++) {
+ km += distanceKm({ lat: c[i - 1][1], lon: c[i - 1][0] }, { lat: c[i][1], lon: c[i][0] });
+ }
+ length[o] = (length[o] ?? 0) + km;
+ }
+ return { length };
+}
+
+export function riverMinOrder(kmPerPx, { length }, cellKm, minGap = MIN_GAP_PX) {
+ const orders = Object.keys(length).map(Number).filter(k => length[k] > 0).sort((a, b) => a - b);
+ if (!orders.length) return 1;
+ if (!(kmPerPx > 0) || !Number.isFinite(kmPerPx)) return orders.at(-1);
+ const l1 = length[orders[0]];
+ for (const k of orders) if ((cellKm * l1) / length[k] >= minGap * kmPerPx) return k;
+ return orders.at(-1); // whole planet: just the biggest
+}