Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active August 2, 2018 10:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pbogden/d02a16d867c3d8a3dbf26f4a9bb9e6b1 to your computer and use it in GitHub Desktop.
Save pbogden/d02a16d867c3d8a3dbf26f4a9bb9e6b1 to your computer and use it in GitHub Desktop.
Rotating GeoJSON in Three.js
license: gpl-3.0
height: 960
border: no

GeoJSON in Three.js rotating about the North Pole.

In Mike Bostock's original, the "z" axis is parallel to the North Pole. In Three.js, the default is "y" axis pointing "up". Transform from original Cartesian axes to Three default by rotating 270 degrees about the X axis.

xThree = x = cos(φ)cos(λ)   φ = latitude
zThree = - y = - cos(φ)sin(λ)   λ = longitude
yThree = z = sin(φ)

That is:

  • Three & Original "x" axes are identical
  • Three's "z" = Original "y"
  • Three's "y" = Original "z" -- these point through North Pole
<!DOCTYPE html>
<title>rotating geojson</title>
<body>
<script src="https://unpkg.com/three@0.84"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/d3-array@1"></script>
<script src="https://unpkg.com/d3-collection@1"></script>
<script src="https://unpkg.com/d3-dispatch@1"></script>
<script src="https://unpkg.com/d3-request@1"></script>
<script src="https://unpkg.com/d3-timer@1"></script>
<script>
var width = 960,
height = 960,
radius = 228,
mesh,
graticule,
scene = new THREE.Scene,
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000),
renderer = new THREE.WebGLRenderer({alpha: true});
camera.position.z = 400;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
d3.json("https://unpkg.com/world-atlas@1/world/50m.json", function(error, topology) {
if (error) throw error;
scene.add(graticule = wireframe(graticule10(), new THREE.LineBasicMaterial({color: 0xaaaaaa})));
scene.add(mesh = wireframe(topojson.mesh(topology, topology.objects.land), new THREE.LineBasicMaterial({color: 0xff0000})));
d3.timer(function(t) {
// Rotate about the "y" axis, which is parallel to the North Pole
graticule.rotation.y = mesh.rotation.y = t / 10000;
renderer.render(scene, camera);
});
});
// Converts a point [longitude, latitude] in degrees to a THREE.Vector3.
// Axes have been rotated so Three's "y" axis is parallel to the North Pole
function vertex(point) {
var lambda = point[0] * Math.PI / 180,
phi = point[1] * Math.PI / 180,
cosPhi = Math.cos(phi);
return new THREE.Vector3(
radius * cosPhi * Math.cos(lambda),
radius * Math.sin(phi),
- radius * cosPhi * Math.sin(lambda)
);
}
// Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
function wireframe(multilinestring, material) {
var geometry = new THREE.Geometry;
multilinestring.coordinates.forEach(function(line) {
d3.pairs(line.map(vertex), function(a, b) {
geometry.vertices.push(a, b);
});
});
return new THREE.LineSegments(geometry, material);
}
// See https://github.com/d3/d3-geo/issues/95
function graticule10() {
var epsilon = 1e-6,
x1 = 180, x0 = -x1, y1 = 80, y0 = -y1, dx = 10, dy = 10,
X1 = 180, X0 = -X1, Y1 = 90, Y0 = -Y1, DX = 90, DY = 360,
x = graticuleX(y0, y1, 2.5), y = graticuleY(x0, x1, 2.5),
X = graticuleX(Y0, Y1, 2.5), Y = graticuleY(X0, X1, 2.5);
function graticuleX(y0, y1, dy) {
var y = d3.range(y0, y1 - epsilon, dy).concat(y1);
return function(x) { return y.map(function(y) { return [x, y]; }); };
}
function graticuleY(x0, x1, dx) {
var x = d3.range(x0, x1 - epsilon, dx).concat(x1);
return function(y) { return x.map(function(x) { return [x, y]; }); };
}
return {
type: "MultiLineString",
coordinates: d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X)
.concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y))
.concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return Math.abs(x % DX) > epsilon; }).map(x))
.concat(d3.range(Math.ceil(y0 / dy) * dy, y1 + epsilon, dy).filter(function(y) { return Math.abs(y % DY) > epsilon; }).map(y))
};
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment