diff --git a/README.md b/README.md index f810e32..1f7c3dd 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,17 @@ Alan K. Philbrick’s interrupted sinu-Mollweide projection. An interrupted sinusoidal projection with asymmetrical lobe boundaries. +# d3.geoTwoPointEquidistant(point0, point1) · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js) + +The two-point equidistant projection, displaying 99.9996% of the sphere thanks to polygon clipping. + +# d3.geoTwoPointEquidistantUsa() · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/reclip.js) + +[](https://observablehq.com/@d3/two-point-equidistant) + +The two-point equidistant projection with points [-158°, 21.5°] and [-77°, 39°], approximately representing Honolulu, HI and Washington, D.C. + +### New projections New projections are introduced: diff --git a/src/index.js b/src/index.js index 92d605c..a77f1f1 100644 --- a/src/index.js +++ b/src/index.js @@ -27,5 +27,7 @@ export { geoInterruptedMollweide, geoInterruptedMollweideHemispheres, geoInterruptedSinuMollweide, - geoInterruptedSinusoidal + geoInterruptedSinusoidal, + geoTwoPointEquidistant, + geoTwoPointEquidistantUsa } from "./reclip.js"; diff --git a/src/reclip.js b/src/reclip.js index 4e6c95c..2edd499 100644 --- a/src/reclip.js +++ b/src/reclip.js @@ -1,5 +1,5 @@ import {merge} from "d3-array"; -import {geoInterpolate} from "d3-geo"; +import {geoDistance, geoInterpolate} from "d3-geo"; import { geoBerghaus as berghaus, geoGingery as gingery, @@ -11,6 +11,7 @@ import { geoInterruptedMollweideHemispheres as interruptedMollweideHemispheres, geoInterruptedSinuMollweide as interruptedSinuMollweide, geoInterruptedSinusoidal as interruptedSinusoidal, + geoTwoPointEquidistant as twoPointEquidistant } from "d3-geo-projection"; import geoClipPolygon from "./clip/polygon.js"; @@ -27,6 +28,8 @@ export function geoInterruptedMollweide() { return clipInterrupted(interruptedMo 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)); } +export function geoTwoPointEquidistant() { return clipTwoPointEquidistant.apply(this, arguments); } +export function geoTwoPointEquidistantUsa() { return geoTwoPointEquidistant([-158, 21.5], [-77, 39]); } function reclip(projection, vertical = false) { const {lobes} = projection; @@ -97,3 +100,20 @@ function clipInterrupted(projection) { return reset(projection); } + +function clipTwoPointEquidistant(a, b) { + const epsilon = 1e-3; + const u = geoDistance(a, b) * 90 / Math.PI + epsilon; + const ellipse = { + type: "Polygon", + coordinates: [[ + [180 - u, epsilon], + [180 - u, -epsilon], + [-180 + u, -epsilon], + [-180 + u, epsilon], + [180 - u, epsilon] + ] + ] + }; + return twoPointEquidistant(a, b).preclip(geoClipPolygon(ellipse).clipPoint(false)); +} diff --git a/test/snapshots.js b/test/snapshots.js index 9bb59df..f853c57 100644 --- a/test/snapshots.js +++ b/test/snapshots.js @@ -29,6 +29,7 @@ import { geoPolyhedralCollignon, geoPolyhedralWaterman, geoTetrahedralLee, + geoTwoPointEquidistantUsa } from "../src/index.js"; const width = 960; @@ -138,7 +139,7 @@ export async function tetrahedralLeeSouth() { .rotate([-30, 0]) .angle(-30) .precision(0.1) - .fitSize([960, 500], { type: "Sphere" }) + .fitSize([width, height], { type: "Sphere" }) ); } @@ -152,19 +153,19 @@ export async function gingery() { } export async function berghaus7() { - return renderWorld(geoBerghaus().lobes(7).fitSize([960, 500], { type: "Sphere" })); + return renderWorld(geoBerghaus().lobes(7).fitSize([width, height], { type: "Sphere" })); } export async function berghaus13() { - return renderWorld(geoBerghaus().lobes(13).fitSize([960, 500], { type: "Sphere" })); + return renderWorld(geoBerghaus().lobes(13).fitSize([width, height], { type: "Sphere" })); } export async function gingery7() { - return renderWorld(geoGingery().lobes(7).fitSize([960, 500], { type: "Sphere" })); + return renderWorld(geoGingery().lobes(7).fitSize([width, height], { type: "Sphere" })); } export async function gingery3() { - return renderWorld(geoGingery().lobes(3).fitSize([960, 500], { type: "Sphere" })); + return renderWorld(geoGingery().lobes(3).fitSize([width, height], { type: "Sphere" })); } export async function goodeOcean() { @@ -242,6 +243,13 @@ export async function interruptedSinusoidal() { return renderWorld(geoInterruptedSinusoidal()); } +// https://github.com/d3/d3-geo/issues/46 +export async function twoPointEquidistantUsa() { + return renderWorld( + geoTwoPointEquidistantUsa().fitSize([width, height], { type: "Sphere" }) + ); +} + // more tests // https://github.com/d3/d3-geo-polygon/issues/7 @@ -250,7 +258,7 @@ export async function cubic45() { geoCubic() .parents([-1, 2, 0, 2, 5, 2]) .rotate([0, 0, 45]) - .fitSize([960, 500], { type: "Sphere" }) + .fitSize([width, height], { type: "Sphere" }) ); } @@ -274,7 +282,7 @@ export async function rhombicHalf1() { geoRhombic() .parents([-1, 0, 6, 2, 1, 9, 11, 3, 4, 8, 6, 10]) .precision(0.1) - .fitSize([960, 500], { type: "Sphere" }) + .fitSize([width, height], { type: "Sphere" }) ); } export async function rhombicHalf2() { @@ -283,7 +291,7 @@ export async function rhombicHalf2() { .parents([4, 0, 6, 2, 1, 9, 11, 3, 4, 8, -1, 10]) .angle(-19.5) .precision(0.1) - .fitSize([960, 500], { type: "Sphere" }) + .fitSize([width, height], { type: "Sphere" }) ); } diff --git a/test/snapshots/twoPointEquidistantUsa.png b/test/snapshots/twoPointEquidistantUsa.png new file mode 100644 index 0000000..f00103f Binary files /dev/null and b/test/snapshots/twoPointEquidistantUsa.png differ