|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
.land { |
|
fill: #bbb; |
|
} |
|
|
|
.boundary { |
|
fill: none; |
|
stroke: #fff; |
|
stroke-linejoin: round; |
|
stroke-linecap: round; |
|
} |
|
|
|
.handleText, .siteNameText, .titleText, .axis { |
|
opacity: 0.5; |
|
font-size: 10px; |
|
} |
|
|
|
.mapPoint, .linegraph { |
|
stroke: black; |
|
stroke-width: 0.5px; |
|
fill-opacity: 0.8; |
|
} |
|
|
|
.mapPoint { |
|
cursor: pointer; |
|
} |
|
|
|
.linegraph_line { |
|
stroke: #bbb; |
|
fill: none; |
|
stroke-width: 1px; |
|
} |
|
|
|
.startIcon { |
|
opacity: 0.5; |
|
cursor: pointer; |
|
} |
|
|
|
.axis .domain, .slider .handle { |
|
stroke: #555; |
|
stroke-opacity: .5; |
|
stroke-linecap: round; |
|
} |
|
|
|
.axis .domain { |
|
stroke-width: 1.25px; |
|
} |
|
|
|
.slider .handle { |
|
stroke-width: 2.5px; |
|
cursor: move; |
|
} |
|
|
|
.axis { |
|
-webkit-user-select: none; |
|
-moz-user-select: none; |
|
user-select: none; |
|
} |
|
|
|
.axis .domain { |
|
fill: none; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="https://d3js.org/d3.v3.min.js"></script> |
|
<script src="https://d3js.org/topojson.v1.min.js"></script> |
|
<script> |
|
|
|
var width = 960, |
|
height = 500, |
|
mapWidth = 600, |
|
mapHeight = 320; |
|
|
|
var leftMargin = Math.max(0,(width-mapWidth) / 2); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
svg.append("g") |
|
.attr("transform", "translate(" + leftMargin + ",0)") |
|
.append("svg") |
|
.attr("id", "map") |
|
.attr("width", mapWidth) |
|
.attr("height", mapHeight); |
|
|
|
d3.select("#map").append("g") |
|
.attr("id","mapCountries"); |
|
|
|
d3.select("#map").append("g") |
|
.attr("id","mapCities"); |
|
|
|
svg.append("g") |
|
.attr("id","linegraphs"); |
|
|
|
var projection = d3.geo.mercator() |
|
.center([0,-30]) |
|
.scale(90) |
|
.rotate([0,0]) |
|
.translate([280,250]); |
|
|
|
var geo_path = d3.geo.path() |
|
.projection(projection); |
|
|
|
var x = d3.scale.linear() |
|
.domain([1950, 2005]) |
|
.range([leftMargin + 20, leftMargin + 550]) |
|
.clamp(true); |
|
|
|
var y = d3.scale.linear() |
|
.domain([0, 20000]) |
|
.range([475, 380]); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(x) |
|
.orient("top") |
|
.tickFormat(function(d) { if (d == 1950 || d == 2005) {return d}; }) |
|
.tickSize(0) |
|
.tickPadding(12); |
|
|
|
var yAxis = d3.svg.axis() |
|
.scale(y) |
|
.orient("right") |
|
.tickValues([0, 4000, 8000, 12000, 16000, 20000]) |
|
.tickFormat(function(d) { return d / 1000 }) |
|
.tickSize(0) |
|
.tickPadding(12); |
|
|
|
var line = d3.svg.line() |
|
.x(function(d) { return x(d[0]); }) |
|
.y(function(d) { return y(d[1]); }) |
|
.interpolate("linear"); |
|
|
|
var color = d3.scale.linear() |
|
.domain([0,2000,4000,6000,8000,10000,12000,14000,16000,18000,20000]) |
|
.range(["#fff7f3","#fde0dd","#fcc5c0","#fa9fb5","#f768a1","#dd3497","#ae017e","#7a0177","#49006a"]) |
|
.interpolate(d3.interpolateHcl); |
|
|
|
var legendColorList = []; |
|
for (var i = 0; i <= 20000; i+=100) { legendColorList.push(i); } |
|
|
|
var brush = d3.svg.brush() |
|
.x(x) |
|
.extent([0, 0]); |
|
|
|
var startTriangle = "M0,0L0,10L8,5", |
|
stopRectangle = "M0,0L0,10L10,10L10,0"; |
|
|
|
// country boundaries displayed differently in chrome and firefox |
|
var country_boundary_width = "0.5px"; |
|
if (!!window.chrome && !!window.chrome.webstore) { |
|
country_boundary_width = "0.1px"; |
|
} |
|
|
|
|
|
var urbanareas_years = []; |
|
for (var i=1950; i<2010; i=i+5){ urbanareas_years.push(i) } |
|
|
|
var initialSite = "London_United Kingdom", |
|
sliderlocation = 1950.0, |
|
animationOn = false, |
|
animationObj = {}; |
|
|
|
|
|
d3.json("world-50m.json", function(error, topology) { |
|
if (error) throw error; |
|
d3.tsv("urbanareas1_1.tsv", function(error, urbanareas_all) { |
|
if (error) throw error; |
|
|
|
// filter, sort and format urban areas data |
|
|
|
var urbanareas = urbanareas_all.filter(function(d){ return d['pop2005'] > 2500; }) |
|
urbanareas.sort(function(a,b){ return a['pop2005'] - b['pop2005']; }) |
|
urbanareas.forEach(function(d){ |
|
if (d['City_Alternate'] == "") { |
|
d['City_Alternate'] = d['City']; |
|
} |
|
d['ID'] = d['City_Alternate'] + "_" + d['Country']; |
|
d['lat'] = +d['Latitude']; |
|
d['lon'] = +d['Longitude']; |
|
d['history'] = []; |
|
urbanareas_years.forEach(function(yr){ |
|
d['history'].push(+d['pop' + yr]) |
|
}) |
|
}) |
|
|
|
// draw axes, slider handle, and labels |
|
|
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0," + 358 + ")") |
|
.call(xAxis); |
|
|
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.attr("transform", "translate(" + (leftMargin + 575) + ",0)") |
|
.call(yAxis); |
|
|
|
d3.select("#linegraphs").selectAll(".colorLegend") |
|
.data(legendColorList) |
|
.enter().append("circle") |
|
.attr("class", "colorLegend") |
|
.attr("r", 2.5) |
|
.attr("fill", function(d) { return color(d); }) |
|
.attr("cy", function(d){ return y(d); }) |
|
.attr("cx", function(d){ return leftMargin + 575; }); |
|
|
|
svg.append("text") |
|
.attr("class", "siteNameText") |
|
.attr("id", "siteText" ) |
|
.attr("x", leftMargin + 560) |
|
.attr("y", 325) |
|
.attr("text-anchor", "end") |
|
.text(initialSite.replace("_",", ")); |
|
|
|
svg.append("text") |
|
.attr("class", "titleText") |
|
.attr("id", "tempTypeText1" ) |
|
.attr("x", leftMargin + 10) |
|
.attr("y", 325) |
|
.attr("text-anchor", "start") |
|
.text("METRO AREA POPULATION (millions)"); |
|
|
|
svg.append("text") |
|
.attr("class", "siteNameText") |
|
.attr("x", leftMargin + 290) |
|
.attr("y", 346) |
|
.attr("text-anchor", "end") |
|
.text("animate"); |
|
|
|
svg.append("path") |
|
.attr("class", "startIcon") |
|
.attr("d", startTriangle) |
|
.attr("transform", "translate(" + (leftMargin + 300) + "," + 338 + ")"); |
|
|
|
var slider = svg.append("g") |
|
.attr("class", "slider"); |
|
|
|
slider.selectAll(".extent,.resize") |
|
.remove(); |
|
|
|
var handle = slider.append("line") |
|
.attr("class", "handle") |
|
.attr("x1", leftMargin + 200) |
|
.attr("y1", 358) |
|
.attr("x2", leftMargin + 200) |
|
.attr("y2", 495); |
|
|
|
var handleText = slider.append("text") |
|
.attr("class", "handleText") |
|
.attr("x", leftMargin + 210) |
|
.attr("y", 495) |
|
.attr("text-anchor", "start") |
|
.text(""); |
|
|
|
// draw map and cities |
|
|
|
d3.select("#mapCountries").append("path") |
|
.datum(topojson.feature(topology, topology.objects.land)) |
|
.attr("d", geo_path) |
|
.attr("class", "land"); |
|
|
|
svg.select("#mapCountries").append("path") |
|
.datum(topojson.mesh(topology, |
|
topology.objects.countries, |
|
function(a, b) { |
|
return a !== b && (a.id / 1000 | 0) === (b.id / 1000 | 0); |
|
})) |
|
.attr("d", geo_path) |
|
.attr("stroke-width", country_boundary_width) |
|
.attr("class", "boundary"); |
|
|
|
d3.select("#mapCities").selectAll(".mapPoint") |
|
.data(urbanareas) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "mapPoint") |
|
.attr("r", 2.5) |
|
.style("stroke-width", function(d){ if (d.ID == initialSite) { return 1.5 } else { return 0.5 }}) |
|
.attr("city_ID", function(d) {return d.ID;}) |
|
.attr("station_name", function(d) {return d.City_Alternate;}) |
|
.attr("country_name", function(d) {return d.Country;}) |
|
.attr("transform", function(d) {return "translate(" + projection([d.lon,d.lat]) + ")";}); |
|
|
|
// draw timeseries graph |
|
|
|
var linedata = urbanareas.filter(function(k){ return k.ID == initialSite; })[0].history; |
|
|
|
d3.select("#linegraphs").selectAll(".linegraph_line") |
|
.data([d3.zip(urbanareas_years,linedata)]) |
|
.enter().append("path") |
|
.attr("class", "linegraph_line") |
|
.attr("d", function(d){ return line(d);}); |
|
|
|
d3.select("#linegraphs").selectAll(".linegraph") |
|
.data(linedata) |
|
.enter().append("circle") |
|
.attr("class", "linegraph") |
|
.attr("r", 2) |
|
.attr("fill", function(d) { return color(d); }) |
|
.attr("cy", function(d){ return y(d); }) |
|
.attr("cx", function(d,i){ return x(urbanareas_years[i]); }); |
|
|
|
// interaction functions |
|
|
|
function updateMapColors(year) { |
|
d3.select("#mapCities").selectAll(".mapPoint") |
|
.attr("fill", function(d) { |
|
var i = urbanareas_years.indexOf(Math.floor(year / 5) * 5); |
|
var val = d.history[i]; |
|
if (year > urbanareas_years[i]) { |
|
val += (d.history[i+1] - d.history[i]) / (urbanareas_years[i+1] - year); |
|
} |
|
return color(val); |
|
}); |
|
}; |
|
|
|
function updateLinegraph(city_ID) { |
|
|
|
var linedata = urbanareas.filter(function(k){ return k.ID == city_ID; })[0].history; |
|
|
|
d3.select("#linegraphs").selectAll(".linegraph_line") |
|
.data([d3.zip(urbanareas_years,linedata)]) |
|
.transition() |
|
.attr("d", function(d){ return line(d);}); |
|
|
|
d3.select("#linegraphs").selectAll(".linegraph") |
|
.data(linedata) |
|
.transition() |
|
.attr("fill", function(d) { return color(d); }) |
|
.attr("cy", function(d){ return y(d); }); |
|
|
|
} |
|
|
|
function brushed() { |
|
|
|
var value = brush.extent()[0]; |
|
if (d3.event.sourceEvent) { |
|
value = x.invert(d3.mouse(this)[0]); |
|
brush.extent([value, value]); |
|
} |
|
var year = Math.floor(value); |
|
|
|
handle |
|
.attr("x1", x(value)) |
|
.attr("x2", x(value)); |
|
handleText |
|
.attr("x", x(value) + 5) |
|
.text(year); |
|
|
|
sliderlocation = value; |
|
|
|
updateMapColors(year); |
|
|
|
}; |
|
|
|
function animator() { |
|
slider |
|
.call(brush.extent([sliderlocation, sliderlocation])) |
|
.call(brush.event); |
|
if (sliderlocation >= 2005.0) { |
|
animationOn = false; |
|
clearInterval(animationObj); |
|
d3.select(".startIcon") |
|
.attr("d", startTriangle); |
|
} else { |
|
sliderlocation += 1.0/4.0; |
|
} |
|
}; |
|
|
|
// apply interaction functions |
|
|
|
brush.on("brush", brushed); |
|
|
|
slider |
|
.call(brush) |
|
.call(brush.extent([sliderlocation, sliderlocation])) |
|
.call(brush.event); |
|
|
|
d3.select(".startIcon") |
|
.on("click", function(e) { |
|
if (animationOn) { |
|
animationOn = false; |
|
clearInterval(animationObj); |
|
d3.select(this).attr("d", startTriangle); |
|
} else { |
|
animationOn = true; |
|
d3.select(this).attr("d", stopRectangle); |
|
animationObj = setInterval( animator, 1 ); |
|
} |
|
}); |
|
|
|
d3.select("#mapCities").selectAll(".mapPoint") |
|
.on("click", function(e){ |
|
pt = d3.select(this)[0][0].attributes; |
|
d3.select("#siteText").text(pt.station_name.value + ", " + pt.country_name.value); |
|
d3.selectAll(".mapPoint").style("stroke-width", 0.5); |
|
d3.select(this).style("stroke-width", 1.5); |
|
updateLinegraph(pt.city_ID.value); |
|
}); |
|
|
|
}); |
|
}); |
|
|
|
</script> |