KvothesLute
KvothesLute

Reputation: 195

D3 Map Projection Not Showing Map

Hi i am trying to create a map of the city of Birmingham, though i can see the paths have been generated and the data is being loaded. I do not see anything on the html page.

I have seen people use the projection function without setting center or translate and it would visual their maps but this has not worked for me.

I have looked into possible solutions and found centering your projection by the city you are interested in should help with getting the map to display properly but this did not helped. I also tried to play around with the scale but this also did not help.

Essentially my expected results was a map of Birmingham to be displayed in the middle of my svg object.

var w= 1400;
var h = 700;
var svg = d3.select("body").append("svg").attr("width",w).attr("height",h   );
var projection = d3.geoMercator().translate([w/2, h/2]).scale(100).center([1.8904,52.4862]);
var path = d3.geoPath().projection(projection);
var ukmap = d3.json("https://martinjc.github.io/UK-GeoJSON/json/eng/wpc_by_lad/topo_E08000025.json");

 // draw map
Promise.all([ukmap]).then(function(values){    
    var map = topojson.feature(values[0],values[0].objects.E08000025).features
    console.log(map);
    svg.selectAll("path")
        .data(map)
        .enter()
        .append("path")
        .attr("class","continent")
        .attr("d", path)
        .style("fill", "#f0e4dd") //steelblue
    });
    ```

Upvotes: 1

Views: 719

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38151

It looks like the path is in the middle of your svg:

enter image description here

It's just really small.

With a d3 Mercator projection scale of 100 you are displaying 360 degrees of longitude across 100 pixels. So, with an svg that is 1400 pixels across, you could be showing 14 earths. Not ideal for a city. If you up the scale value to 10000 you'll at least see your feature, but it's not quite centered and it's still pretty small, try values for center and scale like so:

.center([-1.9025,52.4862])
.scale(100000)

(keeping the translate the same)

Now we're getting somewhere:

enter image description here

But this is still tedium, we can simply use projection.fitExtent or projection.fitSize to do the scaling automagically:

Promise.all([ukmap]).then(function(values){    
    var map = topojson.feature(values[0],values[0].objects.E08000025)
    projection.fitSize([w,h],map);
    var features = map.features;

    svg.selectAll("path")
      .data(features)
      .enter()
      ...

This stretches the feature to fill the specified dimensions (it takes a geojson object, not an array, hence my slight restructuring). We can also specify a margin like so:

 projection.fitExtent([[100,100],[w-100,h-100]],map);

This provides a 100 pixel margin around the map so it doesn't touch the edge of the SVG.

Both of these methods, fitSize and fitExtent, automatically set the projeciton translate and scale, so we can actually skip setting the scale, center, and translate manually (translate and scale essentially do the same thing: one after projection and one before, respectively. It's usually easier to use both though when setting the projection parameters manually)

Upvotes: 2

Related Questions