Search code examples
d3.jsgeojsonqgis

Rendering GeoJson file in D3 v4 is not scaling properly


I am attempting to render a map with an overlay using GeoJSON data. I have loaded the data into QGIS as well as mapshaper and it renders correctly; however, my JavaScript code is not rendering what I would expect (the entire SVG is filled in, making me think it is a scaling issue.)

I believe my code for calculating the scale is accurate.

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

I have uploaded my sample project onto plunker https://plnkr.co/edit/jzXRaOs2xXpZuJraMq3p.

The expected result should look like: Map of the US


Solution

  • The problem lies in your geojson:

    ... coordinates":[[[1983816.6475302426,681422.0990409602], ...
    

    this data is projected data, that is it uses a 2 dimensional cartesian system of coordinates. D3 projections use lat long pairs (points on a 3 dimensional globe), with values no more than 180 degrees east/west and 90 degree north/south. The specific ellipsoid that lat long pairs in D3 are supposed to use is defined in the WGS84 datum. D3 expects geographic data to be located within this coordinate space.

    If you want to use the auto-scaling function that you have referenced in the question, then you will need to reproject your data so that it is in WGS84 (or alternatively, find a US geojson that already uses this spatial reference system). Tools such as mapshaper can help convert data online (though you may need to specify what spatial reference system your data uses before applying any transforms).

    Another option is to use a geoTransform (geo.transform in v3) to scale your already projected data. As your data is already 2 dimensional on a cartesian plane, you can simply create a function to scale it to match your svg coordinate space. I haven't thought about an autoscaling function for this, but it should be possible. An example of a geo.transform is found here (with a similar geographic feature set):

    https://bl.ocks.org/andrew-reid/496078bd5e37fd22a9b43fd6be84b36b

    (As mapshaper and qGIS anticipate multiple different spatial reference systems, they will display this data normally if a projection is specified in the data, or as plain x,y data, which is why it might appear to work on other platforms but not in d3 projections, which assume all data is lat long pairs).