D3.js Map with Albers Projection: How to rotate it?
I'm building a map of the Philippines with d3.js and for a strange reason the map looks like rotated on the left, so that the country doesn't look how it really is. I've tried to modify the projection.rotate field but doesn't seems like is the correction line.
var width = 1060,
height = 860;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.style("overflow", "auto");
d3.json("ph.json", function(error, ph) {
if (error) return console.error(error);
var subunits = topojson.feature(ph, ph.objects.ph_subunits);
var projection = d3.geo.albers();
projection.rotate([-4 ,0]);
projection.scale(3000);
projection.center([122.427150, 12.499176]);
projection.parallels([10, 15])
projection.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection)
.pointRadius(2);
svg.append("path")
.datum(subunits)
.attr("d", path);
});
This is the code.
Any help would be great!
Thanks!
Albers projections can be a bit tricky if you don't know the underlying transformations. Unless showing the US (for which the parameters of d3.geoAlbers are defaulted to), you'll likely need to modify the parallels, rotation, and center.
Standard Parallels
This is an albers projection with its parallels set to 10 and 15 .parallels([10,15])
, as in your question, and a scale of 100 (and with rotation and center set to [0,0]):
This shape is due to the conical nature of the projection. If southern parallels are chosen, the concavity is reversed. More extreme latitudes/parallels will result in a greater bend.
The parallels selected should run through your area of interest as the area near the parallels is where distortion is minimized.
Rotation
If we wanted to say, focus on Australia (I'll keep the parallels the same as the image above for the sake of demonstration):
We could simply use the geographic center of Australia as the center of the projection .projection.center([x,y])
and scale to an appropriate level .projection.scale(1000)
:
The only change is that Australia is bigger, it is still as distorted as when it was in the corner of the map and smaller. It was shifted up/down and left/right and then magnified with no other transformations.
As your question surmises, rotation is the solution to the problem of showing areas not near the prime meridian.
If we rotate the first map (which is the same as the second map) by 100 degrees of longitude .projection.rotate([100,0])
, we get:
The world has spun 100 degrees, and an imaginary line drawn vertically through the center of the map aligns with the meridian at -100 degrees of longitude or 100 degrees west.
Centering
If our area of interest is Central America (for which the parallels you used - and I have continued to use - will work for) then the next step is to shift the center of the map up and down along the projection's central meridian. For Central America, this shift might be 20 degrees : .projection.center([0,20])
. With an appropriate scale, let's say 1000 the result will look like:
Philippines
So, for the Philippines, using your center coordinate [122.427150, 12.499176] and parallels, an ideal Albers projection might look like:
projection.rotate([-122.427150,0])
.center([0,12.499176])
.scale(1200)
.parallels([10,15]);
Which for me yielded:
Summary
For an Albers:
projection.parellels([a,b])
.rotate([-x,0])
.center([0,y])
.translate([w/2,h/2])
Where a
and b
are parallels that intersect the area of interest, y
is the central parallel, and x
is the central meridian.
If using a method (projeciton.fitSize
/projection.fitExtent
) to automatically center and scale a feature, you don't need to use the center and rotate methods, but you must still set parallels and rotation first.