2
votes

I am working with D3 JS libraries to display maps. Loading the U.S. states map is working fine, but if I attempt to load county maps for individual states, it isn't working properly. The file I am using for all states is found at https://raw.githubusercontent.com/d3/d3.github.com/master/us-10m.v1.json. The state map I am currently using is https://raw.githubusercontent.com/deldersveld/topojson/master/countries/us-states/MI-26-michigan-counties.json, though other states have the same behavior.

Using the Chrome developer tools, I noticed that the states (the one that works) renders with one "path" element with one "d" element underneath it for the entire map:

<path d="M558.8236946374037,348.30360060633L559.3534721355569,348.2802219377176L567.3201262115587,347.9178525742255L571.2684679430778,347.7775805625511L572.0381446856777,347.7542018939387L577.0160540079474…

The counties render one "g" element with multiple "path" elements:

<g class="counties"><path d="M-86.2371554117112,44.517643343110294L-85.81834245759008,44.51265840274941L-85.82187674412275,44.16371257748768L-86.04276965241448,44.16620504766812L-86.38912973261591,44.178667398570326L-86.35201972402291,44.22851680217914L-86.26896399050521,44.344416665569646L-86.25305970110821,44.40049724462957L-86.24952541457554,44.48274876058412Z"></path><path d="M-84.13072063824123,42.42521462663013L-83.66596195919543,42.43144580208123L-83.55286479015007…

The D3 code I use to render them is the same, just pointing to the different data sources. This one points to the (working) states code:

d3.json("us-10m.v1.json", function (error, us) {
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter()
.append("path")
.attr("d", path).append("svg:title");

The files themselves seem to be identically formatted, with (of course) different data, so I am at a loss as to why the large differences in the way the svg code is generated. The two differences I see are that the U.S. states file has a "bbox" element that the state files lack, and the Michigan file has "properties" elements inside of the geometries collection.

1

1 Answers

3
votes

You problem is the opposite of this one. The US topojson is already projected (to a 960x600 pixel area) - the underlying coordinates in the topojson represent pixel coordinates. Maps that use pre-projected geographic features don't use d3 geographic projections. If drawing with a path generator, this looks like:

 var path = d3.geoPath(); // or
 var path = d3.geoPath().projection(null);

However, the Michigan data contains latitude/longitude pairs. If we convert your Michigan topojson to geojson (so we have human readable coordinates), we get values such as: [-87.86292721673836,45.35386708864823]. If we use a null projection and treat these as pixel values, the features will be to the left of the viewable SVG as the x values are negative. So we need a projection for our path:

var path = d3.geoPath().projection(someProjection);

I'm not sure what projection you would want - the pre-projected US data uses an Albers projection, so if we wanted to replicate this and scale and center Michigan, we could use:

var projection = d3.geoAlbers()
   .fitSize([width,height],geojsonObject);   // width/height of SVG/canvas
var path = d3.geoPath().projection(projection);

With an Albers it is tailored to the US, if using fitSize or fitExtent on other parts of the world, results may not be as expected, projection rotation must be set to account for different locations.

There are other ways that can be used to center/scale the map but this is covered in many questions/answers. But, to display geographic data, you need to project it from spherical coordinates to planar, and any d3 geoProjection can do this.