3

I've made a lot of maps in D3 and never had this problem, where the output is so small you can't see it. I've included a small geojson file, which is easily viewable using various other tools (e.g. here).

It appears that the projection is not being set properly with d3.geoPath(). The API specifies that d3.geoPath() takes a projection argument, however when I try to set the projection using this method (as in the example below), the resulting map is not projected properly. It's only after I set the projection using the d3.geoPath().projection() method that I obtain the proper result.

Is this an error in the documentation or am I missing something?

//setup and map parameters
var height = 300;
var width = 300;
var projection = d3.geoAlbers().scale(1000).translate([width/2, height/2]);
var path = d3.geoPath(projection);

//geoJSON data
var geojson = 
{"type":"FeatureCollection","features":[
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-86.910592,33.536105],[-86.907192,33.542204999999996],[-86.90539199999999,33.541305],[-86.901608,33.543456],[-86.895692,33.548805],[-86.89249199999999,33.550404],[-86.888392,33.549304],[-86.879772,33.553267999999996],[-86.868549,33.553418],[-86.8651,33.557134999999995],[-86.856129,33.562064],[-86.85159499999999,33.559404],[-86.848891,33.559805],[-86.852991,33.552104],[-86.85519099999999,33.543904999999995],[-86.855694,33.538151],[-86.860817,33.53405],[-86.850509,33.535693],[-86.83124699999999,33.544767],[-86.827029,33.534708],[-86.82383999999999,33.533389],[-86.826703,33.528081],[-86.826807,33.521107],[-86.822808,33.514319],[-86.81474899999999,33.502386],[-86.81411899999999,33.500078],[-86.816041,33.496234],[-86.823825,33.488839],[-86.825743,33.485408],[-86.82772299999999,33.477393],[-86.828558,33.472626999999996],[-86.82791,33.46967],[-86.833503,33.467552999999995],[-86.839366,33.463845],[-86.841534,33.460941],[-86.842309,33.465699],[-86.849758,33.466302],[-86.861663,33.456759],[-86.869367,33.4514],[-86.87749699999999,33.449382],[-86.884293,33.445453],[-86.88948599999999,33.441072999999996],[-86.890131,33.438437],[-86.899491,33.434508],[-86.908391,33.429308],[-86.922929,33.415512],[-86.925484,33.419897999999996],[-86.93114299999999,33.422801],[-86.93749199999999,33.421608],[-86.945352,33.425982999999995],[-86.946468,33.428407],[-86.937809,33.435314],[-86.925764,33.449968999999996],[-86.920859,33.454651999999996],[-86.92215,33.456643],[-86.927679,33.453849999999996],[-86.932965,33.449332],[-86.935435,33.446132],[-86.94133699999999,33.442150999999996],[-86.941301,33.445651999999995],[-86.942842,33.450103],[-86.946817,33.45017],[-86.95131599999999,33.45181],[-86.950279,33.458104],[-86.945494,33.463234],[-86.929693,33.478538],[-86.92935299999999,33.479858],[-86.920255,33.492874],[-86.92450699999999,33.493904],[-86.93110999999999,33.496843],[-86.933543,33.494935999999996],[-86.94144999999999,33.494743],[-86.944717,33.495934],[-86.946237,33.502279],[-86.946224,33.506071],[-86.941811,33.507621],[-86.937693,33.512206],[-86.92634,33.512381999999995],[-86.917255,33.517139],[-86.915435,33.519832],[-86.908779,33.518958999999995],[-86.903385,33.516537],[-86.898659,33.520165999999996],[-86.894459,33.526136],[-86.896992,33.530504],[-86.910592,33.536105]]]},"properties":{"a":1}}
]}

var svg = d3.select("#map");
svg.selectAll("path").data(geojson.features).enter().append("path").attr("fill","red").attr("d", path);
<script src="https://d3js.org/d3.v4.min.js"></script>

<svg height="300px" width="300px" id="map"> </svg>
Al R.
  • 2,430
  • 4
  • 28
  • 40
  • I'll have to reread the API, but setting var path = d3.geoPath().projection(projection) fixed the issue. – Al R. Jun 12 '17 at 19:57

1 Answers1

3

You will need to set your projection parameters correctly. A scale factor of 1 000 is much to small to show a small area as in your example. A scale factor of 50 000 might be more appropriate. You also need to properly center your map.

You will need to find a point near the center of your feature to properly center the map. You can do this manually by using google earth, or other methods to find a path centroid in geographic coordinates (not svg coordinates; however, you can find the svg centroid to refine the projection as well by translating features, I am not using that approach in my answer though).

With an Albers projection you should center on the y axis and rotate on the x axis:

var projection = d3.geoAlbers()
   .rotate([-x,0])
   .center([0,y])
   .scale(k)
   .translate([width/2,height/2])

You want to use the negative of your central meridian because you rotate the globe under the map. See this answer for a more graphical explanation of an Albers parameters

An Albers projection in d3 has default center and rotate coordinates. If you don't set the center coordinates with both rotate and center, the map will keep default parameters that are intended to show the entire US; this will modify the intended projection. Other projections are generally centered on [0,0] in d3 by default, which is off the coast of Africa.

I eyeballed a center coordinate using google earth to use in this projection. The centering point I am using here is:

86.884 degrees West (-86.884 degrees East)
33.507 degress North

I also zoomed in much further than your original zoom factor, using 100 000 rather than 1 000. Here is the projection showing your data:

//setup and map parameters
var height = 300;
var width = 300;
var projection = d3.geoAlbers()
   .scale(100000)
   .translate([width/2, height/2])
   .rotate([86.884,0])
   .center([0,33.507]);
var path = d3.geoPath(projection);

//geoJSON data
var geojson = 
{"type":"FeatureCollection","features":[
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-86.910592,33.536105],[-86.907192,33.542204999999996],[-86.90539199999999,33.541305],[-86.901608,33.543456],[-86.895692,33.548805],[-86.89249199999999,33.550404],[-86.888392,33.549304],[-86.879772,33.553267999999996],[-86.868549,33.553418],[-86.8651,33.557134999999995],[-86.856129,33.562064],[-86.85159499999999,33.559404],[-86.848891,33.559805],[-86.852991,33.552104],[-86.85519099999999,33.543904999999995],[-86.855694,33.538151],[-86.860817,33.53405],[-86.850509,33.535693],[-86.83124699999999,33.544767],[-86.827029,33.534708],[-86.82383999999999,33.533389],[-86.826703,33.528081],[-86.826807,33.521107],[-86.822808,33.514319],[-86.81474899999999,33.502386],[-86.81411899999999,33.500078],[-86.816041,33.496234],[-86.823825,33.488839],[-86.825743,33.485408],[-86.82772299999999,33.477393],[-86.828558,33.472626999999996],[-86.82791,33.46967],[-86.833503,33.467552999999995],[-86.839366,33.463845],[-86.841534,33.460941],[-86.842309,33.465699],[-86.849758,33.466302],[-86.861663,33.456759],[-86.869367,33.4514],[-86.87749699999999,33.449382],[-86.884293,33.445453],[-86.88948599999999,33.441072999999996],[-86.890131,33.438437],[-86.899491,33.434508],[-86.908391,33.429308],[-86.922929,33.415512],[-86.925484,33.419897999999996],[-86.93114299999999,33.422801],[-86.93749199999999,33.421608],[-86.945352,33.425982999999995],[-86.946468,33.428407],[-86.937809,33.435314],[-86.925764,33.449968999999996],[-86.920859,33.454651999999996],[-86.92215,33.456643],[-86.927679,33.453849999999996],[-86.932965,33.449332],[-86.935435,33.446132],[-86.94133699999999,33.442150999999996],[-86.941301,33.445651999999995],[-86.942842,33.450103],[-86.946817,33.45017],[-86.95131599999999,33.45181],[-86.950279,33.458104],[-86.945494,33.463234],[-86.929693,33.478538],[-86.92935299999999,33.479858],[-86.920255,33.492874],[-86.92450699999999,33.493904],[-86.93110999999999,33.496843],[-86.933543,33.494935999999996],[-86.94144999999999,33.494743],[-86.944717,33.495934],[-86.946237,33.502279],[-86.946224,33.506071],[-86.941811,33.507621],[-86.937693,33.512206],[-86.92634,33.512381999999995],[-86.917255,33.517139],[-86.915435,33.519832],[-86.908779,33.518958999999995],[-86.903385,33.516537],[-86.898659,33.520165999999996],[-86.894459,33.526136],[-86.896992,33.530504],[-86.910592,33.536105]]]},"properties":{"a":1}}
]}

var svg = d3.select("#map");
svg.selectAll("path").data(geojson.features).enter().append("path").attr("fill","red").attr("d", path);
<script src="https://d3js.org/d3.v4.min.js"></script>

<svg height="300px" width="300px" id="map"> </svg>
Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
  • Good answer, +1. You should create a tag for D3 geo projections... you would be the top answerer!. – Gerardo Furtado Jun 13 '17 at 00:16
  • Thanks, while projections are probably half my answers, I'd answer other topics for d3 more if you didn't beat me to the punch (and you answer with better answers than what I would have given too anyways, which is partially explained by the fact you are on track to be the top answerer of the much wider d3 tag). – Andrew Reid Jun 13 '17 at 03:32
  • Haha, I don't beat anybody! I live in Australia, meaning that I'm sleeping when you guys are awake and most of the questions (USA time) are posted. The only thing I do is, the next day, answering some questions which don't have an answer yet... – Gerardo Furtado Jun 13 '17 at 05:36