Use Proj4js to convert proj4 or WKT projection definition strings into D3 projections.
// Create Proj4js projection function
var proj4Projection = proj4(wkt); // WGS84 to projection defined in `wkt`
// Use this to create a D3 projection
var project = function(lambda, phi) {
return proj4Projection
.forward([lambda, phi].map(radiansToDegrees));
};
project.invert = function(x, y) {
return proj4Projection
.inverse([x, y]).map(degreesToRadians);
};
var projection = d3.geoProjection(project);
Geographic data from Eurostat's GISCO program. The example projections came from Spatial Reference.
forked from armollica's block: Proj4/WKT + D3
There are three ways to transition the shapes from one projection to the other:
-- Mike Bostock's and Jason Davies' projection transitions is probably the correct way to do it
-- Noah Veltman's flubber interpolates shapes beautifully, but not lines (graticules) yet
-- Peter Beshai's d3-interpolate-path works well with those graticules since version 2
xxxxxxxxxx
<html>
<head>
<style>
html {
font-family: monospace;
}
svg {
cursor: crosshair;
}
#projection-menu {
position: absolute;
right: 10px;
top: 10px;
}
.europe {
fill: none;
stroke: #000;
}
.graticule {
fill: none;
stroke: #aaa;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
.coordinates {
text-anchor: middle;
fill: #000;
text-shadow: -1px 0px 0px #fff,
0px 1px 0px #fff,
1px 0px 0px #fff,
0px -1px 0px #fff;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<select id="projection-menu"></select>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://unpkg.com/proj4"></script>
<script src="https://unpkg.com/d3-interpolate-path"></script>
<script>
var options = [
{ name: "Pulkovo 1942(58) / Poland Zone 1", proj4: "+proj=sterea +lat_0=50.625 +lon_0=21.08333333333333 +k=0.9998 +x_0=4637000 +y_0=5647000 +ellps=krass +towgs84=33.4,-146.6,-76.3,-0.359,-0.053,0.844,-0.84 +units=m +no_defs" },
{ name: "Madrid 1870 (Madrid) / Spain", proj4: "+proj=lcc +lat_1=40 +lat_0=40 +lon_0=0 +k_0=0.9988085293 +x_0=600000 +y_0=600000 +a=6378298.3 +b=6356657.142669561 +pm=madrid +units=m +no_defs" },
{ name: "IRENET95 / Irish Transverse Mercator", proj4: "+proj=tmerc +lat_0=53.5 +lon_0=-8 +k=0.99982 +x_0=600000 +y_0=750000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs" },
{ name: "NGO 1948 Norway Zone 8", proj4: "+proj=tmerc +lat_0=58 +lon_0=29.05625 +k=1 +x_0=0 +y_0=0 +a=6377492.018 +b=6356173.508712696 +units=m +no_defs" },
{ name: "ED50 / France EuroLambert", proj4: "+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=2.337229166666667 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=intl +units=m +no_defs" },
{ name: "ELD79 / Libya Zone 6", proj4: "+proj=tmerc +lat_0=0 +lon_0=11 +k=0.9999 +x_0=200000 +y_0=0 +ellps=intl +units=m +no_defs" },
{ name: "WGS 84 / North Pole LAEA Europe", proj4: "+proj=laea +lat_0=90 +lon_0=10 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs" },
{ name: "Albanian 1987 / Gauss-Kruger Zone 4", proj4: "+proj=tmerc +lat_0=0 +lon_0=21 +k=1 +x_0=4500000 +y_0=0 +ellps=krass +units=m +no_defs" },
{ name: "Andrew Special", proj4: "+proj=lcc +lat_1=40 +lat_0=40 +lon_0=0 +k_0=0.9988085293 +x_0=600000 +y_0=600000 +a=7378298.3 +b=7356657 +axis=wnd +units=m +no_defs" }
];
d3.geoProjectionProj4 = function(_) {
function degreesToRadians(degrees) { return degrees * Math.PI / 180; }
function radiansToDegrees(radians) { return radians * 180 / Math.PI; }
var wkt = _;
var proj = proj4(wkt);
var project = function(lambda, phi) {
return proj.forward([lambda, phi].map(radiansToDegrees));
};
project.invert = function(x, y) {
return proj.inverse([x, y]).map(degreesToRadians);
};
var p = d3.geoProjection(project);
p.wkt = function(_) {
if (_) {
proj = proj4(wkt = _);
return p;
}
return wkt;
}
return p;
}
var projection = d3.geoProjectionProj4(options[0].proj4);
var width = 960,
height = 500;
var coordinateFormat = d3.format(".2f");
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var path = d3.geoPath()
.projection(projection);
var graticule = d3.geoGraticule()
.precision(0.1)
.extent([[-12, 33],[35, 70]])
d3.json("europe.json", function(error, data) {
if (error) throw error;
var europe = topojson.feature(data, data.objects.europe);
projection.fitExtent([[0,0], [width, height]], europe);
var grid = svg.selectAll(".graticule").data(graticule.lines())
.enter().append("path")
.attr("class", "graticule")
.attr("d", path);
var borders = svg.append("path").datum(europe)
.attr("class", "europe")
.attr("d", path);
var coordinates = svg.append("text")
.attr("class", "coordinates")
.attr("dy", "-1.66em")
.classed("hidden", true);
var menu = d3.select("#projection-menu")
.on("change", change);
menu.selectAll("option")
.data(options)
.enter().append("option")
.text(function(d) { return d.name; });
function change() {
projection
.wkt(options[this.selectedIndex].proj4)
.fitExtent([[0,0], [width, height]], europe);
borders
.transition(d3.transition().duration(750))
.attr("d", path);
// Make the graticule paths transition nicely
grid
.transition(d3.transition().duration(750))
.attrTween('d', function (d) {
var previous = d3.select(this).attr('d');
var current = path(d);
return d3.interpolatePath(previous, current);
});
}
svg
.on("mousemove", mousemove)
.on("mouseleave", mouseleave);
function mousemove() {
var mouse = d3.mouse(this),
p = projection.invert(mouse),
text = "(" + coordinateFormat(p[1]) + "°, " +
coordinateFormat(p[0]) + "°)";
coordinates
.classed("hidden", false)
.attr("x", mouse[0])
.attr("y", mouse[1])
.text(text);
}
function mouseleave() {
coordinates.classed("hidden", true);
}
});
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v1.min.js
https://unpkg.com/proj4
https://unpkg.com/d3-interpolate-path