geojson3d
Version:
Renders GeoJsons using Three.js
153 lines (132 loc) • 5.58 kB
JavaScript
var d3 = require('d3');
var d3Projections = require('d3-geo-projection');
var turf = require('turf');
var THREE = require('three');
var projections = {
"Aitoff": d3Projections.geoAitoff(),
"Albers": d3.geoAlbers(),
"Albers-USA": d3.geoConicEqualArea().parallels([29.5, 45.5]).rotate([98, 0]).center([0, 38]),
"August": d3Projections.geoAugust().scale(60),
"Baker": d3Projections.geoBaker().scale(100),
"Boggs": d3Projections.geoBoggs(),
"Bonne": d3Projections.geoBonne().scale(120),
"Bromley": d3Projections.geoBromley(),
"Conic": d3.geoConicEqualArea(),
"Collignon": d3Projections.geoCollignon().scale(93),
"Craster Parabolic": d3Projections.geoCraster(),
"Eckert I": d3Projections.geoEckert1().scale(165),
"Eckert II": d3Projections.geoEckert2().scale(165),
"Eckert III": d3Projections.geoEckert3().scale(180),
"Eckert IV": d3Projections.geoEckert4().scale(180),
"Eckert V": d3Projections.geoEckert5().scale(170),
"Eckert VI": d3Projections.geoEckert6().scale(170),
"Eisenlohr": d3Projections.geoEisenlohr().scale(60),
"Equirectangular": d3.geoEquirectangular(),
"Hammer": d3Projections.geoHammer().scale(165),
"Hill": d3Projections.geoHill(),
"Goode Homolosine": d3Projections.geoHomolosine(),
"Kavrayskiy VII": d3Projections.geoKavrayskiy7(),
"Lambert cylindrical equal-area": d3Projections.geoCylindricalEqualArea(),
"Lagrange": d3Projections.geoLagrange().scale(120),
"Larrivée": d3Projections.geoLarrivee().scale(95),
"Laskowski": d3Projections.geoLaskowski().scale(120),
"Loximuthal": d3Projections.geoLoximuthal(),
"Mercator": d3.geoMercator(),
"Miller": d3Projections.geoMiller().scale(100),
"McBryde–Thomas Flat-Polar Parabolic": d3Projections.geoMtFlatPolarParabolic(),
"McBryde–Thomas Flat-Polar Quartic": d3Projections.geoMtFlatPolarQuartic(),
"McBryde–Thomas Flat-Polar Sinusoidal": d3Projections.geoMtFlatPolarSinusoidal(),
"Mollweide": d3Projections.geoMollweide().scale(165),
"Natural Earth": d3Projections.geoNaturalEarth(),
"Nell–Hammer": d3Projections.geoNellHammer(),
"Polyconic": d3Projections.geoPolyconic().scale(100),
"Robinson": d3Projections.geoRobinson(),
"Sinusoidal": d3Projections.geoSinusoidal(),
"Sinu-Mollweide": d3Projections.geoSinuMollweide(),
"van der Grinten": d3Projections.geoVanDerGrinten().scale(75),
"van der Grinten IV": d3Projections.geoVanDerGrinten4().scale(120),
"Wagner IV": d3Projections.geoWagner4(),
"Wagner VI": d3Projections.geoWagner6(),
"Wagner VII": d3Projections.geoWagner7(),
"Winkel Tripel": d3Projections.geoWinkel3(),
"Identity": d3.geoProjection(function(x, y) { return [x, y];}) };
// return scaling factor that fit bounds within width/height
function fit(bounds, width, height)
{
var topLeft = bounds[0];
var bottomRight = bounds[1];
var w = bottomRight[0] - topLeft[0];
var h = bottomRight[1] - topLeft[1];
var hscale = width / w;
var vscale = height / h;
// pick the smallest scaling factor
var scale = (hscale < vscale) ? hscale : vscale;
return scale;
}
// D3.js projection from GeoJSON bounds
function getProjection(geojson, width, height, projection) {
// From:
// http://stackoverflow.com/questions/14492284/center-a-map-in-d3-given-a-geojson-object?answertab=active#tab-top
// We are using Turf to compute centroid (turf.centroid) and bounds (turf.envelope) because
// D3's geo.centroid and path.bounds function expect clockwise polygons, which we cannot always guarantee:
// https://github.com/mbostock/d3/wiki/Geo-Paths#_path
if (projection == undefined) {
projection = "Identity";
}
var center = turf.centroid(geojson).geometry.coordinates;
var the_projection = projections[projection]
.center(center)
.scale(1)
.translate(center);
// Create the path
var path = d3.geoPath().projection(the_projection);
// Using the path determine the bounds of the current map and use
// these to determine better values for the scale and translation
// var env = turf.envelope(geojson);
var bounds = path.bounds(geojson);
var scale = fit(bounds, width, height);
if (projection == "Albers-USA" || projection == "Albers") {
the_projection = projections[projection]
.center(center)
.scale(scale)
.translate([-width/2, -height/2]);
} else {
the_projection = projections[projection]
.center(center)
.scale(scale)
.translate([0, 0]);
}
return the_projection;
}
// Use D3.js projection to create array of Three.js points/vectors from GeoJSON ring
function ringToPoints(ring, projection) {
return ring.map(function(point) {
if (projection != null) {
var projected = projection(point);
return new THREE.Vector2(projected[0], projected[1]);
}
return new THREE.Vector2(point[0], point[1]);
});
}
// Create Three.js polygon from GeoJSON Polygon
function createPolygonShape(polygon, projection) {
var outerRing = polygon[0];
var points = ringToPoints(outerRing, projection);
//points = points.slice(0, 1000) + [points[0]]
//console.log(points);
var polygonShape = new THREE.Shape(points);
polygon.slice(1).forEach(function(hole) {
points = ringToPoints(hole, projection);
var holeShape = new THREE.Shape(points);
polygonShape.holes.push(holeShape);
});
return polygonShape;
}
var geo = {
fit: fit,
getProjection: getProjection,
ringToPoints: ringToPoints,
createPolygonShape: createPolygonShape,
projections: projections
}
module.exports = geo