-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathreclip.js
119 lines (109 loc) · 4.1 KB
/
reclip.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import {merge} from "d3-array";
import {geoDistance, 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,
geoTwoPointEquidistant as twoPointEquidistant
} 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)); }
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;
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);
}
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));
}