Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing already projected geoJSON map in d3.js

Tags:

d3.js

geojson

With the v3 of d3.js I am having problems drawing a map using geoJSON data. The code and the resulting map is shown at: http://bl.ocks.org/73833ec90a8a77b0e29f. Using v2 of d3.js this example generates a correct map.

  1. My data is already projected (they are Dutch National Grid/Rijksdriehoekstelsel coordinates). In order to compensate for this I wrote my own projection function that just translates the coordinate system of the map to pixels (e.g. scaling and translation).
  2. d3.geo.path() in v3 of d3.js resamples the data. However, the points generated in the resampling do not seem to be in the same coordinate system as my map (I assume they are lon, lat coordinates).

I would prefer not to translate the coordinates of my map to lon,lat coordinates, since the map is already projected the way I would like, and as far as I can tell this is not a trivial projection.

If the problems are indeed caused by the resampling, I would like to disable the resampling. However, in the documentation I could not really find how to do this. Instead of passing a projection function to d3.geo.path.projection(), I could pass an streaming object. I thought the following would work:

var projection = d3.geo.projection(function(x, y) {
    return [ scale*(x-xmin), height-scale*(y-ymin) ];
  }).precision(0);

but it doesn't. Probably also to do with the fact that I don't have lat,lon coordinates. How can I disable the resampling using a custom projection function?

Or when something else is causing the problems, I would like to hear that to.

Thanks.

like image 360
Jan van der Laan Avatar asked Feb 01 '13 12:02

Jan van der Laan


2 Answers

I ran into the same problem recently.

The way to do that is to explicitly tell d3 that you do not want a projection. The answer is in this link.

"If projection is null, the path uses the identity transformation, where the input 
geometry is not projected and is instead rendered directly in raw coordinates. This can be 
useful for fast rendering of already-projected geometry, or for fast rendering of the 
equirectangular projection."

So you want to have

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

And then, something like this

g.selectAll("path")
            .data(json.features)
            .enter().append("path")
            .attr("d", path)
like image 101
Laurent Sigal Avatar answered Nov 03 '22 10:11

Laurent Sigal


In response to user603124 answer I had another look at the problem (up until now I sticked with v2 of d3.js). My original idea of creating an object works. However, in my original implementation I had the scaling and zooming wrong. Using the answer to another question to get the scaling and zooming right:

<script>
  var height = 400;
  var width  = 400;

  var vis = d3.select("#vis").append("svg")
      .attr("width", width).attr("height", height)

  d3.json("po_2012_simplified.json", function(json) {

      var projection = d3.geo.projection(function(x, y) { return [x, y];})
        .precision(0).scale(1).translate([0, 0]);

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

      var bounds = path.bounds(json),
          scale  = .95 / Math.max((bounds[1][0] - bounds[0][0]) / width, 
                  (bounds[1][1] - bounds[0][1]) / height),
          transl = [(width - scale * (bounds[1][0] + bounds[0][0])) / 2, 
                  (height - scale * (bounds[1][1] + bounds[0][1])) / 2];

      projection.scale(scale).translate(transl);

      vis.selectAll("path").data(json.features).enter().append("path")
        .attr("d", path)
        .style("fill", "#D0D0D0")
        .style("stroke-width", "0.5px")
        .style("stroke", "black")
    });

</script>

See http://bl.ocks.org/djvanderlaan/5336035 for a complete working solution.

like image 6
Jan van der Laan Avatar answered Nov 03 '22 08:11

Jan van der Laan