How to make to drawing a SVG with D3.js?

Viewed 89

I'm trying to draw a map in an SVG with the GEOJSON data that I bring from this API, the SVG paths are filled with the data, however, the SVG is blank, as can be seen when executing the code below. Notice the document.write, the data is returned correctly.

var svg = d3.select("svg")
d3.json('https://api.mocki.io/v1/d214eb47')
    .then(data => {      
       svg.append('g')
          .selectAll('path')
          .data(data.features)
          .enter()
          .append('path')
          .attr('d', d3.geoPath().projection(d3.geoMercator()))

          document.write(JSON.stringify(data));
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script>
<svg  width="600" height="600"></svg>

I tested it with another GEOJSON file, and managed to draw in SVG as seen when executing the code below:

const link = "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson";   

var svg = d3.select("svg")

d3.json(link)
        .then(data => {    
         svg.append("g")
        .selectAll("path")
        .data(data.features)
        .enter()
        .append("path")          
          .attr("d", d3.geoPath()
              .projection(d3.geoMercator())
          )
          
         //document.write('data ', JSON.stringify(data))
        
        });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script>
<svg width="600" height="600"></svg>

Does anyone know what's wrong with the first code snippet?

2 Answers

There is no issue in the data, the problem here is just that you're trying to map a very small region (San Francisco), unlike the world map in your second example. That said, you should set the scale and the center of the projection. In your case:

const projection = d3.geoMercator()
    .scale(100000)
    .center([-122.3, 37.75])

The resulting code:

var svg = d3.select("svg");
const projection = d3.geoMercator()
  .scale(100000)
  .center([-122.3, 37.75])
d3.json('https://api.mocki.io/v1/d214eb47')
  .then(data => {
    svg.append('g')
      .selectAll('path')
      .data(data.features)
      .enter()
      .append('path')
      .attr('d', d3.geoPath().projection(projection))
  })
path {
  fill: wheat;
  stroke: darkslateblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script>
<svg width="600" height="600"></svg>

But it's way easier just using fitExtent with your SVG width and height:

const projection = d3.geoMercator()
  .fitExtent([
    [0, 0],
    [600, 600]
  ], data)

And here is the resulting code:

var svg = d3.select("svg");
d3.json('https://api.mocki.io/v1/d214eb47')
  .then(data => {
    const projection = d3.geoMercator()
      .fitExtent([
        [0, 0],
        [600, 600]
      ], data)
    svg.append('g')
      .selectAll('path')
      .data(data.features)
      .enter()
      .append('path')
      .attr('d', d3.geoPath().projection(projection))
  })
path {
  fill: wheat;
  stroke: darkslateblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script>
<svg width="600" height="600"></svg>

Your GeoJSON file has very small areas that are displayed but with practically zero size shapes in given projection and default SVG viewBox.

Check how I've found the boundaries of the SVG G element with your geodata. I then used that to define SVG viewBox attribute. If SVG width or height are not set then SVG will expand to available "container" size. So you can add a surrounding DIV element and set its size if you wish (fitting SVG in your container and thus in your layout).

UPDATE: you can even change the SVG viewBox attribute later with javascript by using returned values from the getBBox() function. The other possibility in another answer is of course great, too (fitting extent of your SVG).

var svg = d3.select("svg");

const link = "https://api.mocki.io/v1/d214eb47";   
//const link = "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson";   

d3.json(link)
    .then(data => {      
    svg.append('g')
      .selectAll('path')
      .data(data.features)
      .enter()
      .append('path')
      .attr('d', d3.geoPath().projection(d3.geoMercator()));

        //console.log(data.features);
        console.log(document.getElementsByTagName("g")[0].getBBox());
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script>
<svg viewBox="152.95315551757812 140.7493133544922 0.421722412109375 0.4228363037109375"></svg>

Related