This is a map of Jody from Tap Twice Tea's Journey across Asia. See a live version of this page! This map was made with d3. Thanks to Mike Bostock for the great Let's Make a Map tutorial.
Last active
August 29, 2015 14:16
-
-
Save dhoboy/7e1dfa5ed8ebebef8dfe to your computer and use it in GitHub Desktop.
Jody's Journey: final map
This file contains 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 styles */ | |
.country { fill: #B8B8B8; } | |
.interior-boundary { | |
fill: none; | |
stroke: #FFFFFF; | |
stroke-dasharray: 2,2; | |
stroke-linejoin: round; | |
} | |
.place, .place-label { fill: #444; } | |
text { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
font-size: 10px; | |
font-weight: bold; | |
} | |
.route { | |
fill: none; | |
stroke: red; | |
stroke-width: 3px; | |
stroke-linecap: round; | |
stroke-opacity: .8; | |
} | |
/* title and legend styles */ | |
.legend { | |
position: relative; | |
margin: 0px 10px 80px 10px; | |
} | |
.key { | |
position: absolute; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
font-size: 12px; | |
font-weight: bold; | |
margin: 0px 15px 0px 0px; | |
} | |
.Kolkata { left: 30px; } | |
.Darjeeling { left: 160px; } | |
.Nepal { left: 305px; } | |
.Myanmar { left: 425px; } | |
.Cambodia { left: 570px; } | |
.Laos { left: 725px; } | |
.Xishuangbanna { left: 850px; } | |
.Anhui { left: 30px; } | |
.Fujian { left: 160px; } | |
.Wuyi { left: 290px; } | |
.Qingdao { left: 390px; } | |
.Lishan { left: 515px; } | |
.Nantou { left: 640px; } | |
.Alishan { left: 775px; } | |
.Taiwan { left: 905px; } | |
.Shizuoka { left: 30px; } | |
.Uji { left: 180px; } | |
.Kyoto { left: 260px; } | |
.credit { left: 940px; top: 40px; font-size: 10px;} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script> | |
<script src="http://d3js.org/topojson.v1.js"></script> | |
<script> | |
var width = 1000, | |
height = 590, | |
margin = { top: 40, left: 30, right: 150, bottom: 50 }; | |
// draw svg | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
// map projection | |
var projection = d3.geo.patterson() | |
.center([58,54]) | |
.scale(520) | |
.translate([0,0]) | |
.precision(.1); | |
// path generator | |
var path = d3.geo.path() | |
.projection(projection) | |
.pointRadius(3); | |
// coordinates are added to this when constructRoute is called | |
var journey = [ | |
{name:"Kolkata", dates:"Feb. 17-22"}, | |
{name:"Darjeeling", dates:"Feb. 23-28"}, | |
{name:"Nepal", dates:"March 1-6"}, | |
{name:"Myanmar", dates:"March 7-10"}, | |
{name:"Cambodia", dates:"March 11-13"}, | |
{name:"Laos", dates:"March 14-18"}, | |
{name:"Xishuangbanna", dates:"March 18-21"}, | |
{name:"Anhui", dates:"March 22-28"}, | |
{name:"Fujian", dates:"March 29-31"}, | |
{name:"Wuyi", dates:"April 1-6"}, | |
{name:"Qingdao", dates:"April 6-10"}, | |
{name:"Lishan", dates:"April 11-14"}, | |
{name:"Nantou", dates:"April 15-18"}, | |
{name:"Alishan", dates:"April 18-20"}, | |
{name:"Taiwan", dates:"April 21-26"}, | |
{name:"Shizuoka", dates:"April 27-May 1"}, | |
{name:"Uji", dates:"May 2-4"}, | |
{name:"Kyoto", dest: "18", dates:"May 4-8"} | |
]; | |
d3.json("asia.json", function(error, asia) { | |
var subunits = topojson.feature(asia, asia.objects.subunits), | |
places = topojson.feature(asia, asia.objects.places), | |
route = constructRoute(journey, places.features); | |
// draw the map | |
svg.selectAll(".subunit") | |
.data(subunits.features) | |
.enter() | |
.append("path") | |
.attr("class", function(d) { return "country"; }) | |
.attr("d", path); | |
// draw interior boundaries between countries | |
svg.append("path") | |
.datum(topojson.mesh(asia, asia.objects.subunits, function(a, b) { | |
return a !== b; | |
})) | |
.attr("d", path) | |
.attr("class", "interior-boundary"); | |
// draw curved paths between places | |
svg.selectAll(".route") | |
.data(route.coordinates) | |
.enter() | |
.append("path") | |
.attr("class", "route") | |
.attr("d", function(d) { | |
var source = projection(d[0]); | |
var target = projection(d[1]); | |
var dx = target[0] - source[0], | |
dy = target[1] - source[1], | |
dr = Math.sqrt(dx * dx + dy * dy); | |
return "M" + source[0] + "," + source[1] + "A" + dr + "," + dr + " 0 0,1 " + target[0] + "," + target[1]; | |
}); | |
// draw places on map | |
svg.append("path") | |
.datum(places) | |
.attr("d", path) | |
.attr("class", "place"); | |
// set all place labels | |
svg.selectAll(".place-label") | |
.data(places.features) | |
.enter() | |
.append("text") | |
.attr("class", function(d) { return "place-label " + d.properties.name; }) | |
.attr("transform", function(d) { return "translate(" + projection(d.geometry.coordinates) + ")"; }) | |
.attr("x", function(d) { return d.geometry.coordinates[0] > -1 ? 6 : -6; }) | |
.attr("dy", ".35em") | |
.style("text-anchor", function(d) { return d.geometry.coordinates[0] > -1 ? "start" : "end"; }) | |
.text(function(d) { return d.properties.name; }); | |
// tweak specific labels to remove overlap with other labels and routh paths | |
svg.select(".place-label.Kyoto") | |
.attr("x", -33) | |
.attr("dy", "-.15em"); | |
svg.select(".place-label.Uji") | |
.attr("dy", "-.15em"); | |
svg.select(".place-label.Anhui") | |
.attr("x", 8); | |
svg.select(".place-label.Wuyi") | |
.attr("x", -29); | |
svg.select(".place-label.Fujian") | |
.attr("x", -34); | |
svg.select(".place-label.Nantou") | |
.attr("x", -40) | |
.attr("dy", ".01em"); | |
svg.select(".place-label.Alishan") | |
.attr("dy", ".7em"); | |
svg.select(".place-label.Lishan") | |
.attr("dy", ".5em"); | |
svg.select(".place-label.Nepal") | |
.attr("x", -33) | |
.attr("dy", ".25em"); | |
// add a key | |
var key = d3.select("body").append("div") | |
.attr("class", "legend"); | |
key.selectAll(".key") | |
.data(journey) | |
.enter() | |
.append("div") | |
.attr("class", function(d) { | |
return "key " + d.name; | |
}) | |
.style("top", function(d,i) { | |
if (i >= 7 && i <= 14) { | |
return "20px"; | |
} else if (i >= 15) { | |
return "40px"; | |
} | |
}) | |
.text(function(d) { | |
return d.name + ": " + d.dates; | |
}); | |
key.append("div") | |
.attr("class", "key credit") | |
.text("Map by: ") | |
.append("a") | |
.attr("href", "http://bl.ocks.org/dhoboy") | |
.attr("target", "_blank") | |
.text("dhoboy"); | |
}); | |
function constructRoute(journey, places) { | |
// get the coords of destinations in order | |
var r = { | |
type: "LineString", | |
coordinates: [] | |
}; | |
journey.forEach(function(d) { | |
places.forEach(function(p) { | |
if (d.name == p.properties.name) { | |
d.coords = p.geometry.coordinates; | |
p.dest = d.dest; | |
} | |
}) | |
r.coordinates.push(d.coords); | |
}); | |
// form coords for curved path drawing | |
var route = { | |
type: "MultiLineString", | |
coordinates: [] | |
}; | |
for (var i = 0; i < r.coordinates.length - 1; i++) { | |
route.coordinates.push([r.coordinates[i], r.coordinates[i+1]]); | |
} | |
return route; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment