Search code examples
htmld3.jsscalinggeo

d3 geo map overflow


I try to display my map in a div - but i want this map to be restricted to the div-size - right now, it ignores the w, and h values and the vis css setting, too.

Does it make any sense to set the svg height and width? when i put the scale of the projection up, it will always be bigger than the org. svg.

var w = 200;
var h = 300;

// this will attach a svg-element with the class "map" to "mydiv"
var map = d3.select("#mydiv").append("svg:svg")
    .attr("width", w)
    .attr("height", h)
    .attr("class", "map"); 

var projection = d3.geo.albers()
    .origin([10.5,51])
    .scale(2000)
    .translate([100, 150]);

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

d3.json('germany.json', function(json) {
    map.selectAll('path')
        .data(json.features)
        .enter().append('path').attr('d', path);
});

and my css:

#vis {
    overflow: hidden;
    width: 350px;
    height: 220px;
}

Solution

  • There are basically two ways to scale D3 maps:

    • Scale the projection.

    • Put the map in a g element and set a transform on that element. This has the annoying side effect of scaling your stroke-width as well, so you might need to set a scaled stroke-width style on the g element.

    In both cases, you're scaling the map without direct relation to container element. I don't think there's a way to scale to the container automatically - I think you need to get the width of the container via JS (here I use jQuery, but there are pure-JS options as well), then set the scale factor accordingly:

    var containerWidth = $('#myDiv').width(),
        baseWidth = 1000,
        baseStrokeWidth = 1,
        scaleFactor = containerWidth/baseWidth;
    
    // now use scaleFactor in a transform or projection scale, e.g.:
    d3.select('g.mapContainer')
        .attr("transform", "scale(" + scaleFactor + ")")
        // and invert the scale to keep a constant stroke width:
        .style("stroke-width", baseStrokeWidth / scaleFactor + "px");
    

    If your container size will change, you additionally need to set a window.onresize handler to recalculate the scale and update the transform. This is one reason to use a transformed g attribute instead of a projection scale - to handle the window resize, you don't have to reproject your whole map, just scale the existing paths up or down, a much faster operation.