Zooming to countries via button

920 views
Skip to first unread message

Matt

unread,
Feb 23, 2012, 9:59:17 AM2/23/12
to d3-js
Hi,

I have rendered the world-countries.json file.

Now I have a select menu on the side which contains all the country
names selected from the json. What do I have to implement that the map
is zoomed when I select e.g. Spain. I'm a bit clueless here.

Lars Kotthoff

unread,
Feb 23, 2012, 10:02:51 AM2/23/12
to d3...@googlegroups.com, bentz.m...@googlemail.com
I've done this at http://www.larsko.org/v/igdp/

The basic idea is to get the bounding box of the country that has been selected
and set the parameters of the projection (zoom level and center) accordingly.

HTH,

Lars

Matt

unread,
Feb 23, 2012, 10:19:31 AM2/23/12
to d3-js
Hi Lars,

that looks really, really nice!

Could you maybe show me the parts of the coding which does those
things (get the bounding box, set parameters accordingly..). I looked
at you whole source code but unfortunately don't understand it
completely. I'm still new to D3.

On Feb 23, 4:02 pm, Lars Kotthoff <l...@larsko.org> wrote:
> I've done this athttp://www.larsko.org/v/igdp/

Lars Kotthoff

unread,
Feb 23, 2012, 10:32:45 AM2/23/12
to d3...@googlegroups.com, bentz.m...@googlemail.com
> Could you maybe show me the parts of the coding which does those
> things (get the bounding box, set parameters accordingly..). I looked
> at you whole source code but unfortunately don't understand it
> completely. I'm still new to D3.

The relevant lines are below, in the function updateMap(). The argument to that
function is a geojson fragment that can be either the entire map (which it is in
the beginning) or the fragment for a specific country (this way the
centering/zooming works).

proj.scale(Math.min(wscale*5, pscale));
tproj.scale(Math.min(wscale*5, pscale));
var off = tproj([-bounds0[0][0], -bounds0[1][1]]),
other = tproj([-bounds0[1][0], -bounds0[0][1]]),
len = [off[0] - other[0], off[1] - other[1]],
dx = off[0] + (width - clistw - 2*margin)/2 - len[0]/2,
dy = off[1] + (height - 7.5*margin)/2 - len[1]/2;
proj.translate([dx, dy]);
map.selectAll("path")
.transition().duration(1000)
.attr("d", path);

Figuring out the offsets between different projections is a bit hairy, but
relatively straightforward. You might want to check out this pull request
(https://github.com/mbostock/d3/pull/330) that adds a geotranslate method to the
projections that allows you to input user coordinates instead of projected
coordinates.

You might be able to get away with simply copying the updateMap() function and
adjusting/removing some of the variables in there (width, margin etc). You might
be able to use it directly from the code that generates your list of countries
if you're using .data() and the argument you're giving it is the actual json,
e.g.
.on("click", function(d) { updateMap(d); });

Lars

Matt

unread,
Feb 24, 2012, 6:57:26 AM2/24/12
to d3-js
I can't get it working. :/ Here is my code. I use the world-
countries.json file from the d3 data directory. Could you maybe try to
show me what I have to do in my code to zoom in a country if I choose
a select menu entry: e.g. Spain. So the only value I get is "Spain".
Or we can assume I have a json file from Spain extracted from the
world-countries.json file if that's simpler to explain.

var projection = null;

function
drawMap(mapJSON,locationJSON,streamJSON,lon,lat,x,y,scale,globeIndicator)
{
var margin = {top: 0, right: 0, bottom: 0, left: 0},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

if (globeIndicator==true)
projection =
d3.geo.azimuthal().scale(scale).origin([lon,lat]).mode("orthographic");
else
projection = d3.geo.mercator().scale(1743).translate([x, y]);

circle = d3.geo.greatCircle(),
path = d3.geo.path().projection(projection);

var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scale(projection.scale())
.on("zoom", move);

var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top
+ ")")
.call(zoom);

svg.append("rect")
.attr("class", "frame")
.attr("width", width)
.attr("height", height);

var g = svg.append("g")
.attr("id","states");

d3.json(mapJSON, function(collection) {
states = g.selectAll(".map")
.data(collection.features)
.enter().append("path")
.attr("class", "map")
.attr("d", path)
.attr("title", function(o){
return o.properties.name;
})
.on("mouseover", function() {
$(this).attr("class","state_selected");
})
.on("mouseout", function() {
$(this).attr("class","map");

})
.on("click", function(d) { })
;
});
}

Lars Kotthoff

unread,
Feb 24, 2012, 10:02:21 AM2/24/12
to d3...@googlegroups.com, bentz.m...@googlemail.com
Where do you generate the menu entries? I would use something like

menu.selectAll("option")
.data(collection.features)
.enter().append("option")
.text(function(d) { return d.name; })
.on("click", function(d) { updateMap(d); });

Lars

Ayesha Mahmood

unread,
Feb 18, 2013, 9:32:20 AM2/18/13
to d3...@googlegroups.com, bentz.m...@googlemail.com
This might be a dumb but what difference does it make if you apply the transformation to the project vs directly selecting the paths in question?

Lars Kotthoff

unread,
Feb 19, 2013, 4:01:45 AM2/19/13
to d3...@googlegroups.com, sill...@gmail.com, bentz.m...@googlemail.com
> This might be a dumb but what difference does it make if you apply the
> transformation to the project vs directly selecting the paths in question?

Not sure if I understand what you mean. You have to change the projection if you
want to "zoom in" on the country that you selected. If you want to highlight by
e.g. changing the colour, you don't need to change the projection but can
select the paths for a country and change their attributes.

Lars
Reply all
Reply to author
Forward
0 new messages