Jody from Tap Twice Tea‘s destinations on his current trip across Asia. Map made with d3. Thanks Mike Bostock for the great Let’s Make a Map tutorial.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.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;
pointer-events: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.js"></script>
<script src="//d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="//d3js.org/topojson.v1.js"></script>
<script>
var width = 1000,
height = 590;
// 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(1.5);
// coordinates are added 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", 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 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; });
// adjust Kyoto and Nantou labels
svg.select(".place-label.Kyoto")
.attr("x", -30)
.attr("dy", ".15em");
// probably a better way than these magic numbers...
svg.select(".place-label.Nantou")
.attr("x", -38)
.attr("dy", ".15em");
});
function constructRoute(journey, places) {
// forming route for the path generator
var route = {
type: "LineString",
coordinates: []
};
// add coords to journey and route
journey.forEach(function(d) {
places.forEach(function(p) {
if (d.name == p.properties.name) {
d.coords = p.geometry.coordinates;
}
})
route.coordinates.push(d.coords);
});
return route;
}
</script>