This question already has an answer here:
Importing data from .csv using d3.js
(1 answer)
Closed 7 years ago.
All I want is to load a basic pie/donut chart, (actually a few bar plots in addition to that , too), but looks like there is some error in my . If I comment the same, I am able to serve the bare-bones python rendered page(but not the pie chart, though).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet" href="/static/css/style.css">
<style>
.legend{
font-size: : 12px;
}
rect{
stroke-width: 2;
}
#chart_Imp,#chart_Bid{
width: 50%;
}
.axis{
font: 10px sans-serif;
}
.axis path,
.axis line{
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div style="max-width: 800px; border:2px black solid">
<h1>{{haider_Imp}}</h1>
<div id="chart_Imp"></div>
<h1>{{haider_Bid}}</h1>
<div id="chart_Bid"></div>
</div>
<div id="Bar-plots">
<div id="Bar-plots 1st set">
<h1>{{haider_cpa}}</h1>
<div id="cpa"></div>
<h1>{{haider_cpc}}</h1>
<div id="cpc"></div>
<h1>{{haider_cpm}}</h1>
<div id="cpm"></div>
</div>
<div id="Bar-plots 2nd set">
<h1>{{haider_avgbid}}</h1>
<div id="avg_bid"></div>
<h1>{{haider_winrate}}</h1>
<div id="winrate"></div>
</div>
</div>
<script src="/static/script/d3.min.js"></script>
<script>
(function(d3){
'use strict';
var width = 360;
var height = 360;
var radius = Math.min(width,height)/2;
var donutWidth = 75;
var legendRectSize = 18;
var legendSpacing = 4;
var color = d3.scale.category20b();
var svg = d3.select('#chart_Imp')
.append('svg')
.attr('width',width)
.attr('height',height)
.append('g')
.attr('transform','translate('+(width/2)+','+(height/2)+')');
var arc = d3.svg.arc()
.innerRadius(radius-donutWidth)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) { return d.impsplit; })
.sort(null);
d3.csv('./static/summary.csv',function(error,dataset){
dataset.forEach(function(d) {
d.impsplit = +d.impsplit;
});
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d',arc)
.attr('fill',function(d,i)
{
return color(d.data.label);
});
var legend = svg.selectAll('.legend1')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
});
(window.d3);
var margin = {top: 20,right:20, bottom: 70, left: 40},
width = 600-margin.left-margin.right,
height = 300 - margin.top - margin.bottom;
</script>
</body>
</html>
It might be because you use your piechart dataset outside of the .csv callback function. I am under the impression that, when using the d3.csv() or d3.tsv() functions, you need to use the retrieved data in the callback function. You however, use your data outside the callback function.
check out this answer, it might help out.
Related
I happened to play around with the D3js-Library to visualize some SQL-JSON_LD data and want to do the following:
attach individual id-TAG as well as data-set (Matrix with various elements) to each slice
My Code right now looks like this
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = {
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"},};
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) { return parseInt(d.value); })))
.attr("id", function(d, i) { console.log('CP1'); return "arc-" + arc_ids[i]; }) // This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
on("click", function(d){console.log(d.id);console.log(d.data.toString())}); //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
I also checked this1, this2 and this3 as they seemed promising, but it still does not work for me.
Afterwards I want to use the attached data of an arc to print it into another svg-graphic. But first adressing has to work.
And I'm sorry for the post with more than one specific question!
Thank you for your help!
you must append the path before give it an id or data
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = [
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"}];
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) {
return parseInt(d.value);
})))
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("id", function(d, i) { return "arc-" + arc_ids[i]; })
// This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
.on("click", function(d){
console.log(this.id);
console.log(d.data.toString())
}) //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
I'm new to web stuff but even I know this is a stupid question... I still can't figure out what I'm doing wrong though. The code on the site here: http://bl.ocks.org/mbostock/2206590 seems as if you could copy and paste it into an html document and merely modify the link to the us.json so that it points to the full file path. However, the code merely pulls up a blank page.
The inspection of the page source code on the demo ( http://bl.ocks.org/mbostock/raw/2206590/ )is the exact same as the code provided on the main page. What am I missing to implement this??
Thanks!!!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaa;
}
#states .active {
fill: orange;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-width: 1.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500,
centered;
var projection = d3.geo.albersUsa()
.scale(1070)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var g = svg.append("g");
d3.json("/mbostock/raw/4090846/us.json", function(error, us) {
if (error) throw error;
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
});
function clicked(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;
}
g.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
</script>
Download the us.json file and put it in the same directory as your index.html. Then change the path to the us.json file:
d3.json("us.json", function(error, us) {
This worked for me. If you are using Chrome and accessing the index.html file on your local computer, not from a remote webserver, you will need to run a local webserver for this to work. If you just try to open the index.html file in Chrome, it won't display properly due to Chrome's local file restrictions. It needs to be accessed through a webserver.
Hope this helps.
I have zero experience using d3 and I use Javascript and jQuery only sporadically.
I am trying to create a simple scatterplot with a slider in d3 (and jQuery). The purpose of the slider is to select the dataset to plot. I have a JSON object all_data, a list of maps. For each map in the list, I use the "SI" and "F1" lists as x and y values, respectively.
When the slider is moved, I call the plotData() function with as argument an integer number that represents the index of the dataset in all_data (So plotData(5) would plot the 6th dataset in all_data).
Now, for some reason, the plot axes construct nicely, but the data points do not plot. I have tried to debug the code by placing console.log(data) and
function (d,i) { console.log(d); return x(d['SI']) at relevant sections in the code (see the comment lines). The data object contains data at all points in the function. The problem seems to arise after g.selectAll("scatter-dots") part. I suspect the final call to the function plotting the 'dots' is not called, but I cannot figure out why.
My question is: how can I fix my code such that the data points are added to the plot? Any other tips on coding, design or performance improvements are more than welcome - here to learn.
EDIT: I use a custom d3.slider extension from GitHub
var all_data = [{"REGRESSION_Y": [ list_of_floats ], "F1": [ list_of_floats ], "SI": [ list_of_floats ], "f+": some_float_val }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<link rel="stylesheet" href="d3/d3-slider-master/d3.slider.css" />
<link rel="stylesheet" href="plot.css" />
<script type="text/javascript" src="jquery/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="d3/d3.v3.min.js"></script>
<script type="text/javascript" src="json_data.js"></script>
<script type="text/javascript" src="d3/d3-slider-master/d3.slider.js"></script>
</head>
<body>
<h2><span id="slider3text">Data points selected: 5</span></h2>
<div id='container' style='width: 75%; height: 100%' class='centered'>
<div id="plotfield" style='width: 100%; height: 90%'></div>
<div id="slider3" style='height: auto'></div>
</div>
</body>
<script type="text/javascript">
d3.select('#slider3').call(d3.slider().axis(true).min( 5 ).max( all_data.length-1 ).step(1).on("slide", function(evt, value) {
d3.select('#slider3text').text("Data points selected: " + value);
plotData(value)
}));
</script>
<script type="text/javascript">
function plotData(i) {
var data = all_data[i]
$("#plotfield").empty()
var margin = {top: 50, right: 5, bottom: 80, left: 5}
, width = $("#container").width() - margin.left - margin.right
, height = $("#container").height() - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d['SI']; })])
.range([ 0, width ]);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d['F1']; })])
.range([ height, 0 ]);
var chart = d3.select('#plotfield')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis);
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var g = main.append("svg:g");
//console.log(data), this worked here
g.selectAll("scatter-dots")
.data(data)
.enter().append("svg:circle")
// I have tried function (d,i) { console.log(d); return x(d['SI'])
//in the below anonymous functions, but that doesn't yield an output,
//causing me to suspect that the functions are not executed.
.attr("cx", function (d,i) { return x(d['SI']); } )
.attr("cy", function (d) { return y(d['F1']); } )
.attr("r", 3);
}
and the CSS file:
.centered {
position: fixed;
top: 50%;
left: 50%;
/* bring your own prefixes */
transform: translate(-50%, -50%);
}
body {
font: 11px sans-serif;
}
.tooltip {
position: absolute;
width: 200px;
height: 28px;
pointer-events: none;
}
.axis line, .axis path {
shape-rendering: crispEdges;
stroke: black;
fill: none;
}
circle {
fill: steelblue;
}
From what I see the data[i] returned an object. This is of course not a problem, but the d3 method enter() only supports arrays.
This means that you cannot have things like d['f1'] when using enter() to add data. Converting them into arrays before using them fixes this and you can use d (the array entry) or i the array entry index.
The problem turned out to be the fact dat the var data = all_data[i] returned an object, instead of an array. A colleague of mine provided the following edit to make it work. (Also some other code was changed to add a trend line to the script).
<script type="text/javascript">
function plotData(i) {
// extracting data
var data = all_data[i];
var REGRESSION_Y = data.REGRESSION_Y;
var F1 = data.F1;
var SI = data.SI;
// removing previous plot
$("#plotfield").empty()
// margins and spacings (slightly larger, y-axis was invinsible)
var margin = {top: 50, right: 15, bottom: 80, left: 15}
, width = $("#container").width() - margin.left - margin.right
, height = $("#container").height() - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, d3.max(SI)])
.range([ 0, width ]);
var y = d3.scale.linear()
.domain([0, d3.max(F1)])
.range([ height, 0]);
var chart = d3.select('#plotfield')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis);
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
// append extra g part in svg and append to it
var g = main.append("svg:g")
// append the line
g.append('line')
.attr('x1', x(d3.min(SI)))//function(d, i) { return x(d[]) + i; })
.attr('x2', x(d3.max(SI)))//(2)//function(d, i) { return y(d['REGRESSION_Y']); });
.attr('y1', y(d3.min(REGRESSION_Y)))
.attr('y2', y(d3.max(REGRESSION_Y)))
.style('stroke', '#1B3277')
.style('stroke-width','1')
// append the dots
g.selectAll("scatter-dots")
.data(SI)
.enter().append("svg:circle")
.attr("cx", function (d,i) { return x(SI[i]); } )
.attr("cy", function (d,i) { return y(F1[i]); } )
.attr("r", 3);
}
</script>
I am trying to plot a png map file with graticules. The graticule extent should correspond to the width and height of the map file (see end of script). Although the left and upper extent show correctly, the lower and right extent do not correspond to the map dimensions.
I also played around with the extent values in the map function and only the left and upper extent are responsive.
Any suggestions?
<!doctype html>
<meta charset="utf-8">
<script src="./js/d3.js"></script>
<script src="./js/topojson.js"></script>
<script src="./js/jquery3.1.0.min.js"></script>
<style>
.MapPad {
padding: 30px 30px 30px 30px;
}
.graticule {
fill: none;
stroke: #000;
stroke-opacity: .15;
}
.graticule.outline {
stroke: black;
stroke-opacity: 1;
stroke-width: 2px;
stroke-dasharray: initial;
}
.LonLatLabel {
font-family: helvetica;
font-size: 22px;
dominant-baseline: central;
text-anchor: middle;
</style>
<body>
<div id='cont1_1'></div>
<script charset="utf-8">
//The function to plot the maps
function plotMaps (container, width, height, rasterBounds, demFile){
var projection = d3.geoMercator()
.scale(1)
.translate([0, 0]);
var b = [projection(rasterBounds[0]), projection(rasterBounds[1])],
s = 1 / 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]
//update projection
projection
.scale(s)
.translate(t)
// geo path generator
var path = d3.geoPath()
.projection(projection)
var map = d3.select(container).append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'MapPad');
//define the data layers before drawing to ensure the order of appearance
var gratLines = map.append('g');
var demLayer = map.append('g');
var samplPointsLayer = map.append('g');
var outline = map.append('g');
//make the graticule
var graticule = d3.geoGraticule().extent([[rasterBounds[0][0], rasterBounds[1][0]], [rasterBounds[0][1], rasterBounds[1][1]]]).step([1, 1]);
gratLines.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// get the coordinates of the line paths and use them as labels
map.selectAll('text')
.data(graticule.lines())
.enter().append("text")
.text(function(d) {
if (d.coordinates[0][0] == d.coordinates[1][0]) {return (d.coordinates[0][0]);}
else if (d.coordinates[0][1] == d.coordinates[1][1]) {return (d.coordinates[0][1]);}
})
.attr("class","LonLatLabel")
.attr('transform', function(d) { return ('translate(' + projection(d.coordinates[0])[0] + ',' + projection(d.coordinates[1])[1] + ')')
});
//outline of the map
outline.append("path")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", path);
/*var color = d3.scale.ordinal()
.domain(["1", "2", "3"])
.range(["#ffd633", "#aaff00" , "#267300"]);
*/
demLayer.append('svg:image')
.attr('xlink:href', demFile)
.attr('width', width)
.attr('height', height);
d3.json('SamplingPoints.json', function(err, data) {
samplPointsLayer.selectAll('circles')
.data(data.features)
.enter().append('circle')
.attr('r', 5)
.each(function(d) {
var lonlat = projection(d.geometry.coordinates);
d3.select(this)
.attr('cx', lonlat[0])
.attr('cy', lonlat[1])
.style('fill', 'black')
.style("opacity", .5)
});
});
}
//calculate the number with which the size of each map should be divided
var mainWidth = 230
//Plot the maps in each div
//Alps
var widthAlps = 4665;
var heightAlps = 3589;
var resCoefAlps = widthAlps/mainWidth
var rasterBoundsAlps = [[ 5.907077970880465 , 45.29815864865324 ] , [ 11.330836684119511 , 48.15780097787413 ]];
plotMaps('#cont1_1', widthAlps/resCoefAlps, heightAlps/resCoefAlps, rasterBoundsAlps, 'dem_alps.png');
</script>
</body>
Here's the result:
enter image description here
Issue solved! The [xmin, ymin], [xmax, ymax] values in the d3.geoGraticule().extent() function should be:
[rasterBounds[0][0], rasterBounds[0][1]], [rasterBounds[1][0], rasterBounds[1][1]]]
I'm trying to modify the example of a zoomable map of the United States, provided by mbostock to fit with Europe.
At the point I've been able to get it sort of, it's so small in the beginning!
I've looked at everything trying to change the presentation size in the beginning, i.e. before the zoom, to be larger, but I've not been able to figure it out. How to achieve this?
All the code is here.
But really, it's just the following short file and an eu.json file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaa;
}
#states .active {
fill: orange;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
</style>
<body>
<div id="map"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script>
var width = 960;
var height = 500;
var centered;
var projection = d3.geo.mercator();
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var g = svg.append("g");
d3.json("eu.json", function(error, data) {
if (error) throw error;
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(data, data.objects.europe).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(data, data.objects.europe, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
});
function clicked(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;
}
g.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
</script>