// browser check for zoom
var isOpera = !!(window.opera && window.opera.version); // Opera 8.0+
var isFirefox = testCSS('MozBoxSizing'); // FF 0.8+
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
// At least Safari 3+: "[object HTMLElementConstructor]"
var isChrome = !isSafari && testCSS('WebkitTransform'); // Chrome 1+
var isIE = /*@cc_on!@*/false || testCSS('msTransform'); // At least IE6
//console.log("isChrome= ",isChrome,", isFirefox= ",isFirefox, ", isSafari= ",isSafari, ", isIE= ", isIE,", isOpera= ", isOpera);
var selectedData=[];
var circles;
var margin = {top: 5, right: 20, bottom: 20, left: 20},
width = 1280 - margin.left - margin.right,
height = 517 - margin.top - margin.bottom;
var projection = d3.geo.equirectangular()
.scale(170)
.translate([width / 2, height / 2])
.precision(.1);
/*var projection = d3.geo.azimuthalEquidistant()
.scale(150)
.translate([width / 2, height / 2])
.clipAngle(180 - 1e-3)
.precision(.1);*/
var path = d3.geo.path()
.projection(projection);
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scale(projection.scale())
//.scaleExtent([height, 8 * height])
.on("zoom", move);
var color = d3.scale.ordinal()
// .range(["#999353","#17AACC"]);
.range(["#E88D0C","#FFEC09"]);
var tooltipdiv = d3.select("body")
.append("div")
.attr("class", "tooltip");
var svg = d3.select("#map_background").append("svg")
.attr("width", width+ margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("display","block")
.style("margin","auto")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var mapSvg = svg.append("g")
.attr("id", "map");
mapSvg.append("rect")
.attr("class", "background")
//.attr("width", width + margin.left + margin.right)
//.attr("height", height + margin.top + margin.bottom);
var circlesSvg = svg.append("g")
.attr("id","circles");
var margin1 = {top: 1, right: 30, bottom: 20, left: 30},
width1 = 1260 - margin1.left - margin1.right,
height1 = 150 - margin1.top - margin1.bottom;
var charts = d3.select("#charts").append("svg")
.attr("width", width1 + margin1.left + margin1.right)
.attr("height", height1 + margin1.top + margin1.bottom)
.append("g")
.attr("transform", "translate(" + margin1.left + "," + margin1.top + ")");
d3.json("world-countries.json", function(json) {
mapSvg.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path)
d3.csv("meteorites_new1.csv", function(error, data) {
var data1400=[];
console.log("--data.length=",data.length);
data.forEach(function(d){ if (+d.year >= 1400) data1400.push(d); });
visualize(data1400);
});
});
//zoom in buttons
var zoomin = d3.select("body")
.append("div")
.attr("class","zoom")
.style("top", "300px")
.style("left","30px")
.style("padding-top","2px")
.text("+")
.on("click", function(){
d3.select("#map_background svg").attr("width","100%");
fireZoomEvent( -0.2);
});
var zoomout = d3.select("body")
.append("div")
.attr("class","zoom")
.style("top", "335px")
.style("left","30px")
.style("padding-top","1px")
.text("-")
.on("click", function(){
fireZoomEvent(0.2);
});
//help text
/*d3.select("#charts").append("div")
.attr("class","help")
.text('Click and drag to select a period, click to deselect')
.style("top","10px")
.style("right","10px")
.style("color","#FFF")
.style("position","relative")*/
function visualize(data){
function rScale(value){
if (value < 100) return 3;
else if (value < 500) return 5;
else if (value < 1000) return 8;
else if (value < 5000) return 12;
else if (value < 10000) return 16;
else return 20;
}
drawAllCircles(data);
svg.append("text")
.style("fill","white")
.text("start")
.attr("x", "20px")
.attr("y", "20px")
.attr("id","button")
.on("click", reset);
svg.append("text")
.style("fill","white")
.text("stop")
.attr("x", "70px")
.attr("y", "20px")
.attr("id","button")
.on("click", stop);
var meteorites = crossfilter(data);
meteorites.fell = meteorites.dimension(function(d){return d.fell});
meteorites.year = meteorites.dimension(function(d){return +d.year;});
var yearCount = meteorites.year.group().top(Infinity);
meteorites.type = meteorites.dimension(function(d){return d.recclass;});
var typeCount = meteorites.type.group().top(10);
typeCount.sort(function(a, b){
return a.key-b.key
})
// menu area ------
d3.select("#menu").append('div')
.attr("class","help")
.text("Move your mouse over the circles for more information, click on them to go to the database record (external page)");
d3.select("#menu").append("h4")
.text("Select")
var found_fellMenu = d3.select("#menu").append('div')
.attr("class","found_fellMenu");
found_fellMenu.append("div") //menu
.attr("class","help")
.text("Finds and Falls:");
var found_fellList = [{name:'Finds',id:'found'},{name:'Falls',id:'fell'},{name:'All',id:'all'}];
found_fellMenu.selectAll('#menuItem')
.data(found_fellList)
.enter()
.append("div")
.attr("id","menuItem")
.attr("class",function(d){ if (d.id ==='all')return 'active last'; })
.html(function(d){return d.name;})
.on('click', function(d){
d3.selectAll('.found_fellMenu #menuItem').classed('active',false);
switch (d.id){
case 'found':
d3.select(this).classed('active',true);
selectedData = meteorites.fell.filter('Found').top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'fell':
d3.select(this).classed('active',true);
selectedData = meteorites.fell.filter('Fell').top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'all':
d3.select(this).classed('active',true);
selectedData = meteorites.fell.filterAll().top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
}
});
var typeMenu = d3.select("#menu").append('div')
.attr("class","typeMenu");
typeMenu.append("div") //menu
.attr("class","help")
.text("According to type:");
var type_List = [{name:'Stony',id:'stony'},{name:'Iron',id:'iron'},{name:'Stony-iron',id:'stony-iron'},{name:'All',id:'all'}];
typeMenu.selectAll('#menuItem')
.data(type_List)
.enter()
.append("div")
.attr("id","menuItem")
.attr("class",function(d){ if (d.id ==='all')return 'active last'; })
.html(function(d){return d.name;})
.on('click', function(d){
d3.selectAll('.typeMenu #menuItem').classed('active',false);
switch (d.id){
case 'stony':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filter(function(d1){return d1.indexOf("Mesosiderite") < 0 && d1.indexOf("Pallasite") < 0 && d1.charAt(0) !="I";}).top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'iron':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filter(function(d1){return d1.charAt(0) =="I";}).top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'stony-iron':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filter(function(d1){return d1.indexOf("Mesosiderite") >=0 || d1.indexOf("Pallasite") >=0;}).top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'all':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filterAll().top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
}
});
// found - fell
var lunarMenu = d3.select("#menu").append('div')
.attr("class","lunarMenu");
lunarMenu.append("div") //menu
.attr("class","help")
.text("Meteorites coming from the Moon and Mars:")
var lunarList = [{name:'Lunar',id:'lunar'},{name:'Martian',id:'martian'},{name:'All',id:'all'}];
lunarMenu.selectAll('#menuItem')
.data(lunarList)
.enter()
.append("div")
.attr("id","menuItem")
.attr("class",function(d){ if (d.id ==='all')return 'active last'; })
.html(function(d){return d.name;})
.on('click', function(d){
d3.selectAll('.lunarMenu #menuItem').classed('active',false);
switch (d.id){
case 'lunar':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filter(function(d1){return d1.indexOf("Lunar") >= 0;}).top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'martian':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filter(function(d1){return d1.indexOf("Martian") >= 0;}).top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
case 'all':
d3.select(this).classed('active',true);
selectedData = meteorites.type.filterAll().top(Infinity);
circles.remove();
drawAllCircles(selectedData);
break;
}
});
// end menu area ------
//console.log("data.length=",data.length);
var xMin = d3.min(yearCount, function(d){return d.key;});
var xMax = d3.max(yearCount, function(d){return d.key;});
var yMax = d3.max(yearCount, function(d){return d.value;});
//var x = d3.scale.linear()
var x= d3.scale.pow()
.exponent(6)
.domain([xMin,xMax])
.range([0, width1]);
var y = d3.scale.linear()
.domain([0, yMax])
.range([height1, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(3)
.tickFormat(d3.format(""))
.tickValues([1400,1500,1600,1700,1800,1850,1900,1950,2000,2013])
.orient("bottom");
var chartBrush = d3.svg.brush()
.x(x)
//.on("brushstart", brushstart)
.on("brush", brushmove)
.on("brushend", brushend);
charts.append("g")
.attr("class", "brush")
.call(chartBrush)
.selectAll("rect")
.attr("height", height1+2)
.attr("transform", "translate( 0,-1)");
var bar = charts.selectAll("#bar")
.data(yearCount)
.enter().append("g")
.each(function(d){
d._fellCount = $.grep(data, function(e){ return e.fell == "Fell" && e.year == d.key; }).length;
d._foundCount = d.value - d._fellCount;
})
.attr("id", "bar")
.attr("transform", function(d) { return "translate(" + x(d.key) + ",0)"; })
.attr("class","year")
.on("mouseover", function(d){
var textTooltip = ""+d.key+'
Falls: '+d._fellCount+'
Finds: ' +d._foundCount;
tooltipdiv.html(textTooltip)
.style("top", d3.event.pageY - 20 + "px")
.style("left", d3.event.pageX + 20 + "px")
.style("visibility", "visible");
})
.on("mouseout", function(){tooltipdiv.style("visibility", "hidden"); });
bar.append("rect") //found
.attr("width", 3)
.attr("y", function(d) { return y(d._foundCount); })
.attr("height", function(d) { return y(0) - y(d._foundCount); })
.style("fill", "#E88D0C")
bar.append("rect") //fell
.attr("width", 3)
.attr("y", function(d) {return y(d.fell); })
.attr("height", function(d) { return y(0)-y(d._fellCount); })
.style("fill", "#FFEC09")
charts.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height1 + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle")
charts.append("line")
.attr("x1", 0)
.attr("y1", -0.5)
.attr("x2", width1)
.attr("y2", -0.5)
.attr("id","chartAxis")
charts.append("line")
.attr("x1", 0)
.attr("y1", height1+1)
.attr("x2", width1)
.attr("y2", height1+1)
.attr("id","chartAxis")
//charts.call(chartBrush);
charts.append("line")
.attr("x1", -2)
.attr("y1", -2)
.attr("x2", -2)
.attr("y2", 132)
.attr("id","movingLine")
.style("stroke", "#FFF")
.style("stroke-width","2px")
.style("position","absolute");
charts.append("text")
.text("Number of Falls")
.attr("x", "0px")
.attr("y", "20px")
.attr("class","label")
.style("fill","#FFF")
charts.append("text")
.text("Number of Finds")
.attr("x", "0px")
.attr("y", "120px")
.attr("class","label")
.style("fill","#FFF")
var yearPanel = svg.append("text")
.attr("class","yearText")
.text('2013')
.attr('y',50)
.attr('x',500);
var year = 1400,
yearEnd = 2013,
animate;
start();
function start() {
if (year === 1400) circles.style("visibility","hidden");
var xValue = x(year)
yearPanel.text(year);
charts.select("#movingLine")
.attr("x1", xValue)
.attr("x2", xValue);
//console.log(year);
if (year < yearEnd) animate = setTimeout(start, 100);
else {
d3.select(".background").style("opacity",1);
d3.select("#map")
.transition()
.duration(2000)
.style("opacity",1);
//circles.style("stroke-width","0.5").style("stroke","#FFFFFF");
d3.select('#menu').transition().duration(2000).style("opacity",0.9);
}
var yearCircles = circles.filter(function(d){ if (year < 1800) return +d.year > year-5 && +d.year <= year ;
else return +d.year === year });
yearCircles.filter(function(d){ return d.fell === "Fell" })
.attr("cx",function(d) { return projection([d.longitude,d.latitude])[0] - (Math.floor(Math.random() * 801) - 400);})
.attr("cy",function(d) { return projection([d.longitude,d.latitude])[1] - (Math.floor(Math.random() * 801) - 400);})
.attr("r", 1)
.style("opacity",0)
.style("visibility","visible")
.transition()
.duration(1000)
.attr("cx",function(d) { return projection([d.longitude,d.latitude])[0];})
.attr("cy",function(d) { return projection([d.longitude,d.latitude])[1];})
.attr("r", function(d) { return rScale(d.mass);})
.style("opacity",0.8)
yearCircles.filter(function(d){ return d.fell === "Found" })
.attr("r", 1)
.style("visibility","visible")
.transition()
.duration(2000)
.attr("r", function(d) { return rScale(d.mass);})
if (year < 1800) year += 5;
else year += 1;
}
function stop(){
clearTimeout(animate)
circles.style("visibility","visible");
year = yearEnd;
var xValue = x(year)
charts.select("#movingLine")
.attr("x1", xValue)
.attr("x2", xValue);
yearPanel.text('2013');
d3.select(".background").style("opacity",1);
d3.select("#map")
.transition()
.duration(2000)
.style("opacity",1);
d3.select('#menu').transition().duration(2000).style("opacity",0.9);
}
function reset(){
year = 1400;
start();
}
function brushstart() {
//charts.classed("selecting", true);
}
function brushmove() {
}
function brushend() {
if (chartBrush.empty()){
selectedData = meteorites.year.filterAll().top(Infinity);
yearPanel.text('2013');
}
else {
var extent = d3.event.target.extent();
//console.log("in brushend = ", extent);
bar.classed("selected", function(d) {return extent[0] <= +d.key && +d.key <= extent[1]; });
//update crossfilter
selectedData = meteorites.year.filterRange([extent[0],extent[1]]).top(Infinity);
yearPanel.text(Math.floor(extent[0]) +' - '+ Math.floor(extent[1]));
}
circles.remove();
drawAllCircles(selectedData);
}
function drawAllCircles(data){
circles = circlesSvg.selectAll("circle")
.data(data)
.enter().append("svg:a")
.attr("xlink:href", function(d){return "http://www.lpi.usra.edu/meteor/metbull.php?code="+d.id;})
.attr("target","_blank")
.append("svg:circle")
.attr("cx",function(d) { return projection([d.longitude,d.latitude])[0];})
.attr("cy",function(d) { return projection([d.longitude,d.latitude])[1];})
.attr("opacity",0.8)
.attr("r", function(d) { return rScale(d.mass);})
.style("fill", function(d) { return color(d.fell); })
.style("stroke-width",function(d){if (d.history) return 1;})
.on("mouseover", function(d){
var textTooltip = ''+d.name+'
type: '+d.recclass+'
mass: '+(d.mass)+' kg
'+d.fell+'
year: '+d.year;
if (d.history) textTooltip = textTooltip + '