Skip to content

Instantly share code, notes, and snippets.

@milkbread
Last active December 18, 2015 03:39
Show Gist options
  • Save milkbread/5720205 to your computer and use it in GitHub Desktop.
Save milkbread/5720205 to your computer and use it in GitHub Desktop.
HTML: Earthquakes with interactive filtering (crossfilter.js) -- PREV: earthquakes_1.0.html

D3 + Crossfilter

this is a basic example on the usage of Crossfilter in combination with D3.
This exemplary map is a visualistation of the world wide earthquakes between 01.05.2012 & 31.05.2012

<h3>D3 + Crossfilter</h3>
<h3 class=sub>this is a basic example on the usage of Crossfilter in combination with D3.<br>This exemplary map is a visualistation of the world wide earthquakes between 01.01.2012 & 31.12.2012</h3>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://bl.ocks.org/milkbread/raw/5779939/RKMapping_0.4.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.6.1/leaflet.js"></script>
<script src="http://bl.ocks.org/milkbread/raw/5947143/crossfilter.min.js"></script>
<script src="RKToolbox.js"></script>
<style>
@import url(http://cdn.leafletjs.com/leaflet-0.6.1/leaflet.css);
@import url(http://bl.ocks.org/milkbread/raw/5957651/simplyArcDemoStyles.css);
@import url(RKToolbox_0.1.css);
#map {
width: 960px;
height: 380px;
}
input{
width: 40px;
}
#polys:hover{
fill:rgba(200,0,0,0.8);
}
#polys{
stroke-width:1px;
stroke:rgba(255,255,255,1);
}
.overlay_poly{
fill:rgba(100,100,100,0.3);
}
.overlay_multipoly{
fill:rgba(0,0,0,0.7);
}
#delaunay{
fill:none;
stroke:rgba(0,0,255,1);
stroke-width:1px;
}
circle{
}
</style>
</head>
<body>
<h1>World Wide Earthquakes in 2009</h1>
<div id=map></div>
<div id=timeline></div>
<div id=heading></div>
<div id=info></div>
<div>
</div>
<script>
var map = L.map('map').setView([40, 11], 2);//.setView([49.5537, 7.7783], 8);
var stamen = L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png', {attribution: 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 <a href="http://maps.stamen.com/#toner/8/51.072/14.584" rel="author" target="_blank"> Stamen</a>, Data-overlay: <a href="http://earthquake.usgs.gov/earthquakes/" rel="author" target="_blank">&copy; USGS</a>'}).addTo(map);
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {attribution: 'Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade, Data-overlay: <a href="http://earthquake.usgs.gov/earthquakes/" rel="author" target="_blank">&copy; USGS</a>',key: 'BC9A493B41014CAABB98F0471D759707',styleId: 22677 });
var baseLayers = {"stamen": stamen, "cloudmade": cloudmade};
L.control.layers(baseLayers).addTo(map);
var overLayer = new mapOverlay(map);
var infoContainer = d3.select("#info").append("info").text("This will show you some information!")
var maximum = 0;
var colors = {fill:["#00f", "#f00"], opacity:0.25, stroke:["#f00","#0ff"]}
var magniExt = [2,4], step_width = 0.1;
var basicInfos = [], maxim, minim;
addInteractions();
var indicatorPositions = [new Date(2100, 0, 1),new Date(1800, 0, 1)],
timeline = new timeLine(d3.select("#timeline"),960,60);
timeline.addClickListener(clickAction);
function addInteractions(){
//***Basics
basicInfos[0] = d3.select("#heading").append("text").text("Some basic information");
basicInfos[1] = d3.select("#heading").append("text").text("")
basicInfos[2] = d3.select("#heading").append("text");
basicInfos[3] = d3.select("#heading").append("text");
}
var points, size, color, overlay, earthquakes, minMax, count, magniDimension, timeDimension;
d3.json("earthquakes052012.json", function(collection) {
var quakes = collection.features
/* console.log(Date.now())
var date = new Date(1335745236510);
console.log(date.toString().split(" "), date.getDate(), date.getMonth()+1, date.getFullYear())
var x=new Date(2012, 0, 1);
var y=new Date(2013, 0, 1);
console.log(x<y)
*/
earthquakes = crossfilter(quakes);
var n = earthquakes.groupAll().reduceCount().value();
basicInfos[0].text("There are "+ n +" eathquakes within that file!")
//magniDimension = earthquakes.dimension(function(d){return d.properties.mag;})
//resetMagniFilter();
console.log(earthquakes.groupAll().reduceCount().value())
count = earthquakes.groupAll().reduceCount().value();
//get the time dimension and the min-&max values of them...needed for the timeline
timeDimension = earthquakes.dimension(function(d){return d.properties.time;})
timeDimension.top(count).forEach(function(d){
var date = new Date(parseInt(d.properties.time))
//console.log(date)
if(date>indicatorPositions[1])indicatorPositions[1]=date;
if(date<indicatorPositions[0])indicatorPositions[0]=date;
})
timeline.addScalebar(indicatorPositions);
//apply an initial filter...as the dataset is very huge
//indicatorPositions = [new Date(2012, 0, 1),new Date(2012, 0, 31)]
//timeline.replaceIndicators([undefined,indicatorPositions[1]]);
//resetTimeFilter(indicatorPositions);
// console.log(indicatorPositions)
// var mags = earthquakes.groupAll().reduceSum(function(fact) { return fact.properties.mag; }).value()
// console.log("The total of all magnitudes is: " + mags)
overLayer.addGeometries([],"path");
overlay = overLayer.overlay;
reset();
map.on("viewreset", reset);//.on("move",function(){console.log(map.getCenter(),map.getZoom())})
overLayer.features.attr("class",function(d){if(d.type==="Polygon")return "overlay_poly"; else if(d.type==="MultiPolygon") return "overlay_multipoly"})
.attr("id","polys")
.on("mouseover",showInfo)
.on("mouseup",zoomToObject);
function zoomToObject(d){
var local_bounds = d3.geo.bounds(d),
center = new L.LatLng(local_bounds[0][1]+((local_bounds[1][1]-local_bounds[0][1])/2),local_bounds[0][0]+((local_bounds[1][0]-local_bounds[0][0])/2)),
southWest = new L.LatLng(local_bounds[0][1], local_bounds[0][0]),
northEast = new L.LatLng(local_bounds[1][1], local_bounds[1][0]);
var local_bounds = new L.LatLngBounds(southWest, northEast);
map.setView( center, map.getBoundsZoom( local_bounds));
}
})
function clickAction(){
indicatorPositions=timeline.getIndicPosition()
//console.log(indicatorPositions)
resetTimeFilter(indicatorPositions)
reset();
}
function resetTimeFilter(data){
timeDimension.filterAll();
timeDimension.filter(function(d){return (d >= data[0] && d <= data[1])})
count = earthquakes.groupAll().reduceCount().value();
basicInfos[1].text("..."+ count +" eathquakes after filtering!")
basicInfos[3].text("Current time interval: ["+data[0].getDate()+". "+ (data[0].getMonth()+1) +". "+ data[0].getFullYear() +", "+data[1].getDate() +". "+ (data[1].getMonth()+1) +". "+ data[1].getFullYear() +"]")
}
function reset(){
var start_time = Date.now();
var geoDimension = earthquakes.dimension(function(d) { return d.geometry.coordinates; }),
geoms = geoDimension.top(count);
minMax = [100000,0];
geoms.forEach(function(d){
if(d.properties.mag>minMax[1])minMax[1]=d.properties.mag;
if(d.properties.mag<minMax[0])minMax[0]=d.properties.mag;
//if(d.magnitude<[5])console.log(d);
})
size = d3.scale.linear()
.domain(minMax)
.range([1,6]);
color = d3.scale.linear()
.domain(minMax)
.range(colors.fill);
overlay.selectAll("circle").remove();
points = overlay.selectAll("circle").data(geoms).enter().append("circle").attr("id","circles").on("mouseover",transPoint);
points.attr("r",function(d){return size(d.properties.mag)})
.attr("cx",function(d) {var data = overLayer.project(d.geometry.coordinates); return data[0]})
.attr("cy",function(d) {var data = overLayer.project(d.geometry.coordinates); return data[1]})
.attr("fill",function(d){return color(d.properties.mag)}).attr("opacity",colors.opacity).attr("stroke",colors.stroke[0])
//console.log(d3.geo.bounds({type:"MultiPoint", coordinates:centroids}))
overLayer.resetView([[-180-10, -89], [180+10, 89]]);
overLayer.showAll();
basicInfos[2].text("...visualising them took: "+((Date.now() - start_time)/1000).toFixed(4)+"s").append("br")
}
function transPoint(d){
points.transition().duration(500).ease("linear")
.attr("r",function(e){if(e.id==d.id)return size(e.properties.mag)*5; else return size(e.properties.mag)})
.attr("fill",function(e){if(e.id==d.id)return "#fff"; else return color(e.properties.mag)})
.attr("opacity",function(e){if(e.id==d.id)return 0.8; else return colors.opacity})
.attr("stroke",function(e){if(e.id==d.id)return colors.stroke[1]; else return colors.stroke[0]})
showInfo([d.properties.mag, d.geometry.coordinates[2], d.properties.place]);
}
function showInfo(value){
infoContainer.text("This earthquake had a magnitude of: "+value[0] + " - a depth of: "+ value[1] + " and was located at: "+ value[2])
}
function reShow(maxVal){
maximum = maxVal;
overLayer.showAllFiltered(maxVal);
}
</script>
</body>
</html>
//should be able to define: indicWidth, indicSideWidth
function timeLine(container,width,height){
//initialize some variables
var indicWidth = 8, indicSideWidth = 3, indicatorGroup, indicatorLeftpart, indicatorRightpart, indicatorLeft, indicatorRight;
var axisScale = d3.time.scale();
var timelineSVG = container.append("svg")
.attr("width", width)
.attr("height", height);
var timelineGroup = timelineSVG.append("g");
//this is the background of the timeline
timelineGroup.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height)
.attr("id","_background")
var xAxisGroup = timelineGroup.append("g");
//this is the interactive area of the timeline
var timeline = timelineGroup.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height)
.attr("id","timelineSVG")
.on("mousemove",overAction);
defineIndicators();
//add the scalebar...has to be done from 'outside'...returns the 'xAxisGroup'
this.addScalebar=addScalebar;
function addScalebar(domain_){
//add the text and something like a 'scalebar'
axisScale.domain(domain_).range([0,width]); //tutorial for the time scale: http://bl.ocks.org/mbostock/4149176
var xAxis = d3.svg.axis()
.scale(axisScale);
xAxisGroup.call(xAxis).attr("class", "axis");
xAxisGroup.selectAll("text").attr("transform","rotate(-90, 0, 0)");
xAxisGroup.attr("transform","translate(0,"+(height/2)+")");
return xAxisGroup;
}
//variables of the functions
var indicBools = [false,false],
indicPos = [0,width];
//identic for left and right...just call the 'downAction'
function leftDownAction(){
var value = (d3.mouse(this)[0]);
downAction(value, 'left')
}
function rightDownAction(){
var value = (d3.mouse(this)[0]);
downAction(value, 'right')
}
//check which side called me and check if indicator is currently static or dynamic
function downAction(value, side){
if(side=='left'){
if(indicBools[0]==false)indicBools[0]=true;
else {indicPos[0]=value;indicBools[0]=false;indicatorLeftpart.transition().duration(1000).attr("x",value+indicWidth);indicatorLeft.transition().attr("x",value)}
}
else if(side=='right'){
if(indicBools[1]==false)indicBools[1]=true;
else {indicPos[1]=value;indicBools[1]=false;indicatorRightpart.transition().duration(1000).attr("x",value-indicSideWidth);indicatorRight.transition().attr("x",value)}
}
//execute the user defined function for a click event...when there is one
if(clickListenerTest==true && ((indicBools[1]==false && side=='right')|| (indicBools[0]==false && side=='left')))clickListener();
}
//check if and which side was clicked and transition its position
function overAction(){
var dur = 100;
if(indicBools[0]==true){
var value = (d3.mouse(this)[0]);
if(indicPos[1]>value){
indicatorLeft.attr("x",value)
indicatorLeftpart.attr("x",value-indicSideWidth)
}
}
else if(indicBools[1]==true){
var value = (d3.mouse(this)[0]);
if(indicPos[0]<value){
indicatorRight.attr("x",value)
indicatorRightpart.attr("x",value+indicWidth)
}
}
}
this.getIndicPosition=getIndicPosition;
function getIndicPosition(){
return [axisScale.invert(indicPos[0]),axisScale.invert(indicPos[1])];
}
var clickListenerTest = false;
this.addClickListener=addClickListener;
function addClickListener(listener_){
clickListener = listener_;
clickListenerTest = true;
}
this.getClickListener=getClickListener;
function getClickListener(){
return clickListener();
}
this.replaceIndicators=replaceIndicators;
function replaceIndicators(timeArray){
if(timeArray[0]!=undefined){
indicatorLeftpart.attr("x",axisScale(timeArray[0])+indicWidth);
indicatorLeft.attr("x",axisScale(timeArray[0]));}
if(timeArray[1]!=undefined){
indicatorRightpart.attr("x",axisScale(timeArray[1])-indicSideWidth);
indicatorRight.attr("x",axisScale(timeArray[1]));}
}
this.defineIndicators=defineIndicators;
function defineIndicators(){
//add some indicators
//short side of the main left indicator
indicatorGroup = timelineGroup.append("g")
indicatorLeftpart = indicatorGroup.append("rect").attr("x", indicWidth).attr("y", 0).attr("width", indicSideWidth).attr("height", height)
.attr("id","indiSide");
//short side of the main right indicator
indicatorRightpart = indicatorGroup.append("rect").attr("x", width-indicSideWidth-indicWidth).attr("y", 0).attr("width", indicSideWidth).attr("height", height).attr("id","indiSide");
//left main indicator
indicatorLeft = indicatorGroup.append("rect").attr("x", 0).attr("y", 0) .attr("width", indicWidth).attr("height", height)
.attr("id","indicatorLeft")
.on("mousedown",leftDownAction);
//right main indicator
indicatorRight = indicatorGroup.append("rect").attr("x", width-indicWidth).attr("y", 0).attr("width", indicWidth).attr("height", height)
.attr("id","indicatorLeft")
.on("mousedown",rightDownAction);
}
this.reDefineIndicators=reDefineIndicators;
function reDefineIndicators(width, side){
if(side!=undefined)indicSideWidth = side;
indicWidth = width;
indicatorGroup.selectAll("rect").remove()
defineIndicators();
}
}
function dummyFunction(this_){
console.log("This is the 'mousedown'...add a function to the '.downAction()' for action!")
var value = (d3.mouse(this_)[0]);
console.log("Currently, I can offer you the mouseposition: ",value)
}
#timeline{
fill:rgba(0,0,0,0.4);
}
#indicatorLeft{
fill:rgba(255,255,255,0.4);
}
#indiSide{
fill:rgba(255,0,0,0.3);
}
#_background{
fill:rgba(100,100,100,1);
}
.axis path,
.axis line{
fill: none;
stroke: white;
stroke-width:4;
shape-rendering: crispEdges;
opacity:0.2;
}
.axis text {
font-family: sans-serif;
font-size: 10px;
text-anchor:left;
fill:#0f0;
opacity:0.8;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment