1

I have a simple d3 map displaying data from Canada in topojson format, but the entire map is rendering sideways. I have been playing with the rotation parameters but I can't seem to get it just right. I'm wondering if anybody else has had this problem because it seems to me like there should be a very simple swap of an x and a y somewhere that would fix this, but I cannot find it since I am rather new to d3.

This is the code:

window.onload = initialize(); //start script once HTML is loaded

function initialize(){ //the first function called once the html is loaded
    setMap();
};

//set choropleth map parameters
function setMap(){

    //map frame dimensions
    var width = 1000;
    var height = 750;

    //create a new svg element with the above dimensions
    var map  = d3.select('body')
        .append('svg')
        .attr('width', width)
        .attr('height', height);

    //create Canada Alberts equal area conic projection, centred on Canada
    var projection = d3.geo.albers()
        .center([-56, 75])
        .rotate([0, 0, 0])
        .parallels([50, 70])
        .scale(750)
        .translate([width / 2, height / 2]);

    //create svg path generator using the projection
    var path = d3.geo.path()
        .projection(projection);

    //use queue.js to parallelize asynchronous data loading
    queue()
        .defer(d3.json, "data/canada_districts_4326.topojson") //load geometry
        .await(callback); //trigger callback function once data is loaded


    function callback(error, ridingData) {

        //add electoral boundaries geometry to map
        var ridings = map.append('path')
            .datum(topojson.feature(ridingData, ridingData.objects.canada_districts_4326))
            .attr('class', 'ridings')
            .attr('d', path)

        };
}

And this is the result:

enter image description here

  • Try swapping the center coordinates and make the 75 negative: [-75, 56]. At least I assume you didn't want the central meridian in the Indian Ocean. If that's still too far east, try -97 or so. – mkennedy Jun 29 '17 at 19:14
  • swap the coordinate order in your input file – Ian Turton Jun 29 '17 at 19:45
  • @iant: I don't understand why I would need to do that, because I converted my data to geojson in QGIS, then converted that to topojson using topo2geo (github.com/topojson/topojson-server/blob/master/…). Everything should be in the right standards for it to display – wfgeo Jun 30 '17 at 11:33
  • @mkennedy.: My initial coordinates are 56 W, 75 N. This is the approximate geographic centre of Canada. – wfgeo Jun 30 '17 at 11:34
  • you need to swap them because some one in your tool chain is expecting lon/lat instead of lat/lon or visaversa – Ian Turton Jun 30 '17 at 13:39
0

Centering an Albers projection in d3.js can be tricky.

Your map is displaying as it should in d3. This will make more sense if we center on [0,0] and zoom out. A resulting map of the world will look like this:

enter image description here

For reference, the projection I used is in the above map is (I've excluded the scale as this is dependent on svg size):

d3.geo.albers()
.parallels([50,70])
.rotate([0,0,0])
.center([0,0])
.translate([width/2, height/2])

Note: in d3v4 d3.geo.albers() would be d3.geoAlbers()

If we center the map on any coordinate, d3 will only shift the map to the target location. This will result in an image similar to yours. For example, if we centered and zoomed to Canada, we would get an area similar to the boxed area below:

enter image description here

This looks an awful lot like the problem you are describing.

Note, I am using 62.4 N, 96.466667 W as the geographic center of Canada in this answer

The solution to your problem is therefore to rotate the projection along the x axis. Taking the world map from above, if we rotate it 96.4666667 degrees (we rotate the earth under us, so it is the inverse of the longitude we want), we will get:

enter image description here

The projection I am using for the above map:

   var projection = d3.geo.albers()
        .parallels([50,70])
        .rotate([96.466667,0,0])
        .center([0,0])
        .translate([width/2, height/2])

Remember that D3 coordinates are always [x,y].

Now we can center to an appropriate y coordinate, in this case I'm using 62.4 N, and zoom in to an appropriate degree (I'll use a 960px x 500px svg for reference):

enter image description here

And the projection:

  var projection = d3.geo.albers()
    .parallels([50,70])
    .rotate([96.466667,-62.4,62.4])
    .center([0,0])
    .translate([width/2, height/2])
    .scale(700);

Summing that up, the easiest way to create an Albers is to use the following parameters:

d3.geo.albers()
  .rotate([-x,0])
  .center([0,y])
  .parallels([a,b])
  .translate([width/2,height/2])
  .scale(k);

Where x is your longitude, y is your latitude, a and b your parallels, and k your scale (which dependent on your svg size). You do not need to specify the third rotational value, its default is zero. And, if you don't have two parallels, but wish to use one, you can specify the same value for a and b.


Also, unlike most other projections in d3, the default is not [0,0]. I believe this is because, as an Albers has two secant lines or parallels, having all zero defaults wouldn't be useful. Instead, the default values are for the continental United States. For your projection parameters, you have set the rotation to [0,0,0] to over-ride the default rotation. Not overriding these defaults may lead to unexpected behaviour.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.