diff --git a/README.md b/README.md
index 89a2f80..bc913cd 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ Given two spherical arcs [point0, point1] and [point2, point3], returns their in
## Projections
-d3-geo-polygon adds polygon clipping to the polyhedral projections from [d3-geo-projection](https://github.com/d3/d3-geo-projection). Thus, it supersedes the following symbols:
+d3-geo-polygon adds polygon clipping to the polyhedral and interrupted projections from [d3-geo-projection](https://github.com/d3/d3-geo-projection). Thus, it supersedes the following symbols:
# d3.geoPolyhedral(tree, face) · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/polyhedral/index.js), [Examples](https://observablehq.com/@fil/polyhedral-projections-with-d3-geo-polygon)
@@ -78,6 +78,61 @@ The Collignon butterfly projection.
A butterfly projection inspired by Steve Waterman’s design.
+# d3.geoBerghaus · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+The Berghaus projection.
+
+# d3.geoGingery · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+The Gingery projection.
+
+# d3.geoHealpix · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+The HEALPix projection.
+
+# d3.geoInterruptedBoggs · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+Bogg’s interrupted eumorphic projection.
+
+# d3.geoInterruptedHomolosine · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+Goode’s interrupted homolosine projection.
+
+# d3.geoInterruptedMollweide · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+Goode’s interrupted Mollweide projection.
+
+# d3.geoInterruptedMollweideHemispheres · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+The Mollweide projection interrupted into two (equal-area) hemispheres.
+
+# d3.geoInterruptedSinuMollweide · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+Alan K. Philbrick’s interrupted sinu-Mollweide projection.
+
+# d3.geoInterruptedSinusoidal · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js)
+
+[
](https://observablehq.com/@d3/interrupted-clipped)
+
+An interrupted sinusoidal projection with asymmetrical lobe boundaries.
+
+
New projections are introduced:
# d3.geoPolyhedralVoronoi([parents], [polygons], [faceProjection], [faceFind]) · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/polyhedral/voronoi.js)
diff --git a/package.json b/package.json
index 630568a..68c7dda 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,8 @@
"sideEffects": false,
"dependencies": {
"d3-array": "2.5.0 - 3",
- "d3-geo": "2 - 3"
+ "d3-geo": "2 - 3",
+ "d3-geo-projection": "4"
},
"devDependencies": {
"@rollup/plugin-terser": "0.4",
diff --git a/src/index.js b/src/index.js
index d30a047..92d605c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -17,3 +17,15 @@ export {default as geoRhombic} from "./rhombic.js";
export {default as geoDeltoidal} from "./deltoidal.js";
export {default as geoCahillKeyes, cahillKeyesRaw as geoCahillKeyesRaw} from "./cahillKeyes.js";
export {default as geoComplexLog, complexLogRaw as geoComplexLogRaw} from "./complexLog.js";
+export {
+ geoBerghaus,
+ geoGingery,
+ geoHealpix,
+ geoInterrupt,
+ geoInterruptedBoggs,
+ geoInterruptedHomolosine,
+ geoInterruptedMollweide,
+ geoInterruptedMollweideHemispheres,
+ geoInterruptedSinuMollweide,
+ geoInterruptedSinusoidal
+} from "./reclip.js";
diff --git a/src/reclip.js b/src/reclip.js
new file mode 100644
index 0000000..4e6c95c
--- /dev/null
+++ b/src/reclip.js
@@ -0,0 +1,99 @@
+import {merge} from "d3-array";
+import {geoInterpolate} from "d3-geo";
+import {
+ geoBerghaus as berghaus,
+ geoGingery as gingery,
+ geoHealpix as healpix,
+ geoInterrupt as interrupt,
+ geoInterruptedBoggs as interruptedBoggs,
+ geoInterruptedHomolosine as interruptedHomolosine,
+ geoInterruptedMollweide as interruptedMollweide,
+ geoInterruptedMollweideHemispheres as interruptedMollweideHemispheres,
+ geoInterruptedSinuMollweide as interruptedSinuMollweide,
+ geoInterruptedSinusoidal as interruptedSinusoidal,
+} from "d3-geo-projection";
+import geoClipPolygon from "./clip/polygon.js";
+
+/**
+ * Reclip projections from d3-geo-projection
+ */
+export function geoBerghaus() { return reclip(berghaus.apply(this, arguments)); }
+export function geoGingery() { return reclip(gingery.apply(this, arguments)); }
+export function geoHealpix() { return reclip(healpix.apply(this, arguments), true); }
+export function geoInterrupt() { return clipInterrupted(interrupt.apply(this, arguments)); }
+export function geoInterruptedBoggs() { return clipInterrupted(interruptedBoggs.apply(this, arguments)); }
+export function geoInterruptedHomolosine() { return clipInterrupted(interruptedHomolosine.apply(this, arguments)); }
+export function geoInterruptedMollweide() { return clipInterrupted(interruptedMollweide.apply(this, arguments)); }
+export function geoInterruptedMollweideHemispheres() { return clipInterrupted(interruptedMollweideHemispheres.apply(this, arguments)); }
+export function geoInterruptedSinuMollweide() { return clipInterrupted(interruptedSinuMollweide.apply(this, arguments)); }
+export function geoInterruptedSinusoidal() { return clipInterrupted(interruptedSinusoidal.apply(this, arguments)); }
+
+function reclip(projection, vertical = false) {
+ const {lobes} = projection;
+ function reset(projection) {
+ const rotate = projection.rotate();
+ const scale = projection.scale();
+ const translate = projection.translate();
+ projection.rotate([0, 0]).translate([0, 0]);
+ projection.lobes = function (_) {
+ return !arguments.length ? lobes() : reset(lobes(_));
+ };
+
+ projection.preclip((stream) => stream); // clipNone
+ const R = 1 - 1e-7;
+ const Rx = vertical ? 1 : R;
+ let points = [];
+ projection
+ .stream({
+ point(x, y) {
+ points.push([x * Rx, y * R]);
+ },
+ lineStart() {},
+ lineEnd() {},
+ polygonStart() {},
+ polygonEnd() {},
+ sphere() {},
+ })
+ .sphere();
+
+ projection.scale(scale);
+ points = points.map(projection.invert);
+ points.push(points[0]);
+
+ return projection
+ .rotate(rotate)
+ .translate(translate)
+ .preclip(geoClipPolygon({ type: "Polygon", coordinates: [points] }));
+ }
+ return reset(projection);
+}
+
+function clipInterrupted(projection) {
+ const { lobes } = projection;
+ function reset(projection) {
+ const l = lobes?.();
+ const polygon = merge(
+ Array.from(l, (d, i) => {
+ const hemisphere = d.flatMap(
+ (q) => Array.from(q, (p) => geoInterpolate(p, [q[1][0], 0])(1e-9)) // pull inside each lobe
+ );
+ return i === 0
+ ? hemisphere // north
+ : [...hemisphere].reverse();
+ })
+ );
+
+ projection.lobes = function (_) {
+ return !arguments.length ? lobes() : reset(lobes(_));
+ };
+
+ return projection.preclip(
+ geoClipPolygon({
+ type: "Polygon",
+ coordinates: [[...polygon, polygon[0]]],
+ })
+ );
+ }
+
+ return reset(projection);
+}
diff --git a/test/snapshots.js b/test/snapshots.js
index 248d07c..7fed37a 100644
--- a/test/snapshots.js
+++ b/test/snapshots.js
@@ -2,13 +2,24 @@ import { Canvas } from "canvas";
import { readFile } from "fs/promises";
import { feature } from "topojson-client";
import { geoGraticule, geoPath } from "d3-geo";
+import { geoHomolosineRaw } from "d3-geo-projection";
import {
geoAirocean,
+ geoBerghaus,
geoCox,
geoCahillKeyes,
geoComplexLog,
geoCubic,
geoDeltoidal,
+ geoGingery,
+ geoHealpix,
+ geoInterrupt,
+ geoInterruptedBoggs,
+ geoInterruptedHomolosine,
+ geoInterruptedMollweide,
+ geoInterruptedMollweideHemispheres,
+ geoInterruptedSinuMollweide,
+ geoInterruptedSinusoidal,
geoRhombic,
geoDodecahedral,
geoIcosahedral,
@@ -28,7 +39,9 @@ async function renderWorld(projection, { extent, clip = false } = {}) {
extent === undefined
? { type: "Sphere" }
: graticule.extent(extent).outline();
- const world = JSON.parse(await readFile("./node_modules/world-atlas/world/50m.json"));
+ const world = JSON.parse(
+ await readFile("./node_modules/world-atlas/world/50m.json")
+ );
const canvas = new Canvas(width, height);
const context = canvas.getContext("2d");
const path = geoPath(projection, context);
@@ -122,6 +135,106 @@ export async function tetrahedralLeeSouth() {
);
}
+// reclip
+export async function berghaus() {
+ return renderWorld(geoBerghaus());
+}
+
+export async function gingery() {
+ return renderWorld(geoGingery());
+}
+
+export async function berghaus7() {
+ return renderWorld(geoBerghaus().lobes(7).fitSize([960, 500], { type: "Sphere" }));
+}
+
+export async function berghaus13() {
+ return renderWorld(geoBerghaus().lobes(13).fitSize([960, 500], { type: "Sphere" }));
+}
+
+export async function gingery7() {
+ return renderWorld(geoGingery().lobes(7).fitSize([960, 500], { type: "Sphere" }));
+}
+
+export async function gingery3() {
+ return renderWorld(geoGingery().lobes(3).fitSize([960, 500], { type: "Sphere" }));
+}
+
+export async function goodeOcean() {
+ return renderWorld(
+ geoInterrupt(geoHomolosineRaw, [
+ [
+ [
+ [-180, 0],
+ [-130, 90],
+ [-95, 0],
+ ],
+ [
+ [-95, 0],
+ [-30, 90],
+ [55, 0],
+ ],
+ [
+ [55, 0],
+ [120, 90],
+ [180, 0],
+ ],
+ ],
+ [
+ [
+ [-180, 0],
+ [-120, -90],
+ [-60, 0],
+ ],
+ [
+ [-60, 0],
+ [20, -90],
+ [85, 0],
+ ],
+ [
+ [85, 0],
+ [140, -90],
+ [180, 0],
+ ],
+ ],
+ ])
+ .rotate([-204, 0])
+ .precision(0.1)
+ );
+}
+
+export async function interruptedBoggs() {
+ return renderWorld(geoInterruptedBoggs());
+}
+
+export async function healpix() {
+ return renderWorld(geoHealpix());
+}
+
+export async function healpix5() {
+ return renderWorld(geoHealpix().lobes(5));
+}
+
+export async function interruptedHomolosine() {
+ return renderWorld(geoInterruptedHomolosine());
+}
+
+export async function interruptedMollweide() {
+ return renderWorld(geoInterruptedMollweide());
+}
+
+export async function interruptedMollweideHemispheres() {
+ return renderWorld(geoInterruptedMollweideHemispheres());
+}
+
+export async function interruptedSinuMollweide() {
+ return renderWorld(geoInterruptedSinuMollweide());
+}
+
+export async function interruptedSinusoidal() {
+ return renderWorld(geoInterruptedSinusoidal());
+}
+
// more tests
// https://github.com/d3/d3-geo-polygon/issues/7
@@ -150,17 +263,19 @@ export async function rhombic00() {
// https://github.com/d3/d3-geo-polygon/issues/62
export async function rhombicHalf1() {
- return renderWorld(geoRhombic()
- .parents([-1, 0, 6, 2, 1, 9, 11, 3, 4, 8, 6, 10])
- .precision(0.1)
- .fitSize([960, 500], { type: "Sphere" })
+ return renderWorld(
+ geoRhombic()
+ .parents([-1, 0, 6, 2, 1, 9, 11, 3, 4, 8, 6, 10])
+ .precision(0.1)
+ .fitSize([960, 500], { type: "Sphere" })
);
}
export async function rhombicHalf2() {
- return renderWorld(geoRhombic()
- .parents([4, 0, 6, 2, 1, 9, 11, 3, 4, 8, -1, 10])
- .angle(-19.5)
- .precision(0.1)
- .fitSize([960, 500], { type: "Sphere" })
+ return renderWorld(
+ geoRhombic()
+ .parents([4, 0, 6, 2, 1, 9, 11, 3, 4, 8, -1, 10])
+ .angle(-19.5)
+ .precision(0.1)
+ .fitSize([960, 500], { type: "Sphere" })
);
}
diff --git a/test/snapshots/berghaus.png b/test/snapshots/berghaus.png
new file mode 100644
index 0000000..78bca4e
Binary files /dev/null and b/test/snapshots/berghaus.png differ
diff --git a/test/snapshots/berghaus13.png b/test/snapshots/berghaus13.png
new file mode 100644
index 0000000..51f47d0
Binary files /dev/null and b/test/snapshots/berghaus13.png differ
diff --git a/test/snapshots/berghaus7.png b/test/snapshots/berghaus7.png
new file mode 100644
index 0000000..6950b17
Binary files /dev/null and b/test/snapshots/berghaus7.png differ
diff --git a/test/snapshots/gingery.png b/test/snapshots/gingery.png
new file mode 100644
index 0000000..40379be
Binary files /dev/null and b/test/snapshots/gingery.png differ
diff --git a/test/snapshots/gingery3.png b/test/snapshots/gingery3.png
new file mode 100644
index 0000000..b26f8a0
Binary files /dev/null and b/test/snapshots/gingery3.png differ
diff --git a/test/snapshots/gingery7.png b/test/snapshots/gingery7.png
new file mode 100644
index 0000000..be16e20
Binary files /dev/null and b/test/snapshots/gingery7.png differ
diff --git a/test/snapshots/goodeOcean.png b/test/snapshots/goodeOcean.png
new file mode 100644
index 0000000..9dde739
Binary files /dev/null and b/test/snapshots/goodeOcean.png differ
diff --git a/test/snapshots/healpix.png b/test/snapshots/healpix.png
new file mode 100644
index 0000000..a4227d4
Binary files /dev/null and b/test/snapshots/healpix.png differ
diff --git a/test/snapshots/healpix5.png b/test/snapshots/healpix5.png
new file mode 100644
index 0000000..8100b1d
Binary files /dev/null and b/test/snapshots/healpix5.png differ
diff --git a/test/snapshots/interruptedBoggs.png b/test/snapshots/interruptedBoggs.png
new file mode 100644
index 0000000..0cd6419
Binary files /dev/null and b/test/snapshots/interruptedBoggs.png differ
diff --git a/test/snapshots/interruptedHomolosine.png b/test/snapshots/interruptedHomolosine.png
new file mode 100644
index 0000000..e03ed18
Binary files /dev/null and b/test/snapshots/interruptedHomolosine.png differ
diff --git a/test/snapshots/interruptedMollweide.png b/test/snapshots/interruptedMollweide.png
new file mode 100644
index 0000000..2d363c9
Binary files /dev/null and b/test/snapshots/interruptedMollweide.png differ
diff --git a/test/snapshots/interruptedMollweideHemispheres.png b/test/snapshots/interruptedMollweideHemispheres.png
new file mode 100644
index 0000000..1cea268
Binary files /dev/null and b/test/snapshots/interruptedMollweideHemispheres.png differ
diff --git a/test/snapshots/interruptedSinuMollweide.png b/test/snapshots/interruptedSinuMollweide.png
new file mode 100644
index 0000000..9350e4d
Binary files /dev/null and b/test/snapshots/interruptedSinuMollweide.png differ
diff --git a/test/snapshots/interruptedSinusoidal.png b/test/snapshots/interruptedSinusoidal.png
new file mode 100644
index 0000000..bff18cb
Binary files /dev/null and b/test/snapshots/interruptedSinusoidal.png differ
diff --git a/yarn.lock b/yarn.lock
index d083fe2..17fee9d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -338,6 +338,11 @@ commander@2, commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+commander@7:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -357,14 +362,23 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
-"d3-array@2.5.0 - 3":
+"d3-array@1 - 3", "d3-array@2.5.0 - 3":
version "3.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
dependencies:
internmap "1 - 2"
-"d3-geo@2 - 3":
+d3-geo-projection@4:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz#dc229e5ead78d31869a4e87cf1f45bd2716c48ca"
+ integrity sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==
+ dependencies:
+ commander "7"
+ d3-array "1 - 3"
+ d3-geo "1.12.0 - 3"
+
+"d3-geo@1.12.0 - 3", "d3-geo@2 - 3":
version "3.1.1"
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d"
integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==