5
votes

Given a shapefile :

Natural_earth/ne_10m_admin_0_sovereignty.zip

Given we want to reproject it for a D3js data viz, we could reproject at different levels.

1. Get a reprojected shapefile (1), using ogr2ogr :

ogr2ogr -f 'ESRI Shapefile' -t_srs 'EPSG:...' output.shp input.shp

OR 2. get a reprojected topojson (2), using (npm) topojson.js:

topojson \
        -o output.topo.json
        --projection 'd3.geo.albersUsa()' \
        -q 1e5 \
        -s 1 \
        -- input.shp

OR 3. get a reprojected D3js data / SVG (1), D3js code including:

var path = d3.geo.path()
    .projection(d3.geo.albersUsa())   // unsure of this syntaxe, confirmation welcome, just delete this comment.

Overview:

Mike Bostock > Projected Topojson informs us that the 1st and 2nd ways "eliminates the need to project the geometry while rendering, improving performance [...] since the importance of each point is measured in area on-screen rather than on the Earth’s surface." in short, the end pixel quality/file weight ratio is better.

On the other hand, reprojecting the geometry while rendering allow a more agile, last minute projection.

Knowing more ?

This is all what I know. If someone could explain more on these ways, share helping resources for parameters (list of ogr's EPSG, list of d3js projections), and respective benefits/weakness for each, it could be a very interesting parallel manual.

Note: I will give my shoot-answer at it but I just start to dig on it. I guess there are more experienced people around.

1

1 Answers

5
votes

Just a little background to complement the question. GeoJSON files contain the description of the geometry of places on Earth, usually as polygons or collection of polygons, where each vertex is a pair of (longitude, latitude). To create a map in the screen, we need to compute a correspondence between (longitude, latitude) pairs and points in the screen (column n⁰x, line n⁰y). This correspondence is a projection. D3 includes several projections.

In (1), the projection-on-earth is computed using ogr2ogr, but you still need to set the width and height with topojson to adjust the viewport (see http://bl.ocks.org/mbostock/5557726) for an example.

In (2), you want to use topojson to generate a TopoJSON file that has the projection already computed, using the original shapefile directly. To do this, you need to set which projection you want to use, as well as some parameters (width and height):

topojson --width 960 --height 800 \
         --projection 'd3.geo.orthographic' \
         -o output.json -- input.shp

If you do this, the TopoJSON file (output.json) will have the projection already computed, so you don't need to compute it again when setting the geographic path generator:

var path = d3.geo.path()
    .projection(null);

In option (3), you compute the projection when rendering via D3.js. To do this, you need a GeoJSON or TopoJSON files, and configure the projection.

// Assuming GeoJSON
d3.json('output.json', function(error, geodata) {

    // Create the SVG, etc...

    // Create the projection, configure the scale, translation and rotation
    var projection = d3.geo.mercator()
        .translate([width / 2, height / 2]);

    // Create the path generator
    var path = d3.geo.path()
        .projection(projection);

    // Generate the shapes
    svg.selectAll('path.feature').data(geodata.features)
        .enter().append('path')
        .attr('class', 'feature')
        .attr('d', path);
});

Regards,