1

I am attempting to use D3 to display a GeoJSON map of San Francisco. I have the following files which I am serving using "http-server -c-1":

index.html:

<!DOCTYPE html>
<html>
  <head>
    <script src="http://d3js.org/d3.v4.min.js"></script>
  </head>
  <body>
    <script src="./d3Map.js"></script>
  </body>
</html>

d3Map.js:

const d3 = window.d3;

// Append a svg to the HTML body and set its height and width:
const svg = d3.select('body')
  .append('svg')
    .attr('height', 500)
    .attr('width', 500)
// Append the <g> element which is used for grouping SVGs:
const g = svg.append('g');

// Get the GeoJSON data and use it to setup the paths:
d3.json('./sfmaps/neighborhoods.json', (error, topology) => {
  // Setup the projection:
  const bounds = d3.geoBounds(topology);
  const centerX = d3.sum(bounds, (d) => d[0]) / 2;
  const centerY = d3.sum(bounds, (d) => d[1]) / 2;
  const projection = d3.geoMercator()
    .center([centerX, centerY]);

  // Create a geographic path generator and set its projection:
  const path = d3.geoPath()
    .projection(d3.geoMercator());

  g.selectAll('path')
    .data(topology.features)
    .enter()
      .append('path')
      .attr('d', path);
});

When I inspect the resulting page, I have:

<body>
  <svg>
    <g>
      <path d="..."></path>
      <path d="..."></path>
      ...
    </g>
  </svg>
</body

However, the displayed SVG is blank.

I suspected that the projection was not scaled or centered correctly so I tried omitting the .center(...), hard coding center with the latitude and longitude for San Francisco, and using .fitSize(...).

I am a little confused by the documentation's terminology. When it says a value should be a GeoJSON Object I'm not sure if that means it should be the whole JSON (what I named "topology" in my code), the features (topology.features), or an individual path (topology.features[0]). However, I have tried using all three and none of them worked or displayed an error in the console.

The GeoJSON file was made by someone else so I am fairly certain that it is correct.

Do you have any suggestions for what I might be doing wrong or what avenues I should pursue to debug this?

David Moneysmith
  • 799
  • 2
  • 8
  • 10

1 Answers1

0

Two issues:

  1. Applying the projection

You set a projection

  const projection = d3.geoMercator()
    .center([centerX, centerY]);

But then you do not use it:

  const path = d3.geoPath()
    .projection(d3.geoMercator()); // applies default mercator

Try:

  const path = d3.geoPath()
    .projection(projection);  // applies mercator with modified parameters
  1. Translate your projection

The default translate for a d3 projection is generally [480,250], which places the centering coordinate in the middle of an svg that is 960 pixels by 500 pixels. Your svg is 500 x 500, so you should use:

projection.translate([250,250])  

In regards to your question on geojson objects. A geojson object can be either a feature collection (the topology variable in your code) or a specific feature/shape within the feature collection's set of features: topology.features[0], but it can't be an array, like topology.features. It simply must be a valid single geojson object, see More than you ever wanted to know about geojson for more info on valid geojson objects.

If passing a geojson object, such as a feature collection to fitSize, your code might look like:

projection = d3.geoMercator()
    .fitSize([width,height],topology)

Note this doesn't change the centering coordinate, but translates and scales the map feature to fit the specified dimensions. If using this method you don't need to specify the translate as this method defines the translate itself.

If specifying the center coordinate with .center([long,lat]) (and not using fitSize), then you will need to specify the translate as in number 2 above.


A final note:

Geojson does not encode topology, the topojson format records the topology of features. When processed by topojson.js, topojson is converted into geojson. As a variable name, the use of topology infers the use of a topojson rather than a geojson.

Andrew Reid
  • 37,021
  • 7
  • 64
  • 83