<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.ie.css" />
<![endif]-->
<script src="http://cdn.leafletjs.com/leaflet-0.5/leaflet-src.js"></script>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.underscore.min.js"></script>
<script type="text/javascript" src="colorbrewer.js"></script>
<script type="text/javascript" src="leaflet.points-layer.js"></script>
<style type="text/css">
html, body { margin: 0; padding: 0; height: 100%; }
.mapHere { height: 100%; width: 100%; margin:0;padding:0;position:absolute;top:0;left:0; }
.leaflet-popup-content ul { padding-left: 1.5em; }
.circle { visibility: hidden; }
.circle.selected { visibility: visible; }
.quake-timeseries { font: 10px sans-serif; position:absolute;bottom:0;left:0;background-color:rgba(255,255,255,0.8);}
.axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; }
.death{stroke:black;opacity:0.3;fill:red;}
</style>
</head>
<body>
<div class='mapHere' data-source="data-deaths.csv"></div>
<script type="text/javascript">
(function () {
var extent, scale,
classes = 9,
reverse = false;
scheme = colorbrewer['YlGn'][classes],
container = document.querySelector('.mapHere');
map = L.map(container).setView([45, 0], 3);
L.tileLayer(
'http://{s}.tile.osm.org/{z}/{x}/{y}.png'
,{
attribution: '<a href="http://content.stamen.com/dotspotting_toner_cartography_available_for_download">Stamen Toner</a>, <a href="http://www.openstreetmap.org/">OpenStreetMap</a>, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
,maxZoom: 17
}
).addTo(map);
map._initPathRoot();
var svg = d3.select(container.querySelector('svg'))
,markers = svg.append('g')
;
d3.csv(container.dataset.source, function(collection) {
/*
{
"Event_id": "",
"cause_of_death": "asphyxiated",
"CartoDB_Cause_of_death": "drowning or exhaustion related death",
"dataset": "",
"date": "2015-08-27T00:00:00Z",
"quarter": "3Q2015",
"Date-month": "2015 -- 8",
"Year": "2015",
"dead": "70",
"missing": "0",
"dead_and_missing": "70",
"description": "Up to 70 people, presumed to be asylum-seekers, have been found dead in the back of a lorry in Austria.",
"location": "Parndorf",
"latitude": "48",
"longitude": "16.8",
"latlong": "48, 16.8",
"Somme Dedoublement": "",
"name": "",
"route": "Western Balkan route ",
"source": "Austrian Police",
"source_url": "http://www.ft.com/cms/s/0/83323a26-4cc0-11e5-9b5d-89a026fda5c9.html#axzz3k5quE1Mi"
}
*/
collection.forEach(function(d, i, a){
d.LatLng = new L.LatLng( d.latitude || 0, d.longitude || 0);
});
// setup data binding
var markerLayer = markers.selectAll('circle')
.data(collection)
// create, setting up un-changing properties+attributes
markerLayer
.enter()
.append('circle')
.attr('class','death')
;
// update per models (chaning values)
markerLayer
.attr("r", function(d){
return Math.pow((d.dead_and_missing * 1), 0.5);
})
;
// cleanup as they're removed
markerLayer.exit().remove();
map.on("viewreset", update);
update();
function update() {
markerLayer.attr("transform",
function(d) {
return "translate("+
map.latLngToLayerPoint(d.LatLng).x +","+
map.latLngToLayerPoint(d.LatLng).y +")";
}
)
}
;
L.pointsLayer(collection, {
radius: get_radius,
applyStyle: circle_style
}).addTo(map);
var chart = timeseries_chart(scheme)
.x(get_time).xLabel("Earthquake origin time")
.y(get_magnitude).yLabel("Magnitude")
.brushmove(on_brush);
d3.select("body").datum(collection.features).call(chart);
});
function get_time(d) {
return d3.time.format.iso.parse(d.properties.origintime);
}
function get_magnitude(d) {
return +d.properties.magnitude;
}
function on_brush(brush) {
var s = brush.extent();
d3.selectAll(".circle").classed("selected", function (d) {
var time = get_time(d);
return s[0] <= time && time <= s[1];
});
}
function get_radius(d) {
return d.properties.magnitude * d.properties.magnitude;
}
function circle_style(circles) {
if (!(extent && scale)) {
extent = d3.extent(circles.data(), function (d) { return d.properties.depth; });
scale = d3.scale.log()
.domain(reverse ? extent.reverse() : extent)
.range(d3.range(classes));
}
circles.attr('opacity', 0.4)
.attr('stroke', scheme[classes - 1])
.attr('stroke-width', 1)
.attr('fill', function (d) {
return scheme[(scale(d.properties.depth) * 10).toFixed()];
});
circles.on('click', function (d, i) {
L.DomEvent.stopPropagation(d3.event);
var t = '<h3><%- id %></h3>' +
'<ul>' +
'<li>Magnitude: <%- mag %></li>' +
'<li>Depth: <%- depth %>km</li>' +
'</ul>';
var data = {
id: d.id,
mag: d.properties.magnitude,
depth: d.properties.depth
};
L.popup()
.setLatLng([d.geometry.coordinates[1], d.geometry.coordinates[0]])
.setContent(_.template(t, data))
.openOn(map);
});
}
function timeseries_chart(color) {
var margin = { top: 5, right: 5, bottom: 40, left: 45 },
width = 960 - margin.left - margin.right,
height = 80;
var x = d3.time.scale(),
y = d3.scale.linear(),
x_label = "X", y_label = "Y",
brush = d3.svg.brush().x(x).on("brush", _brushmove);
var get_x = no_op,
get_y = no_op;
function timeseries(selection) {
selection.each(function (d) {
x.range([0, width]);
y.range([height, 0]);
var series = d3.select(this).append("svg").attr("class", "quake-timeseries")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g").attr("id", "date-brush")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x_axis = series.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
var y_axis = series.append("g")
.attr("class", "y axis");
x_axis.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", 30)
.style("text-anchor", "end")
.text(x_label);
y_axis.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(y_label);
series.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width - 1)
.attr("height", height - .25)
.attr("transform", "translate(1,0)");
series.append("g")
.attr("class", "brush")
.call(brush)
.selectAll("rect")
.attr("height", height)
.style("stroke-width", 1)
.style("stroke", color[color.length - 1])
.style("fill", color[2])
.attr("opacity", 0.4);
x.domain(d3.extent(d, get_x));
x_axis.call(d3.svg.axis().scale(x).orient("bottom"));
y.domain(d3.extent(d, get_y));
y_axis.call(d3.svg.axis().scale(y).orient("left"));
series.append("g").attr("class", "timeseries")
.attr("clip-path", "url(#clip)")
.selectAll("circle")
.data(d).enter()
.append("circle")
.style("stroke", color[color.length - 2])
.style("stroke-width", .5)
.style("fill", color[color.length - 1])
.attr("opacity", .4)
.attr("r", 2)
.attr("transform", function (d) {
return "translate(" + x(get_x(d)) + "," + y(get_y(d)) + ")";
});
});
}
timeseries.x = function (accessor) {
if (!arguments.length) return get_x;
get_x = accessor;
return timeseries;
};
timeseries.y = function (accessor) {
if (!arguments.length) return get_y;
get_y = accessor;
return timeseries;
};
timeseries.xLabel = function (label) {
if (!arguments.length) return x_label;
x_label = label;
return timeseries;
}
timeseries.yLabel = function (label) {
if (!arguments.length) return y_label;
y_label = label;
return timeseries;
}
timeseries.brushmove = function (cb) {
if (!arguments.length) return brushmove;
brushmove = cb;
return timeseries;
};
function _brushmove() {
brushmove.call(null, brush);
}
function no_op() {}
return timeseries;
}
}());
</script>
</body>
</html>