Created
April 29, 2014 02:31
-
-
Save serdaradali/11389411 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
#map{ | |
background-image:url("img/parchment2.jpg"); | |
} | |
</style> | |
<body> | |
<link type="text/css" rel="stylesheet" href="worldMap.css"/> | |
<script src="http://d3js.org/queue.v1.min.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/topojson.v1.min.js"></script> | |
<script src = "zoomableWorldMap.js"></script> | |
<div id="map" style="width: 1296px;"></div> | |
<script> | |
var sample,sample2,cMap,s_m; | |
function randomData() | |
{ | |
var data = []; | |
for(var i=0; i<41;i++) | |
{ | |
data[i] = Math.floor((Math.random())*100); | |
} | |
return data; | |
} | |
var dataArr = randomData(); | |
var q = queue(); | |
q.defer(d3.json, "data/worldTopo.json"); | |
q.defer(d3.tsv,"data/boats.tsv"); | |
q.await(readMapData); | |
function readMapData(error,countries,boats) { | |
cMap = WorldMap() | |
.mapJSON(countries); | |
// create array of arrays for boats | |
//var totalBoats = boats[boats.length-1].id-boats[0].id+1; | |
var pathCount = [205,166,206,231,455,443,209,229,118,177,73,275,205,176,245,312,214,335,444,64,397,216,262,393,293,257,123,400,217,184,180,165,245,403,51,181,322,271,411,353,208,331,271,374,230,327,459,82,325,152,361,352,83,366,266,203,133,378,93,120,203,62,184,191,374,225,287,231,171,149,274,167,343,500,367,94,148,166,147,365,246,133,300]; | |
d3.select("#map") | |
.datum([boats,pathCount]) | |
.call(cMap); | |
//map = new choroplethMap(collection,sample); | |
//map2 = new choroplethMap(collection,sample); | |
}; | |
</script> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
#globe{ | |
background: #fcfcfa; | |
position: absolute; | |
top:70px; | |
width: 900px; | |
height: 500px; | |
margin-left: 350px; | |
} | |
#animDate{ | |
background: #fcfcfa; | |
width: 700px; | |
height: 50px; | |
margin-left: 575px; | |
} | |
#about{ | |
position: absolute; | |
top:20px; | |
left:10px; | |
width: 200px; | |
height: 200px; | |
font: 300 46px "Helvetica Neue"; | |
} | |
.stroke { | |
fill: none; | |
stroke: #000; | |
stroke-width: 3px; | |
} | |
.fill { | |
fill: #fff; | |
} | |
.graticule { | |
fill: none; | |
stroke: #777; | |
stroke-width: .5px; | |
stroke-opacity: .5; | |
} | |
.land { | |
fill: #222; | |
} | |
.boundary { | |
fill: none; | |
stroke: #fff; | |
stroke-width: .5px; | |
} | |
.overlay { | |
fill: none; | |
pointer-events: all; | |
} | |
.year.label { | |
font: 300 46px "Helvetica Neue"; | |
fill: #ddd; | |
} | |
</style> | |
<body> | |
<link type="text/css" rel="stylesheet" href="worldMap.css"/> | |
<script src="http://d3js.org/queue.v1.min.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/topojson.v1.min.js"></script> | |
<div id="about">BARCELONA WORLD RACE 2010</div> | |
<div id="animDate"></div> | |
<div id="globe"></div> | |
<script> | |
var width = 680, | |
height = 680; | |
var boatPath = [],circles=[]; | |
var pathCount = [205,166,206,231,455,443,209,229,118,177,73,275,205,176,245,312,214,335,444,64,397,216,262,393,293,257,123,400,217,184,180,165,245,403,51,181,322,271,411,353,208,331,271,374,230,327,459,82,325,152,361,352,83,366,266,203,133,378,93,120,203,62,184,191,374,225,287,231,171,149,274,167,343,500,367,94,148,166,147,365,246,133,300]; | |
var projection = d3.geo.orthographic() | |
.scale(270) | |
.translate([width / 2, height / 2]) | |
.clipAngle(90) | |
.precision(.1); | |
var zoom = d3.behavior.zoom() | |
.scaleExtent([1,6]) | |
.on("zoom",zoomed); | |
var drag = d3.behavior.drag() | |
.origin(function() { var r = projection.rotate(); return {x: r[0], y: -r[1]}; }) | |
.on("drag", dragged) | |
.on("dragstart", dragstarted) | |
.on("dragend", dragended); | |
var path = d3.geo.path() | |
.projection(projection); | |
var pathLine = d3.svg.line() | |
.interpolate("cardinal") | |
.x(function(d) { | |
return projection([d.Lon, d.Lat])[0]; }) | |
.y(function(d) { | |
return projection([d.Lon, d.Lat])[1]; }); | |
var colors = d3.scale.category20(); | |
var graticule = d3.geo.graticule(); | |
var svg = d3.select("#globe").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var pathG = svg.append("g"); | |
var format = d3.time.format("%Y-%m-%d"); | |
var label = d3.select("#animDate").append("text") | |
.attr("class", "year label") | |
.attr("text-anchor", "end") | |
.attr("y", 10) | |
.attr("x", 10) | |
.text(format(new Date(1293801017000))); | |
svg.append("rect") | |
.attr("class", "overlay") | |
.attr("width", width) | |
.attr("height", height) | |
.call(zoom) | |
pathG.append("defs").append("path") | |
.datum({type: "Sphere"}) | |
.attr("id", "sphere") | |
.attr("d", path); | |
pathG.append("use") | |
.attr("class", "stroke") | |
.attr("xlink:href", "#sphere"); | |
pathG.append("use") | |
.attr("class", "fill") | |
.attr("xlink:href", "#sphere"); | |
pathG.append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("d", path); | |
var q = queue(); | |
q.defer(d3.json, "data/worldTopo.json"); | |
q.defer(d3.tsv,"data/boats.tsv"); | |
q.await(readMapData); | |
function readMapData(error,world,boatFile) { | |
// to render meridians/graticules on top of lands, use insert which adds new path before graticule in the selection | |
pathG.insert("path", ".graticule") | |
.datum(topojson.feature(world, world.objects.land)) | |
.attr("class", "land") | |
.attr("d", path) | |
pathG.insert("path", ".graticule") | |
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) | |
.attr("class", "boundary") | |
.attr("d", path); | |
pathG.attr("transform","translate(-1671.8701554571276,-859.1978421866252)scale(6)"); | |
//drawBoatsAndPaths(boatFile); | |
animateBoatPathTrail(findBoatTrajectory(2,boatFile),15000); | |
animateBoatPathTrail(findBoatTrajectory(0,boatFile),13000); | |
animateBoatPathTrail(findBoatTrajectory(6,boatFile),12500); | |
animateBoatPathTrail(findBoatTrajectory(7,boatFile),13500); | |
label.transition() | |
.duration(15000) | |
.ease("linear") | |
.tween("text", tweenYear); | |
pathG.transition() | |
.delay(1000) | |
.duration(5500) | |
.ease("linear") | |
.attr("transform","translate(-66.55078689208563,-72.20082705511209)scale(1.1487080744842741)"); | |
} | |
// apply transformations to map and all elements on it | |
function zoomed() | |
{ | |
pathG.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); | |
//grids.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); | |
//geofeatures.select("path.graticule").style("stroke-width", 0.5 / d3.event.scale); | |
pathG.selectAll("path.boundary").style("stroke-width", 0.5 / d3.event.scale); | |
} | |
function dragstarted(d) | |
{ | |
//stopPropagation prevents dragging to "bubble up" which triggers same event for all elements below this object | |
d3.event.sourceEvent.stopPropagation(); | |
d3.select(this).classed("dragging", true); | |
} | |
function dragged() { | |
projection.rotate([d3.event.x, -d3.event.y]); | |
pathG.selectAll("path").attr("d", path); | |
} | |
function dragended(d) | |
{ | |
d3.select(this).classed("dragging", false); | |
} | |
function drawBoatsAndPaths(boats) | |
{ | |
/* draw a boat path | |
for faster line drawing(without interpolation), instead of pathLine use .attr("d", function(d) { return "M" + d.map(projection).join("L"); }); | |
*/ | |
var temp = 0; | |
boatPaths = pathG.selectAll("path.boatPath") | |
.data(pathCount) | |
.enter().append("path") | |
.attr("d",function(d,i){ | |
var l; | |
if(i == 0) | |
l = pathLine(boats.slice(0,d)); | |
else if(i<20) | |
l = pathLine(boats.slice(temp,d+temp)); | |
var trX = projection([boats[temp].Lon,boats[temp].Lat])[0]; | |
var trY = projection([boats[temp].Lon,boats[temp].Lat])[1]; | |
circles[i] = pathG.append("circle") | |
.attr("r", 2) | |
.attr("transform", "translate(" + trX + ","+ trY +")") | |
.attr("fill","blue"); | |
temp += d; | |
return l; | |
}) | |
.attr("class","boatPath") | |
.style("display","none"); | |
} | |
function animateBoats() | |
{ | |
for(var i=0;i<pathCount.length;i++) | |
{ | |
circles[i].transition() | |
.duration(10000) | |
.attrTween("transform",translateAlong(boatPaths[0][i])); | |
} | |
} | |
function translateAlong(path) | |
{ | |
var l = path.getTotalLength(); | |
return function(d, i, a) | |
{ | |
return function(t) | |
{ | |
var p = path.getPointAtLength(t * l); | |
return "translate(" + p.x + "," + p.y + ")"; | |
}; | |
}; | |
} | |
// creates trajectory made of line segments from our raw data. When we create json, this might change. | |
function findBoatTrajectory(bid,boats) | |
{ | |
var temp = 0; | |
var trajArr = []; | |
if(bid == 0) | |
trajArr = boats.slice(0,pathCount[bid]); | |
else | |
{ | |
var cum = 0; | |
for(var i=0;i<bid;i++) | |
cum += pathCount[i]; | |
trajArr = boats.slice(cum,cum+pathCount[bid]); | |
} | |
return trajArr; | |
} | |
// animates boat trajectory. This is in use right now. | |
function animateBoatPathTrail(trajectory,speed) | |
{ | |
var circle; | |
var tmpBoatTraj = pathG.append("path") | |
.attr("class","boatPath") | |
.attr("d",function(){ | |
return pathLine(trajectory); | |
}) | |
.on("mouseover",pathClicked); | |
var totalLength = tmpBoatTraj.node().getTotalLength(); | |
tmpBoatTraj | |
.attr("stroke-dasharray", totalLength + " " + totalLength) | |
.attr("stroke-dashoffset", totalLength) | |
.transition() | |
.duration(speed) | |
.ease("linear") | |
.attr("stroke-dashoffset", 0); | |
// boat is not sync with path animation at the moment | |
/*var trX = projection([trajectory[0].Lon,trajectory[0].Lat])[0]; | |
var trY = projection([trajectory[0].Lon,trajectory[0].Lat])[1]; | |
circle = pathG.append("circle") | |
.attr("r", 2) | |
.attr("transform", "translate(" + trX + ","+ trY +")") | |
.attr("fill","blue"); | |
circle.transition() | |
.duration(10000) | |
.attrTween("transform",translateAlong(tmpBoatTraj.node()));*/ | |
} | |
function animateBoatVariableSpeed(circle,path,speed) | |
{ | |
circle.transition() | |
.duration(500/+speed) | |
.attrTween("transform", translateAlong(path.node())) | |
.each("end", function() | |
{ | |
if(index<boats.length) | |
{ | |
//animateBoatVariableSpeed(circle,boatPath[index++], boats[index].speed); | |
} | |
}); | |
} | |
function tweenYear() { | |
var date = d3.interpolate(0, 3042615000); | |
return function(t) { | |
displayDate(date(t)); }; | |
} | |
// Updates the display to show the specified year. | |
function displayDate(msec) { | |
label.text(format(new Date(msec+1293801017000))); | |
} | |
function pathClicked() | |
{ | |
d3.event.sourceEvent.stopPropagation(); | |
d3.select(this).attr("opacity",0); | |
} | |
d3.select(self.frameElement).style("height", height + "px"); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.choropleth { | |
} | |
.graticule { | |
fill: none; | |
stroke: #777; | |
stroke-opacity: .5; | |
stroke-width: .5px; | |
} | |
.overlay { | |
fill: none; | |
pointer-events: all; | |
} | |
/* region specific style */ | |
.choro_mesh { | |
/*stroke: #9C1400;*/ | |
stroke: #4A2A1B; | |
fill: #D8C5B6; | |
} | |
.boatPath { | |
fill: none; | |
stroke-width: 1px; | |
stroke-opacity: .8; | |
/*stroke-dasharray: 3,2;*/ | |
stroke: #CB301E; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function WorldMap() { | |
var mapJSON = {}; | |
var zoom = d3.behavior.zoom() | |
.scaleExtent([1,6]) | |
.on("zoom",zoomed); | |
var drag = d3.behavior.drag() | |
.origin(function(d) { return d; }) | |
.on("dragstart", dragstarted) | |
.on("drag", dragged) | |
.on("dragend", dragended); | |
var geofeatures,grids,boatPath = [],circles=[]; | |
var graticule = d3.geo.graticule(); | |
var width = 1296, | |
height = 703; | |
var data; | |
var index = 0; | |
var projection = d3.geo.mercator(); | |
var path = d3.geo.path() | |
.projection(projection); | |
var svg; | |
var totalGeoEl = 0; | |
function chart(selection) | |
{ | |
selection.each(function(dataArr) | |
{ | |
boats = dataArr[0]; | |
pathCount = dataArr[1]; | |
//Select svg if it exists in this DOM element | |
svg = d3.select(this).selectAll("svg.choropleth").data([mapJSON.objects]); | |
svg.enter().append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.attr("class", "choropleth"); | |
//uncomment below if you want to draw meridians and parallels | |
/*grids = svg.append("g").append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("d", path);*/ | |
geofeatures = svg.append("g"); | |
svg.append("rect") | |
.attr("class", "overlay") | |
.attr("width", width) | |
.attr("height", height) | |
.call(zoom); | |
var country = topojson.mesh(mapJSON, mapJSON.objects.countries); | |
var countries = topojson.feature(mapJSON, mapJSON.objects.countries); | |
//var state = countries.features.filter(function(d) { return +d.id === 7; })[0]; | |
projection | |
.scale(1) | |
.translate([0, 0]); | |
// Calculate the scale and translate values automatically from boundaries of shapefile | |
var b = path.bounds(country), | |
s = .95 / 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); | |
// bind the geometry data and draw the world | |
/*geofeatures.selectAll("path.choro_mesh") | |
.data(countries.features) | |
.enter().append("path") | |
.attr("class","choro_mesh") | |
.attr("d", path);*/ | |
// function to draw path lines on the map | |
var pathLine = d3.svg.line() | |
.interpolate("cardinal") | |
.x(function(d) { | |
return projection([d.Lon, d.Lat])[0]; }) | |
.y(function(d) { | |
return projection([d.Lon, d.Lat])[1]; }); | |
var colors = d3.scale.category20(); | |
/* draw a boat path | |
for faster line drawing(without interpolation), instead of pathLine use .attr("d", function(d) { return "M" + d.map(projection).join("L"); }); | |
*/ | |
var temp = 0; | |
boatPaths = geofeatures.selectAll("path.boatPath") | |
.data(pathCount) | |
.enter().append("path") | |
.attr("d",function(d,i){ | |
var l; | |
if(i == 0) | |
l = pathLine(boats.slice(0,d)); | |
else if(i<20) | |
l = pathLine(boats.slice(temp,d+temp)); | |
var trX = projection([boats[temp].Lon,boats[temp].Lat])[0]; | |
var trY = projection([boats[temp].Lon,boats[temp].Lat])[1]; | |
circles[i] = geofeatures.append("circle") | |
.attr("r", 2) | |
.attr("transform", "translate(" + trX + ","+ trY +")") | |
.attr("fill","blue"); | |
temp += d; | |
return l; | |
}) | |
.attr("class","boatPath") | |
.style("stroke",function(d,i){return colors[i];}) | |
.style("display","none"); | |
temp = 0; | |
for(var i=0;i<pathCount.length;i++) | |
{ | |
circles[i].transition() | |
.duration(10000) | |
.attrTween("transform",translateAlong(boatPaths[0][i])); | |
} | |
/*for(var j=0;j<3;j++) | |
{ | |
//index = 0; | |
animateBoatConstantSpeed(circles[j],boatPath[j]); | |
//animateBoatVariableSpeed(circles[j],boatPath[j][index++],boats[index].speed); | |
}*/ | |
}); | |
function animateBoatVariableSpeed(circle,path,speed) | |
{ | |
circle.transition() | |
.duration(500/+speed) | |
.attrTween("transform", translateAlong(path.node())) | |
.each("end", function() | |
{ | |
if(index<boats.length) | |
{ | |
animateBoatVariableSpeed(circle,boatPath[index++], boats[index].speed); | |
} | |
}); | |
} | |
function animateBoatConstantSpeed(circle,path) | |
{ | |
circle.transition() | |
.duration(10000) | |
.attrTween("transform", translateAlong(path.node())); | |
} | |
function translateAlong(path) | |
{ | |
var l = path.getTotalLength(); | |
return function(d, i, a) | |
{ | |
return function(t) | |
{ | |
var p = path.getPointAtLength(t * l); | |
return "translate(" + p.x + "," + p.y + ")"; | |
}; | |
}; | |
} | |
} | |
// apply transformations to map and all elements on it | |
function zoomed() | |
{ | |
geofeatures.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); | |
//grids.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); | |
//geofeatures.select("path.graticule").style("stroke-width", 0.5 / d3.event.scale); | |
geofeatures.selectAll("path.choro_mesh").style("stroke-width", 0.8 / d3.event.scale); | |
} | |
function dragstarted(d) | |
{ | |
d3.event.sourceEvent.stopPropagation(); | |
d3.select(this).classed("dragging", true); | |
} | |
function dragged(d) | |
{ | |
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); | |
} | |
function dragended(d) | |
{ | |
d3.select(this).classed("dragging", false); | |
} | |
/********************************** | |
ACCESSORS & MODIFIERS OF THIS CHART | |
**********************************/ | |
chart.width = function(value) { | |
if (!arguments.length) return width; | |
width = value; | |
return chart; | |
}; | |
chart.height = function(value) { | |
if (!arguments.length) return height; | |
height = value; | |
return chart;}; | |
chart.intervalID = function(value) { | |
if (!arguments.length) return intervalID; | |
intervalID = value; | |
return chart;}; | |
chart.mapJSON = function(value) { | |
if (!arguments.length) return mapJSON; | |
mapJSON = value; | |
return chart;}; | |
chart.projection = function(value) { | |
if (!arguments.length) return projection; | |
projection = value; | |
return chart;}; | |
chart.mapGroup = function(value){ | |
if (!arguments.length) return geoFeatures; | |
geoFeatures = value; | |
return chart;}; | |
return chart; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment