reversing order of d3.zoom scale and translate - d3.js

If you click the red button in this example:
https://bl.ocks.org/interwebjill/fe782e6f195b17f6fe6798a24c390d90
you can see that the chart translates so that the circle is in the center and then zooms in to a specified level (reclicking on the button zooms back out). Translating and then zooming in this way leaves a gap on the left that I would rather not have. How might I change the code so that the chart zooms first and then translates to center so that I don't have this gap in the chart?
I have tried reversing the order of the scale and translate in both the zoom definition and the zoomToExtent function but there is no different in effect.

The ultimate source of the problem is d3.interpolateZoom. This interpolator has scale interpolate faster than translate - even though they mostly both are transitioning at the same time. The pattern implemented with d3.interpolateZoom is based on this paper.
Because scale and translate both interpolate differently in d3.interpolateZoom, you get a gap in the side of your chart as the scale decreases/increases more rapidly than the translate values.
d3.interpolateZoom is used when you call the zoom on a transition.
However, if you apply a transform directly on a transition using .attr(), the d3 transition will use d3.interpolateString, which will search the start and end strings for corresponding numbers and use d3.interpolateNumber on those. This will apply the same interpolation to both scale and translate.
Using both methods we can compare the discrepancy between d3.interpolateZoom and d3.interpolateString. Below the black rectangle uses d3.interpolateString while the orange rectangle uses d3.interpolateZoom. Click on a rectangle to start the transition:
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 300);
var g1 = svg.append("g"), g2 = svg.append("g");
var zoom1 = d3.zoom().on("zoom", function() {
g1.attr("transform", d3.event.transform);
});
var zoom2 = d3.zoom().on("zoom", function() {
g2.attr("transform", d3.event.transform);
});
g1.call(zoom1.transform, d3.zoomIdentity
.translate(150, 100)
.scale(2));
g2.call(zoom2.transform, d3.zoomIdentity
.translate(150,100)
.scale(2));
g1.append("rect")
.attr("x", 20)
.attr("y", 20)
.attr("width", 50)
.attr("height", 50);
g2.append("rect")
.attr("x", 22)
.attr("y", 22)
.attr("width", 46)
.attr("height",46)
.attr("fill","orange");
d3.selectAll("rect").on("click", function() {
g1.transition()
.duration(6000)
.attr("transform", d3.zoomIdentity)
.on("end", function() {
d3.select(this).call(zoom1.transform, d3.zoomIdentity);
})
g2.transition()
.duration(6000)
.call(zoom2.transform, d3.zoomIdentity)
});
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>
Where the first rectangle transitions the transform with .attr(), we need to call the zoom afterwards to ensure the zoom has the current transform, we don't need to in this example, but if you wanted to use the zoom after the transform you need to do this
Comparing these two we get:
(Y axis indicates percentage remaining in transition from start attribute to end attribute)
You want scale and translate to move simultaneously at the same rate when transitioning. We can do this if we use a tweening function. Unlike above we can't just use transition().attr("transform",newTransfrom) because you are also drawing canvas and updating the axis. So we'll need to create our own tweening function that can use the current transform and scale, apply it to the axis, canvas, and markers.
For example, rather than calling the zoom (which will use d3.interpolateZoom):
function zoomToExtent(d0, d1) {
zoomRect.call(zoom).transition()
.duration(1500)
.call(zoom.transform, d3.zoomIdentity
.translate(-xSVG(d0), 0)
.scale(width / (xSVG(d1) - xSVG(d0))));
}
Instead, we can use a tweening function which controls the element's transform and applies the same interpolator to scale and translate:
function zoomToExtent(d0, d1) {
//get transition start and end values:
var startScale = d3.zoomTransform(zoomRect.node()).k;
var startTranslate = d3.zoomTransform(zoomRect.node()).x;
var endTranslate = -xSVG(d0);
var endScale = width / (xSVG(d1) - xSVG(d0));
zoomRect.call(zoom).transition()
.duration(1500)
.tween("transform", function() {
var interpolateScale = d3.interpolateNumber(startScale,endScale);
var interpolateTranslate = d3.interpolateNumber(startTranslate,endTranslate);
return function(t) {
var t = d3.zoomIdentity.translate(interpolateTranslate(t),0).scale(interpolateScale(t));
zoomed(t);
}
})
.on("end", function() { // update the zoom identity on end:
d3.select(this).call(zoom.transform, d3.zoomIdentity
.translate(endTranslate, 0)
.scale(endScale));
})
}
You may notice I'm passing a transform value to the zoomed function, since there is no d3.event.transform for this, we need to modify the zoomed function to use the passed parameter if available, otherwise to fall back on the event transform:
function zoomed(transform) {
var t = transform || d3.event.transform;
...
Altogether, that might look something like this.
For another comparison between the two transitioning methods, I've created a gridded comparison that can be toggled between the two zoom identities:
var svg = d3.select("body").append("svg")
.attr("width", 510)
.attr("height", 310);
var g1 = svg.append("g");
var g2 = svg.append("g");
var rectangles1 = g1.selectAll()
.data(d3.range(750))
.enter()
.append("rect")
.attr("x", function(d) { return d%25*20; })
.attr("y", function(d) { return Math.floor(d/25)*20; })
.attr("width", 20)
.attr("height", 20)
.attr("fill","#ccc")
.attr("stroke","white")
.attr("stroke-width", 2);
var rectangles2 = g2.selectAll()
.data(d3.range(750))
.enter()
.append("rect")
.attr("x", function(d) { return d%25*20; })
.attr("y", function(d) { return Math.floor(d/25)*20; })
.attr("width", 20)
.attr("height", 20)
.attr("fill","none")
.attr("stroke","#444")
.attr("stroke-width", 1);
var startZoom = d3.zoomIdentity
.translate(-250,-200)
.scale(4);
var endZoom = d3.zoomIdentity
.translate(-100,-100)
.scale(5);
var zoom1 = d3.zoom().on("zoom", function() { g1.attr("transform", d3.event.transform); });
var zoom2 = d3.zoom().on("zoom", function() { g2.attr("transform", d3.event.transform); });
g1.call(zoom1.transform, startZoom);
g2.call(zoom2.transform, startZoom);
var toggle = true;
svg.on("click", function() {
toggle = !toggle;
g1.transition()
.duration(5000)
.call(zoom1.transform, toggle ? startZoom: endZoom)
g2.transition()
.duration(5000)
.attr("transform", toggle ? startZoom: endZoom)
.on("end", function() {
d3.select(this).call(zoom2.transform, toggle ? startZoom: endZoom);
})
})
rect {
opacity: 0.5;
}
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>

Related

Rotating an SVG image using D3.JS not working

I've been trying to rotate some gears in an svg image clockwise repeatedly using D3.js but I can't seem to understand what's wrong with the code or the image. I was able to translate and rotate each gear using this code below...
d3.selectAll(".st6")
.transition()
.delay(function(d, i, n) { return i * 50; })
.on("start", function repeat() {
d3.active(this)
.transition()
.duration(2500)
.attr('transform', 'rotate(0)')
.transition() //And rotate back again
.duration(2500)
.attr('transform' , 'rotate(90) ')
.on("start", repeat); //at end, call it again to create infinite loop
});
But when I tried using the same code that did rotate a text repeatedly for me, the image became static and not moving again...
here is the code that rotate a text repeatedly for me...
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//draw the text
holder.append("text")
.style("fill", "black")
.style("font-size", "56px")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", "translate(300,150) rotate(0)")
.text("Hi, how r u doing");
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
}, timeInterval);
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Here is what I tried replicating the above method but to no avail...
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
holder.append("svg:image")
.attr("image-anchor", "middle")
.attr("xlink:href", "produc.svg")
.attr("transform", "translate(300,150) rotate(0)")
// Initial starting angle of the text
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
},timeInterval);
var n;
// update the element
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Here is a working example on CodePenclick here
As for the problem with your code (without considering if it is "a sledgehammer to crack a nut"):
You're taking working code that rotates text and not fully updating it to reflect that you are now working with an image. You have updated the append statement to append an image, but you haven't updated the update function to select that image - it's still looking for a text element:
function update(n) {
// rotate the text
holder.select("text")
.attr("transform", "translate(300,150) rotate("+n+")");
}
Since there is no longer a text element, this function doesn't select anything and consequently, doesn't set any element's transform. AS noted in the comment you need to select the image:
function update(n) {
// rotate the text
holder.select("image")
.attr("transform", "translate(300,150) rotate("+n+")");
}
As seen below:
var width = 600;
var height = 300;
var holder = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
holder.append("svg:image")
.attr("image-anchor", "middle")
.attr("xlink:href", "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/compass.svg")
.attr("transform", "translate(200,20) rotate(0)")
.attr("width", 100)
// Initial starting angle of the text
var i = 0;
var timeInterval = 10;
setInterval(function(){
i += 1;
update(i % 360)
},timeInterval);
// update the element
function update(n) {
// rotate the text
holder.select("image")
.attr("transform", "translate(200,20) rotate("+n+",50,50)");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

How do I use event listeners to swtich my d3 bar chart where I only see one and not both?

I have 2 buttons that i want to use to control what data set I am using for my bar chart. Right now I can click on one and it shows my d3 graph without problems. But when I want to switch to the other graph, I click on the button and it shows me that graph on top of my previous graph. How do I make it so that when I switch between graphs, it only shows me one graph.
var djockey = 'top5jockey.csv'
var dtrainer = 'top5trainer.csv'
// Define SVG area dimensions
var svgWidth = 1500;
var svgHeight = 1000;
// Define the chart's margins as an object
var chartMargin = {
top: 30,
right: 30,
bottom: 130,
left: 30
};
// Define dimensions of the chart area
var chartWidth = svgWidth - chartMargin.left - chartMargin.right;
var chartHeight = svgHeight - chartMargin.top - chartMargin.bottom;
// Select body, append SVG area to it, and set the dimensions
var svg = d3.select("body")
.append("svg")
.attr("height", svgHeight)
.attr("width", svgWidth);
// Append a group to the SVG area and shift ('translate') it to the right and to the bottom
var chartGroup = svg.append("g")
.attr("transform", `translate(${chartMargin.left}, ${chartMargin.top})`);
var btnj = document.getElementById("Jockey")
btnj.addEventListener('click', function(e){
change(e.target.id)
})
var btnt = document.getElementById("Trainer")
btnt.addEventListener('click', function(e){
change(e.target.id)
})
function change(value){
if(value === 'Jockey'){
update(djockey);
}else if(value === 'Trainer'){
update(dtrainer);
}
}
function update(data){
d3.csv(data).then(function(data) {
console.log(data);
// Cast the hours value to a number for each piece of tvData
data.forEach(function(d) {
d.Count = +d.Count;
});
// Configure a band scale for the horizontal axis with a padding of 0.1 (10%)
var xScale = d3.scaleBand()
.domain(data.map(d => d.Name))
.range([0, chartWidth])
.padding(0.1);
// Create a linear scale for the vertical axis.
var yLinearScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.Count)])
.range([chartHeight, 0]);
// Create two new functions passing our scales in as arguments
// These will be used to create the chart's axes
var bottomAxis = d3.axisBottom(xScale);
var leftAxis = d3.axisLeft(yLinearScale).ticks(10);
// Append two SVG group elements to the chartGroup area,
// and create the bottom and left axes inside of them
chartGroup.append("g")
.call(leftAxis);
chartGroup.append("g")
.attr("class", "x_axis")
.attr("transform", `translate(0, ${chartHeight})`)
.call(bottomAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
// Create one SVG rectangle per piece of tvData
// Use the linear and band scales to position each rectangle within the chart
chartGroup.selectAll("#bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => xScale(d.Name))
.attr("y", d => yLinearScale(d.Count))
.attr("width", xScale.bandwidth())
.attr("height", d => chartHeight - yLinearScale(d.Count));
}).catch(function(error) {
console.log(error);
})
};
D3 has a function allowing you to remove all svg elements. Basically, you select the svg, then run .remove() at the top of your event listener. It will clear out all svg elements.

D3.zoom jumps when using mouse wheel after programmatically zoom

When I zoom to a specific location on a mouse click and then try to pan or when I'm using the mouse wheel, the zoom behavior jumps. It seems that my zoom level is being restored like it was before the mouse click.
Here is my event handler:
function click(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
svgContainer.transition()
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")");
}
And this is how I "activate" my zoom and pan functionalities.
var svgContainer = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function () {
svgContainer.attr("transform", d3.event.transform)
}))
.append("g")
...
svgContainer.selectAll(null)
.data(feat.features)
.enter()
.append("path")
.on("click", click)
...
Background
You are manipulating the transform applied to your elements in the click function but not updating the zoom state to reflect this. d3.zoom does not track an element's transform at all. So when you modify the transform attribute of an element independently of d3.zoom, d3.zoom no longer "knows" what the transform is of that element - it remains unchanged. D3.zoom does track zoom state - internally and independently of any element's transform attribute.
It might seem odd that d3.zoom doesn't track an element's transform, but there is good reason. d3.zoom isn't always used to manipulate the transform of an element, it may alter something like element width or a scale while that element's transform remains unchanged. Here's a bl.ock of mine where d3.zoom here manipulates only the radius of circles on canvas.
Problem
As you don't update the zoom state in your click event, d3.zoom picks up where it was last left when applying a new zoom event, which explains your symptom: "It seems that my zoom level being restored like it was before the mouse click."
Solution
So, what do you need to do? You need to pass your zoom transform to d3.zoom and trigger a zoom event. This way d3.zoom is apprised of the current zoom state. Luckily there is a method for this. We take a d3.zoomIdentity (k=1,x=0,y=0) and translate and scale as appropriate, we can translate, scale, and then translate again as you have done too:
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
And then we apply the zoom by calling zoom.transform which according to the docs:
sets the current zoom transform of the selected elements to the
specified transform, instantaneously emitting start, zoom and end
events. If selection is a transition, defines a “zoom” tween to the
specified transform using d3.interpolateZoom, emitting a start event
when the transition starts, zoom events for each tick of the
transition, and then an end event when the transition ends (or is
interrupted). (link)
We can call zoom.transform with:
// Apply the zoom and trigger a zoom event with a provided zoom transform:
svg.call(zoom.transform, transform);
So if this is analagous to what you have:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
var transform = "translate(" + 250 + "," + 150 + ")scale(" + k + ")translate(" + -x + "," + -y + ")";
g.transition()
.attr("transform",transform)
.duration(1000);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
This can be analagous to your solution:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
// Apply the zoom and trigger a zoom event:
svg.call(zoom.transform, transform);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>

how to zoom only map and smiley could stay at same place and size

I having problem of zoom over map. The actual problem is when i zoom map, the location showing on map using smiley could also zoom but i don't want to zoom smiley. It could stay at same size and place. Sometime smiley get overlap so to avoid this i am trying to solve the above problem but i don't have idea how to transform attribute contains many things like images and text on map of d3.js. Please have a look at jsfiddle link and you can see that at japan 3 smiley get overlap and keep overlapped even after zooming map.
My JSfiddle link
my code is following:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: white;
stroke-width: 0.25px;
fill: grey;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
height = 500;
var data = [
{
"code":"TYO",
"city":"TOKYO",
"country":"JAPAN",
"lat":"35.68",
"lon":"139.76"
},
{
"code":"OSK",
"city":"Osaka",
"country":"JAPAN",
"lat":" 34.40",
"lon":"135.37"
},
{
"code":"HISH",
"city":"Hiroshima",
"country":"JAPAN",
"lat":"34.3853",
"lon":"132.4553"
},
{
"code":"BKK",
"city":"BANGKOK",
"country":"THAILAND",
"lat":"13.75",
"lon":"100.48"
},
{
"code":"DEL",
"city":"DELHI",
"country":"INDIA",
"lat":"29.01",
"lon":"77.38"
},
{
"code":"SEA",
"city":"SEATTLE",
"country":"USA",
"lat":"38.680632",
"lon":"-96.5001"
}
];
var projection = d3.geo.mercator()
.center([0, 5 ])
.scale(200)
.rotate([-180,0]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var path = d3.geo.path()
.projection(projection);
var g = svg.append("g");
// load and display the World
d3.json("world-110m2.json", function(error, topology) {
// load and display the cities
function drawMap(data){
var circle = g.selectAll("circle")
.data(data)
.enter()
.append("g")
circle.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", 5)
.style("fill", "red");
circle.append("image")
.attr("xlink:href", "http://fc08.deviantart.net/fs71/f/2013/354/8/7/blinking_smiley__animated__by_mondspeer-d6ylwn3.gif")//http://t2.gstatic.//com/images?q=tbn:ANd9GcT6fN48PEP2-z-JbutdhqfypsYdciYTAZEziHpBJZLAfM6rxqYX";})
.attr("class", "node")
.attr("x", function(d) {
return (projection([d.lon, d.lat])[0]) - 8;
})
.attr("y", function(d) {
return (projection([d.lon, d.lat])[1])-8;
})
.attr("width",20)
.attr("height",20)
//});
}
g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries)
.geometries)
.enter()
.append("path")
.attr("d", path)
drawMap(data);
});
// zoom and pan
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("circle")
.attr("d", path.projection(projection));
g.selectAll("path")
.attr("d", path.projection(projection));
});
svg.call(zoom)
</script>
</body>
</html>
Any body help me to zoom only map image not smiley
Implement semantic zooming :)
Try use this example to change your code :) :
Semantic zoom on map with circle showing capital
JSFIDDLE : http://jsfiddle.net/xf7222dg/2/
The code below shrinks the 'circles' depending on scale
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("circle")
.attr("r", function(){
var self = d3.select(this);
var r = 8 / d3.event.scale; // set radius according to scale
self.style("stroke-width", r < 4 ? (r < 2 ? 0.5 : 1) : 2); // scale stroke-width
return r;
});
});
Here is it working with your smileys: http://jsfiddle.net/dmn0d11f/7/
You have to change the 'width' of the nodes (images) not the radius like with the circles. So select the nodes and instead of changing 'r' change 'width' :
g.selectAll(".node")
.attr("width", function(){
var self = d3.select(this);
var r = 28 / d3.event.scale; // set radius according to scale
self.style("stroke-width", r < 4 ? (r < 2 ? 0.5 : 1) : 2); // scale stroke-width
return r;
});

trying to rotate a globe in d3, centering on a particular country

There is probably a simple answer to this question... . I'm using d3 to create a globe, showing all countries. I also have a div with the name of all the countries in it. When I click on a country name, I want the globe to spin to that country. But I'm having trouble getting the syntax right. Can anyone help, please?
var feature;
var projection = d3.geo.azimuthal()
.scale(zoom)
.origin([-71.03,42.37])
.mode("orthographic")
.translate([380, 450]);
var circle = d3.geo.greatCircle()
.origin(projection.origin());
var scale = {
orthographic: 380,
stereographic: 380,
gnomonic: 380,
equidistant: 380 / Math.PI * 2,
equalarea: 380 / Math.SQRT2
};
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#globe").append("svg:svg")
.attr("width", 800)
.attr("height", 800)
.on("dblclick", dblclick)
.on("mousedown", mousedown);
var g = svg.append("g");
d3.json("simplified.geojson", function(collection) {
g.append("g")
.attr("id", "countries")
g.append("g")
.selectAll("path")
.data(collection.features)
.enter().append("svg:path")
.attr("d", clip)
.attr("id", function(d) { return d.properties.ISO3; })
.attr("fill", function(d) { return d.properties.FILL; }) //change color and make clickable if data on this country exists
.on("mouseover", pathOver)
.on("mouseout", pathOut)
.on( "dblclick", dblclick)
.on("mousewheel.zoom", null)
.on("click", click);
feature = svg.selectAll("path");
feature.append("svg:title")
.text(function(d) { return d.properties.NAME; });
//here is where I want to be able to click a country name in the div and have the globe rotate to that country:
$('.represented').click(function(){
var countryabbrev = $(this).attr('id');
projection.origin(projection.invert(#path.centroid(#CAN))); //this line is wrong
refresh(1500);
showPerson(countryabbrev)
});
I've gotten it to find the country and rotate. Now the rotate is sketchy, but at least there's progress:
$('.represented').click(function(){
var countryabbrev = $(this).attr('id');
getCentroid(d3.select("#" + countryabbrev));
//projection.origin(projection.invert(#path.centroid(#CAN)));
projection.origin(getCentroid(d3.select("#" + countryabbrev)));
refresh(1500);
//showPerson(countryabbrev)
});
function getCentroid(selection) {
// get the DOM element from a D3 selection
// you could also use "this" inside .each()
var element = selection.node(),
// use the native SVG interface to get the bounding box
bbox = element.getBBox();
// return the center of the bounding box
return [bbox.x + bbox.width/2, bbox.y + bbox.height/2];
}

Resources