d3-geo-polygon
Version:
Clipping and geometric operations for spherical polygons.
119 lines (103 loc) • 3.28 kB
JavaScript
import {geoCentroid as centroid, geoGnomonic as gnomonic} from "d3-geo";
import {asin, atan2, cos, degrees, max, min, pi, radians, sin} from "./math.js";
import polyhedral from "./polyhedral/index.js";
import octahedron from "./polyhedral/octahedron.js";
export default function(faceProjection = ((face) => {
const c = face.length === 6 ? centroid({type: "MultiPoint", coordinates: face}) : face[0];
return gnomonic().scale(1).translate([0, 0]).rotate([-c[0], -c[1]]);
})) {
const w5 = octahedron.map((face) => {
const xyz = face.map(cartesian);
const n = xyz.length;
const hexagon = [];
let a = xyz[n - 1], b;
for (let i = 0; i < n; ++i) {
b = xyz[i];
hexagon.push(spherical([
a[0] * 0.9486832980505138 + b[0] * 0.31622776601683794,
a[1] * 0.9486832980505138 + b[1] * 0.31622776601683794,
a[2] * 0.9486832980505138 + b[2] * 0.31622776601683794
]), spherical([
b[0] * 0.9486832980505138 + a[0] * 0.31622776601683794,
b[1] * 0.9486832980505138 + a[1] * 0.31622776601683794,
b[2] * 0.9486832980505138 + a[2] * 0.31622776601683794
]));
a = b;
}
return hexagon;
});
const cornerNormals = [];
const parents = [-1, 0, 0, 1, 0, 1, 4, 5];
w5.forEach((hexagon, j) => {
const face = octahedron[j],
n = face.length,
normals = cornerNormals[j] = [];
for (let i = 0; i < n; ++i) {
w5.push([
face[i],
hexagon[(i * 2 + 2) % (2 * n)],
hexagon[(i * 2 + 1) % (2 * n)]
]);
parents.push(j);
normals.push(cross(
cartesian(hexagon[(i * 2 + 2) % (2 * n)]),
cartesian(hexagon[(i * 2 + 1) % (2 * n)])
));
}
});
const faces = w5.map((face) => ({
project: faceProjection(face),
face
}));
parents.forEach((d, i) => {
const parent = faces[d];
parent && (parent.children || (parent.children = [])).push(faces[i]);
});
function face(lambda, phi) {
const cosphi = cos(phi);
const p = [cosphi * cos(lambda), cosphi * sin(lambda), sin(phi)];
const hexagon = lambda < -pi / 2 ? phi < 0 ? 6 : 4
: lambda < 0 ? phi < 0 ? 2 : 0
: lambda < pi / 2 ? phi < 0 ? 3 : 1
: phi < 0 ? 7 : 5;
const n = cornerNormals[hexagon];
return faces[dot(n[0], p) < 0 ? 8 + 3 * hexagon
: dot(n[1], p) < 0 ? 8 + 3 * hexagon + 1
: dot(n[2], p) < 0 ? 8 + 3 * hexagon + 2
: hexagon];
}
return polyhedral(faces[0], face)
.angle(-30)
.scale(110.625)
.center([0, 45]);
}
function dot(a, b) {
let s = 0;
for (let i = 0; i < a.length; ++i) s += a[i] * b[i];
return s;
}
function cross(a, b) {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0]
];
}
// Converts 3D Cartesian to spherical coordinates (degrees).
function spherical(cartesian) {
return [
atan2(cartesian[1], cartesian[0]) * degrees,
asin(max(-1, min(1, cartesian[2]))) * degrees
];
}
// Converts spherical coordinates (degrees) to 3D Cartesian.
function cartesian(coordinates) {
const lambda = coordinates[0] * radians;
const phi = coordinates[1] * radians;
const cosphi = cos(phi);
return [
cosphi * cos(lambda),
cosphi * sin(lambda),
sin(phi)
];
}