From 6ca10b412156f83a1b049c2da4b949b07b478614 Mon Sep 17 00:00:00 2001 From: neocarto Date: Tue, 3 Dec 2024 13:15:42 +0100 Subject: [PATCH] projections wip --- src/projection/hoaxiaoguang.js | 16 +++++++ src/projection/polar.js | 12 +++++ src/projection/spilhaus.js | 76 ++++++++++++++++++++++++++++++++ src/projection/stringtod3proj.js | 20 +++++++++ 4 files changed, 124 insertions(+) create mode 100644 src/projection/hoaxiaoguang.js create mode 100644 src/projection/polar.js create mode 100644 src/projection/spilhaus.js create mode 100644 src/projection/stringtod3proj.js diff --git a/src/projection/hoaxiaoguang.js b/src/projection/hoaxiaoguang.js new file mode 100644 index 0000000..260983f --- /dev/null +++ b/src/projection/hoaxiaoguang.js @@ -0,0 +1,16 @@ +import { geoHufnagel } from "d3-geo-projection"; +const d3 = Object.assign({}, { geoHufnagel }); + +// Approximative projection of HoaXiaoguang (thanks to Fil/@recifs) + +export function HoaXiaoguang() { + const projection = d3 + .geoHufnagel() + .a(0.8) + .b(0.35) + .psiMax(50) + .ratio(1.6) + .angle(90) + .rotate([110, -200, 90]); + return projection; +} diff --git a/src/projection/polar.js b/src/projection/polar.js new file mode 100644 index 0000000..bca327d --- /dev/null +++ b/src/projection/polar.js @@ -0,0 +1,12 @@ +import { geoAzimuthalEquidistant } from "d3-geo"; +const d3 = Object.assign({}, { geoAzimuthalEquidistant }); + +export function Polar() { + const projection = d3 + .geoAzimuthalEquidistant() + .scale(190) + .rotate([0, -90]) + .clipAngle(150); + + return projection; +} diff --git a/src/projection/spilhaus.js b/src/projection/spilhaus.js new file mode 100644 index 0000000..ae2f1bf --- /dev/null +++ b/src/projection/spilhaus.js @@ -0,0 +1,76 @@ +// Thanks to Torben Jansen. +// See https://observablehq.com/@toja/spilhaus-world-ocean-map-in-a-square + +//import * as d3geo from "d3-geo"; +import { geoProjection } from "d3-geo"; +const d3 = Object.assign({}, { geoProjection }); + +function ellipticF(phi, m) { + const { abs, atan, ln, PI: pi, sin, sqrt } = Math; + const C1 = 10e-4, + C2 = 10e-10, + TOL = 10e-6; + const sp = sin(phi); + + let k = sqrt(1 - m), + h = sp * sp; + + // "complete" elliptic integral + if (h >= 1 || abs(phi) === pi / 2) { + if (k <= TOL) return sp < 0 ? -Infinity : Infinity; + (m = 1), (h = m), (m += k); + while (abs(h - k) > C1 * m) { + k = sqrt(h * k); + (m /= 2), (h = m), (m += k); + } + return sp < 0 ? -pi / m : pi / m; + } + // "incomplete" elliptic integral + else { + if (k <= TOL) return ln((1 + sp) / (1 - sp)) / 2; + let g, n, p, r, y; + (m = 1), (n = 0), (g = m), (p = m * k), (m += k); + y = sqrt((1 - h) / h); + if (abs((y -= p / y)) <= 0) y = C2 * sqrt(p); + while (abs(g - k) > C1 * g) { + (k = 2 * sqrt(p)), (n += n); + if (y < 0) n += 1; + (p = m * k), (g = m), (m += k); + if (abs((y -= p / y)) <= 0) y = C2 * sqrt(p); + } + if (y < 0) n += 1; + r = (atan(m / y) + pi * n) / m; + return sp < 0 ? -r : r; + } +} + +function ellipticFactory(a, b, sm, sn) { + let m = Math.asin(Math.sqrt(1 + Math.min(0, Math.cos(a + b)))); + if (sm) m = -m; + + let n = Math.asin(Math.sqrt(Math.abs(1 - Math.max(0, Math.cos(a - b))))); + if (sn) n = -n; + + return [ellipticF(m, 0.5), ellipticF(n, 0.5)]; +} + +//export function Spilhaus() { +const { abs, max, min, sin, cos, asin, acos, tan } = Math; + +function spilhausSquareRaw(lambda, phi) { + let a, b, sm, sn, xy; + const sp = tan(0.5 * phi); + a = cos(asin(sp)) * sin(0.5 * lambda); + sm = sp + a < 0; + sn = sp - a < 0; + b = acos(sp); + a = acos(a); + + return ellipticFactory(a, b, sm, sn); +} + +export function Spilhaus() { + return d3 + .geoProjection(spilhausSquareRaw) + .rotate([-66.94970198, 49.56371678, 40.17823482]); +} diff --git a/src/projection/stringtod3proj.js b/src/projection/stringtod3proj.js new file mode 100644 index 0000000..d6293e5 --- /dev/null +++ b/src/projection/stringtod3proj.js @@ -0,0 +1,20 @@ +import * as d3geo from "d3-geo"; +import * as d3geoprojection from "d3-geo-projection"; +const d3 = Object.assign({}, d3geo, d3geoprojection); + +export function stringtod3proj(string) { + let str = string; + str = str.replace(/\s/g, ""); + + if (str.substring(0, 6) !== "d3.geo") { + if (str.indexOf(".") === -1) { + str += "()"; + } else { + str = str.replace(".", "()."); + } + str = "d3.geo" + str; + } + + const createProjection = new Function("d3", `return (${str})`); + return createProjection(d3); +}