Multiple maps with d3.js: change values of scale and center

一曲冷凌霜 提交于 2019-12-24 07:45:28

问题


I’m building a (d3 v4) cartographic visualization which allows the user to switch between many datasets (json files) and two different regions (administrative units of a country and smaller administrative units into its capital city). Actually the switch from one to another dataset on the initial country level works well, through buttons and jquery.

Problem: it’s a bit less convincing when switching to a map/dataset about the capital city, as the projection is initially set for the whole country and consequently the user has to zoom many times to visualize properly the map of the capital city. I would like to change the values of .scale and .center when calling the projection but after several trials I haven’t found how to do it.

As I only have two different regions to show, my intuition was to set first values of scale and center and to change them to other values (I know the values of .scale and .center I would like to use in both cases) when the user switches to a map of the capital city through a function. Is there any possibility to switch easily these values? Do you have any suggestion to solve this problem?

As I load the json file path into a function when the user clicks on the button to switch to another dataset, I was trying to load the value of scale the same way but I’m probably doing wrong. It seems that the part of the code about the projection can't be put in a function?

Thanks for your help!

Small part of my code:

var width = 1100, height = 770;

var projection = d3.geoConicConformal()
    .scale(19000) // value I would like to which when the region changes
    .center([4.45, 50.53]) // value I would like to which when the region changes
    .translate([width/2,height/2]);

var svg = d3.select( "#mapcontainer" )
    .append( "svg" )
    .attr("width", width)
    .attr("height", height)
    .style("border", "solid 1px black");

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

var color, jsonfile, legendtext;

function load (jsonfile, legendtext, color) {
    d3.selectAll(".currentmap").remove() ;
    d3.json(jsonfile, function(error, belgique) {
        g.selectAll("path")
        .data(belgique.features)
        .enter()
        .append("path")
        .attr("d", path)
        .style("stroke", "#fff")
        .attr( "class", "currentmap")
        .style("fill", function(d) {
            var value = d.properties.DATA;
            if (value) {return color(value);}
            else {return "rgb(250,110,110)"}
            });
            })
};

//one of the following function for each map
function BGQprovinces() { 
    jsonfile = "ATLAS/NewGeoJson/bgq-data1-provinces.json";
    legendText [= …];
    color = d3.scaleOrdinal()
                .domain( […])
        .range([…]);

    load(jsonfile, legendtext, color) ; 
    };

;

回答1:


There area few approaches to accomplish this.

fitSize and fitExtent

One is to modify the projection scale and translate as opposed to scale and center. This is nearly the same operation, but translate pans the projected plane and center will pan the unprojected plane. To do so you need to use projection.fitSize([width,height],geojsonObject), or projection.fitExtent([[x0,y0],[x1,y1]],geojsonObject). The latter will allow margins of say, the first coordinate provided is the top left and the second coordinate provided is the bottom right of a bounding box in which the feature will be constrained.

d3.json(jsonfile, function(error, belgique) {
     projection.fitSize([width,height], belgique);

    // now draw as you would:
    d3.selectAll(".currentmap").remove() ;
    g.selectAll("path")
    .data(belgique.features)
    .enter()
    .append("path")
    .attr("d", path)
    ...

Note that for showing all of a country you need to have a feature that shows the whole country or a feature collection that shows all the parts of a country. You cannot use an array with fitSize or fitExtent, if you have an array of features, you can create a feature collection by using:

var featureCollection = {"type":"featureCollection","features":featureArray}

For your case, I'd suggest using fitSize or fitExtent.

centroid

If you really wanted to modify the center attribute as opposed to translate, or perhaps you want to change the rotation (a more likely outcome for conic conformals in many parts of the world, Belgium should be fine), then you need the geographic coordinates of the center. One way of a handful to do this is to get the centroid of a feature from path.geoCentroid:

var centroid = path.geoCentroid(geojsonObject); 

Then use that to set the projection parameters to rotate:

projection.rotate([ -centroid[0],-centroid[1] ])
projection.center([0,0])

or to center:

projection.rotate([0,0])
projection.center(centroid)

Or a combination of both (depending on map projection type). Now you can apply fitSize or fitExtent, the feature is in the middle already, but now we can set the scale. The reason I suggest this as a potential answer is because not all projections, concic projections in particular, will give desired results by modifying only scale along with translate and/or center.

Of course for conic projections, you may need to find a way to set the parallels as well, but I'll leave that for another answer if it ever comes up.



来源:https://stackoverflow.com/questions/47411470/multiple-maps-with-d3-js-change-values-of-scale-and-center

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!