Search code examples
javascriptd3.jsgeojson

Project data from a single geojson when multiple geojson files are present


I have several geojson layers and I am layering on top of one another using groups. I am centering the map on a given geojson file using Mike's answer found here -> Center a map in d3 given a geoJSON object

My problem, and I believe that it has to do with the asynchronous nature of JavaScript, is that if I put the projection definition on a geojson that is quite large the other geojson features don't project correctly.

How can I properly translate and scale the map using this system? Is there a better way to go about this?

EDIT

I have found a quick and dirty way to do this but it won't allow me to change the projection programmatically. I have used the debugger to get the values of s and t, then I have to hard code those values at the beginning of the script after the projection is defined, before and geojson functions are called.

Here is my code:

var width = 1100,
    height = 850;

var projection = d3.geo.mercator();

var path = d3.geo.path()
    .projection(projection);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var landGroup = svg.append("g"),
    waterGroup = svg.append("g"),
    urbanGroup = svg.append("g"),
    borderGroup = svg.append("g");

d3.json("geoData/naLand.geojson", function(land) {

    projection
        .scale(1)
        .translate([0,0]);
    
    var b = path.bounds(land),
        s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
        t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
    
    projection
        .scale(s)
        .translate(t);

    landGroup.selectAll("path")
        .data(land.features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("fill", "#888888")
        .attr("stroke", "#111111")              
})

d3.json("geoData/naWater.geojson", function(water) {

    waterGroup.selectAll("path")
        .data(water.features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("fill", "#C9DBFF")
        .attr("stroke", "#0066FF")
        
})

d3.json("geoData/PNW_Municipalities.geojson", function(urban) {

    urbanGroup.selectAll("path")
        .data(urban.features)
        .append("path")
        .attr("d", path)
        .attr("fill", "#666666")
        .attr("stroke", "#666666")  
        
})

d3.json("geoData/CanadianBorder.geojson", function(border) {
        
    borderGroup.selectAll("path") 
        .data(border.features) 
        .enter() 
        .append("path") 
        .attr("d", path) 
        .attr("stroke", "#555555")
        .attr("stroke-width", "3")
        .attr("stroke-dasharray", "2,2")
        .attr("fill", "transparent")                
})

Solution

  • You can use queue.js to club all json loading and when all the json is loaded do the necessary action of drawing the projection.

    Something like below:

    queue()
        .defer(d3.json, 'geoData/naLand.geojson')//this will load the json for land
        .defer(d3.json, 'geoData/PNW_Municipalities.geojson')//muncipality
        .defer(d3.json, 'geoData/CanadianBorder.geojson')//canadaborder
        .await(makeMyMap);
    
    function makeMyMap(error, land, muncipality,canadaborder) {
    //make your map
    }
    

    Working example here

    Hope this helps!