-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
d3-geo-polygon exports its own versions of geoPolyhedral, geoPolyhedr…
…alButterfly, geoPolyhedralCollignon and geoPolyhedralWaterman, with minimal code duplication. With this version, d3/d3-geo-projection#132 is not needed anymore. (If you need to use both d3-geo-projection and d3-geo-polygon, they should be included in that order.)
- Loading branch information
Showing
13 changed files
with
541 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,14 @@ | ||
export {default as geoClipPolygon} from "./src/clip/polygon"; | ||
export {default as geoPolyhedral} from "./src/polyhedral/index"; | ||
export {default as geoPolyhedralButterfly} from "./src/polyhedral/butterfly.js"; | ||
export {default as geoPolyhedralCollignon} from "./src/polyhedral/collignon.js"; | ||
export {default as geoPolyhedralWaterman} from "./src/polyhedral/waterman.js"; | ||
|
||
// if necessary, the following line could export a copy of the d3-geo-projection versions under the names xxxxUnclipped | ||
// export {geoPolyhedral as geoPolyhedralUnclipped, geoPolyhedralButterfly as geoPolyhedralButterflyUnclipped, geoPolyhedralCollignon as geoPolyhedralCollignonUnclipped, geoPolyhedralWaterman as geoPolyhedralWatermanUnclipped} from "./node_modules/d3-geo-projection/index"; | ||
|
||
// if necessary, the following line could export a copy of these versions under the names xxxxClipped | ||
// export {default as geoPolyhedral, default as geoPolyhedralClipped} from "./src/polyhedral/index"; | ||
// export {default as geoPolyhedralButterfly, default as geoPolyhedralButterflyClipped} from "./src/polyhedral/butterfly.js"; | ||
// export {default as geoPolyhedralCollignon, default as geoPolyhedralCollignonClipped} from "./src/polyhedral/collignon.js"; | ||
// export {default as geoPolyhedralWaterman, default as geoPolyhedralWatermanClipped} from "./src/polyhedral/waterman.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import {geoCentroid as centroid, geoGnomonic as gnomonic} from "d3-geo"; | ||
import {pi} from "../../node_modules/d3-geo-projection/src/math"; | ||
import polyhedral from "./index"; | ||
import octahedron from "../../node_modules/d3-geo-projection/src/polyhedral/octahedron"; | ||
"./octahedron"; | ||
|
||
export default function(faceProjection) { | ||
|
||
faceProjection = faceProjection || function(face) { | ||
var c = centroid({type: "MultiPoint", coordinates: face}); | ||
return gnomonic().scale(1).translate([0, 0]).rotate([-c[0], -c[1]]); | ||
}; | ||
|
||
var faces = octahedron.map(function(face) { | ||
return {face: face, project: faceProjection(face)}; | ||
}); | ||
|
||
[-1, 0, 0, 1, 0, 1, 4, 5].forEach(function(d, i) { | ||
var node = faces[d]; | ||
node && (node.children || (node.children = [])).push(faces[i]); | ||
}); | ||
|
||
return polyhedral(faces[0], function(lambda, phi) { | ||
return faces[lambda < -pi / 2 ? phi < 0 ? 6 : 4 | ||
: lambda < 0 ? phi < 0 ? 2 : 0 | ||
: lambda < pi / 2 ? phi < 0 ? 3 : 1 | ||
: phi < 0 ? 7 : 5]; | ||
}) | ||
.scale(101.858) | ||
.center([0, 45]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import {geoCentroid as centroid, geoProjection as projection} from "d3-geo"; | ||
import {geoCollignonRaw as collignonRaw} from "d3-geo-projection"; | ||
import {pi, sqrt} from "../../node_modules/d3-geo-projection/src/math"; | ||
import polyhedral from "./index"; | ||
import octahedron from "../../node_modules/d3-geo-projection/src/polyhedral/octahedron"; | ||
|
||
var kx = 2 / sqrt(3); | ||
|
||
function collignonK(a, b) { | ||
var p = collignonRaw(a, b); | ||
return [p[0] * kx, p[1]]; | ||
} | ||
|
||
collignonK.invert = function(x,y) { | ||
return collignonRaw.invert(x / kx, y); | ||
}; | ||
|
||
export default function(faceProjection) { | ||
|
||
faceProjection = faceProjection || function(face) { | ||
var c = centroid({type: "MultiPoint", coordinates: face}); | ||
return projection(collignonK).translate([0, 0]).scale(1).rotate(c[1] > 0 ? [-c[0], 0] : [180 - c[0], 180]); | ||
}; | ||
|
||
var faces = octahedron.map(function(face) { | ||
return {face: face, project: faceProjection(face)}; | ||
}); | ||
|
||
[-1, 0, 0, 1, 0, 1, 4, 5].forEach(function(d, i) { | ||
var node = faces[d]; | ||
node && (node.children || (node.children = [])).push(faces[i]); | ||
}); | ||
|
||
return polyhedral(faces[0], function(lambda, phi) { | ||
return faces[lambda < -pi / 2 ? phi < 0 ? 6 : 4 | ||
: lambda < 0 ? phi < 0 ? 2 : 0 | ||
: lambda < pi / 2 ? phi < 0 ? 3 : 1 | ||
: phi < 0 ? 7 : 5]; | ||
}) | ||
.scale(121.906) | ||
.center([0, 48.5904]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import {geoBounds as bounds, geoCentroid as centroid, geoInterpolate as interpolate, geoProjection as projection} from "d3-geo"; | ||
import {default as clipPolygon} from "../clip/polygon"; | ||
import {abs, cos, degrees, epsilon, pi, radians, sin} from "../../node_modules/d3-geo-projection/src/math"; | ||
import {default as matrix, multiply, inverse} from "../../node_modules/d3-geo-projection/src/polyhedral/matrix"; | ||
|
||
// Creates a polyhedral projection. | ||
// * root: a spanning tree of polygon faces. Nodes are automatically | ||
// augmented with a transform matrix. | ||
// * face: a function that returns the appropriate node for a given {lambda, phi} | ||
// point (radians). | ||
// * r: rotation angle for final polyhedral net. Defaults to -pi / 6 (for | ||
// butterflies). | ||
export default function(root, face, r) { | ||
|
||
r = r == null ? -pi / 6 : r; // TODO automate | ||
|
||
recurse(root, {transform: [ | ||
cos(r), sin(r), 0, | ||
-sin(r), cos(r), 0 | ||
]}); | ||
|
||
function recurse(node, parent) { | ||
node.edges = faceEdges(node.face); | ||
// Find shared edge. | ||
if (parent.face) { | ||
var shared = node.shared = sharedEdge(node.face, parent.face), | ||
m = matrix(shared.map(parent.project), shared.map(node.project)); | ||
node.transform = parent.transform ? multiply(parent.transform, m) : m; | ||
// Replace shared edge in parent edges array. | ||
var edges = parent.edges; | ||
for (var i = 0, n = edges.length; i < n; ++i) { | ||
if (pointEqual(shared[0], edges[i][1]) && pointEqual(shared[1], edges[i][0])) edges[i] = node; | ||
if (pointEqual(shared[0], edges[i][0]) && pointEqual(shared[1], edges[i][1])) edges[i] = node; | ||
} | ||
edges = node.edges; | ||
for (i = 0, n = edges.length; i < n; ++i) { | ||
if (pointEqual(shared[0], edges[i][0]) && pointEqual(shared[1], edges[i][1])) edges[i] = parent; | ||
if (pointEqual(shared[0], edges[i][1]) && pointEqual(shared[1], edges[i][0])) edges[i] = parent; | ||
} | ||
} else { | ||
node.transform = parent.transform; | ||
} | ||
if (node.children) { | ||
node.children.forEach(function(child) { | ||
recurse(child, node); | ||
}); | ||
} | ||
return node; | ||
} | ||
|
||
function forward(lambda, phi) { | ||
var node = face(lambda, phi), | ||
point = node.project([lambda * degrees, phi * degrees]), | ||
t; | ||
if (t = node.transform) { | ||
return [ | ||
t[0] * point[0] + t[1] * point[1] + t[2], | ||
-(t[3] * point[0] + t[4] * point[1] + t[5]) | ||
]; | ||
} | ||
point[1] = -point[1]; | ||
return point; | ||
} | ||
|
||
// Naive inverse! A faster solution would use bounding boxes, or even a | ||
// polygonal quadtree. | ||
if (hasInverse(root)) forward.invert = function(x, y) { | ||
var coordinates = faceInvert(root, [x, -y]); | ||
return coordinates && (coordinates[0] *= radians, coordinates[1] *= radians, coordinates); | ||
}; | ||
|
||
function faceInvert(node, coordinates) { | ||
var invert = node.project.invert, | ||
t = node.transform, | ||
point = coordinates; | ||
if (t) { | ||
t = inverse(t); | ||
point = [ | ||
t[0] * point[0] + t[1] * point[1] + t[2], | ||
(t[3] * point[0] + t[4] * point[1] + t[5]) | ||
]; | ||
} | ||
if (invert && node === faceDegrees(p = invert(point))) return p; | ||
var p, | ||
children = node.children; | ||
for (var i = 0, n = children && children.length; i < n; ++i) { | ||
if (p = faceInvert(children[i], coordinates)) return p; | ||
} | ||
} | ||
|
||
function faceDegrees(coordinates) { | ||
return face(coordinates[0] * radians, coordinates[1] * radians); | ||
} | ||
|
||
var proj = projection(forward), | ||
stream_ = proj.stream; | ||
|
||
// if d3-geo-polygon and proj.preclip are available: | ||
// run around the mesh of faces and stream all vertices to create the clipping polygon | ||
if (clipPolygon && proj.preclip) { | ||
var polygon = []; | ||
outline({point: function(lambda, phi) { polygon.push([lambda, phi]); }}, root); | ||
polygon.push(polygon[0]); | ||
proj.preclip(clipPolygon({ type: "Polygon", coordinates: [ polygon ] })); | ||
} | ||
|
||
function noClip(s) { return s; } | ||
|
||
proj.stream = function(stream) { | ||
var rotate = proj.rotate(), | ||
preclip = proj.preclip ? proj.preclip() : null, | ||
rotateStream = stream_(stream), | ||
sphereStream = ((preclip ? proj.preclip(noClip) : proj).rotate([0, 0]), stream_(stream)); | ||
proj.rotate(rotate); | ||
if (preclip) proj.preclip(preclip); | ||
rotateStream.sphere = function() { | ||
sphereStream.polygonStart(); | ||
sphereStream.lineStart(); | ||
outline(sphereStream, root); | ||
sphereStream.lineEnd(); | ||
sphereStream.polygonEnd(); | ||
}; | ||
return rotateStream; | ||
}; | ||
|
||
return proj; | ||
} | ||
|
||
function outline(stream, node, parent) { | ||
var point, | ||
edges = node.edges, | ||
n = edges.length, | ||
edge, | ||
multiPoint = {type: "MultiPoint", coordinates: node.face}, | ||
notPoles = node.face.filter(function(d) { return abs(d[1]) !== 90; }), | ||
b = bounds({type: "MultiPoint", coordinates: notPoles}), | ||
inside = false, | ||
j = -1, | ||
dx = b[1][0] - b[0][0]; | ||
// TODO | ||
node.centroid = dx === 180 || dx === 360 | ||
? [(b[0][0] + b[1][0]) / 2, (b[0][1] + b[1][1]) / 2] | ||
: centroid(multiPoint); | ||
// First find the shared edge… | ||
if (parent) while (++j < n) { | ||
if (edges[j] === parent) break; | ||
} | ||
++j; | ||
for (var i = 0; i < n; ++i) { | ||
edge = edges[(i + j) % n]; | ||
if (Array.isArray(edge)) { | ||
if (!inside) { | ||
stream.point((point = interpolate(edge[0], node.centroid)(epsilon))[0], point[1]); | ||
inside = true; | ||
} | ||
stream.point((point = interpolate(edge[1], node.centroid)(epsilon))[0], point[1]); | ||
} else { | ||
inside = false; | ||
if (edge !== parent) outline(stream, edge, node); | ||
} | ||
} | ||
} | ||
|
||
// Tests equality of two spherical points. | ||
function pointEqual(a, b) { | ||
return a && b && a[0] === b[0] && a[1] === b[1]; | ||
} | ||
|
||
// Finds a shared edge given two clockwise polygons. | ||
function sharedEdge(a, b) { | ||
var x, y, n = a.length, found = null; | ||
for (var i = 0; i < n; ++i) { | ||
x = a[i]; | ||
for (var j = b.length; --j >= 0;) { | ||
y = b[j]; | ||
if (x[0] === y[0] && x[1] === y[1]) { | ||
if (found) return [found, x]; | ||
found = x; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Converts an array of n face vertices to an array of n + 1 edges. | ||
function faceEdges(face) { | ||
var n = face.length, | ||
edges = []; | ||
for (var a = face[n - 1], i = 0; i < n; ++i) edges.push([a, a = face[i]]); | ||
return edges; | ||
} | ||
|
||
function hasInverse(node) { | ||
return node.project.invert || node.children && node.children.some(hasInverse); | ||
} |
Oops, something went wrong.