Skip to content

Instantly share code, notes, and snippets.

@ganeshv
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ganeshv/2b852863d91ee21ddf71 to your computer and use it in GitHub Desktop.
Save ganeshv/2b852863d91ee21ddf71 to your computer and use it in GitHub Desktop.
World Map Template

World map chloropleth template

This template follows pigshell's convention for "gist templates":

  • It is supplied data using postMessage(), as a single object of the form { opts: {...}, data: [...] }
  • It posts a message to the parent with an object of the form { height: <number> } to enable the framing context to adjust the height of the iframe.
  • If the URL does not contain a hash fragment, it displays sample data from the containing gist.

The following options are supported:

{
    title: "", // Title of map
    projection: "kavrayskiy", // Projection type - valid values are "orthographic", "kavrayskiy"
    field: "data", // Object field to treat as data [default: data]
    country: "country", // Object field to treat as country [default: country]
    colors: "RdYlGn", // Colorbrewer scale (see samples at http://bl.ocks.org/mbostock/5577023), default: RdYlGn
    inverse: false // Invert color scale (lower numbers are better/greener)
}

Data is of the form:

[
  { "country": "Afghanistan", "data": 123, "foo": "some other field" },
  { "country": 365, "data": 234, "bar": "yet another field" },
  ...
]

Countries may be specified by name, ISO 3166-1 two-letter, three-letter or numeric code. We use data sourced from countries.git.

The data field is coerced into numeric form, ignored if null.

Sample data is life expectancy data for countries, obtained from Wikipedia. The template is based on Rotate the world by Jason Davies. Colors by Cynthia Brewer.

Examples (to be run in pigshell):

ycat http://en.wikipedia.org/wiki/List_of_countries_by_life_expectancy | to text | jf '$$.html($$(x).find("table.wikitable").eq(0))' | table2js -e "tr" foo country data | iframe -o title="Life expectancy (years)",proj=orthographic -g 'http://bl.ocks.org/ganeshv/raw/2b852863d91ee21ddf71/#waitforit'

ycat http://en.wikipedia.org/wiki/List_of_countries_by_literacy_rate | table2js -e "table.wikitable tr" country foo bar data | jf 'x.data = +x.data.replace(/%/, ""), x' | grep -e 'x.data > 0' | iframe -o title="Adult Literacy (%)" -g 'http://bl.ocks.org/ganeshv/raw/2b852863d91ee21ddf71/#waitforit'
<!DOCTYPE html>
<!--
World map chloropleth, based on http://bl.ocks.org/jasondavies/4188334
and https://www.jasondavies.com/maps/rotate/. Colors by Cynthia Brewer
http://colorbrewer.org
This template takes a list of Javascript objects, e.g.
[ { "country": "Afghanistan", "data": 123, "foo": "some other field" },
{ "country": 365, "data": 234, "bar": "yet another field" },
...
]
Template options:
title: Title of map
projection: Projection type - valid values are "orthographic", "kavrayskiy".
field: Object field to treat as data [default: data]
country: Object field to treat as country [default: country]
colors: Colorbrewer scale (see samples at http://bl.ocks.org/mbostock/5577023), default: RdYlGn
inverse: Invert color scale (lower numbers are better/greener)
Countries may be specified by name, ISO 3166-1 two-letter, three-letter or
numeric code. We use data from https://github.com/mledoze/countries.git
Data is coerced into numeric form, ignored if null.
Orthographic projection can be zoomed by scroll-wheel and manipulated by
dragging.
Examples:
cat http://en.wikipedia.org/wiki/List_of_countries_by_literacy_rate | table2js -e "table.wikitable tr" country foo bar data | jf 'x.country = x.country.trim(), x.data = +x.data.replace(/%/, ""), x.data < 0 ? undefined : x' | iframe -o title="Adult Literacy (%)" -g /usr/share/template/d3-worldmap1
Tests:
echo '[{"country": 356, "data": 100}, {"country": "Antarctica", "data": 0}]'| jf '$.parseJSON(x)' | iframe -g /usr/share/template/d3-worldmap1
-->
<meta charset="utf-8">
<style>
.country {
fill: #ccc;
stroke: #666;
stroke-width: .5px;
stroke-linejoin: round;
}
.graticule {
fill: none;
stroke: #666;
stroke-width: .5px;
stroke-dasharray: 2,2;
}
.outline {
stroke: #000;
stroke-width: 1px;
}
.foreground {
fill: none;
}
.key {
font: 10px sans-serif;
position: absolute;
top: 0;
left: 0;
}
.rotsel {
pointer-events: none;
}
.rotbox {
stroke: #000;
stroke-width: 1px;
fill: none;
pointer-events: all;
}
.caption {
font: 12px sans-serif;
font-weight: bold;
}
.key path {
display: none;
}
.key line {
stroke: #000;
shape-rendering: crispEdges;
}
.sea {
fill: rgba(166,206,245,.5);
}
.tooltip {
position: absolute;
text-align: center;
padding: 8px;
font: 11px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
#container {
position: relative;
}
#footer {
position: absolute;
font: 12px sans-serif;
bottom: 22px;
right: 150px;
z-index: 1;
text-align: right;
visibility: hidden;
}
.hint {
color: #999;
}
</style>
<body>
<div id="container">
<div id="footer">
<input id="rotstate" type="checkbox" name="rotstate" value="true">Spin</input>
<div class="hint">mousewheel to zoom, drag to rotate</div>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/d3.v3.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/d3.geo.projection.v0.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/topojson.v1.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/colorbrewer.v1.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/queue.v1.min.js"></script>
<script src="http://pigshell.com/common/d3.v3/d3.geo.zoom.js"></script>
<script>
var width = 960,
height = 500,
sheight = 550;
var speed = 1.0,
rotating = false;
var defaults= {
title: "",
field: "data",
country: "country",
colors: "RdYlGn",
proj: "kavrayskiy",
inverse: ""
};
window.addEventListener('message', function(e) {
var opts = e.data.opts,
data = e.data.data;
return main(opts, data);
});
function main(o, data) {
var opts = $.extend({}, defaults, o),
colorscale = opts.colors,
field = opts.field;
if (colorbrewer[colorscale] === undefined) {
colorscale = "RdYlGn";
}
var ortho = d3.geo.orthographic()
.precision(.5)
.translate([width / 2, height / 2])
.clipAngle(90)
.clipExtent([[1, 1], [width - 1, height - 1]])
.scale(250)
.rotate([-80, -10]);
var kavrayskiy = d3.geo.kavrayskiy7();
var graticule = d3.geo.graticule();
var projections = {
'kavrayskiy': {
'projection': kavrayskiy,
'outline_datum': graticule.outline
},
'orthographic': {
'projection': ortho,
'outline_datum': {type: "Sphere"}
}
};
var projtype = projections[opts.proj] ? opts.proj : "kavrayskiy";
var projection = projections[projtype]['projection'];
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", sheight);
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
svg.append("path")
.datum(projections[projtype]['outline_datum'])
.attr("class", "sea foreground")
.attr("d", path);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
svg.append("path")
.datum(projections[projtype]['outline_datum'])
.attr("class", "outline foreground")
.attr("d", path);
data = data.map(function(d) {
d[field] = (d[field] === undefined || isNaN(+d[field])) ? null : +d[field];
return d;
}).filter(function(d) {
return d[field] !== null;
});
var datadomain = d3.extent(data.map(function(x) { return x[field]; })),
colors = d3.scale.quantize()
.domain(opts.inverse ? [datadomain[1], datadomain[0]] : datadomain)
.range(colorbrewer[colorscale][9]);
var x = d3.scale.linear()
.domain(datadomain)
.range([0, 240]);
var tf = ".0f",
tsign = "",
drange = datadomain[1] - datadomain[0];
if (datadomain[0] < 0) {
tsign = "+";
}
if (drange <= 2.0) {
tf = ".2f";
} else if (drange < 10.0) {
tf = ".1f";
}
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(x)
.tickSize(13)
.tickFormat(d3.format(tsign + tf));
var xbar = svg.append("g")
.attr("transform", "translate(" + (width / 2 - 120) + "," + (sheight - 30) + ")")
.attr("class", "key");
xbar.selectAll("rect")
.data(d3.pairs(x.ticks(10)))
.enter().append("rect")
.attr("height", 8)
.attr("x", function(d) { return x(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.style("fill", function(d) { return colors(d[0]); });
xbar.call(xAxis).append("text")
.attr("class", "caption")
.attr("y", -6)
.attr("x", x(x.ticks(10)[0]))
.text(opts.title);
queue()
.defer(d3.json, "http://pigshell.com/common/d3.v3/world-110m.json")
.defer(d3.json, "http://pigshell.com/common/d3.v3/countries.json")
.await(loaded);
function loaded(err, world, countrydb) {
var countries = topojson.feature(world, world.objects.countries).features;
data = data.map(function(x) {
var c = x[opts.country];
if (c === undefined) {
return {};
}
if (!isNaN(+c)) {
c = c.toString();
}
if (typeof c === 'string' || c instanceof String) {
c = c.trim();
var cl = c.toLowerCase();
var clist = countrydb.filter(function(i) {
return i["name"].toLowerCase() === cl ||
i["cca3"] === c || i["cca2"] === c ||
i["nativeName"] === cl ||
+i["ccn3"] === +c ||
i["altSpellings"].indexOf(c) !== -1;
});
x["_id"] = clist.length ? +clist[0]["ccn3"] : -1;
} else {
x["_id"] = -1;
}
return x;
});
countries = countries.map(function(c) {
c.properties["_data"] = data.filter(function(x) { return x["_id"] === c.id; })[0];
c.properties["_country"] = countrydb.filter(function(x) { return +x["ccn3"] === c.id; })[0];
return c;
});
svg.selectAll(".country")
.data(countries)
.enter().insert("path", ".outline")
.attr("class", "country foreground")
.attr("d", path)
.style("fill", function(d, i) {
return (d.properties["_data"] && d.properties["_data"][field] !== null) ? colors(d.properties["_data"][field]) : '#f8f8f8';
})
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
var str = d.properties["_country"] ? d.properties["_country"].name : "Unknown";
str += (d.properties["_data"] && d.properties["_data"][field] !== null) ? ": " + d.properties["_data"][field] : "";
tooltip.html(str)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
if (projtype === 'orthographic') {
d3.select("#footer").style("visibility", "visible");
d3.select("#rotstate")
.on("change", function() {
rotating = d3.select(this).property('checked');
});
svg.selectAll(".foreground")
.call(d3.geo.zoom().projection(projection)
.scaleExtent([projection.scale() * .7, projection.scale() * 10])
.on("zoom.redraw", function() {
d3.event.sourceEvent.preventDefault();
svg.selectAll("path").attr("d", path);
}));
d3.timer(function() {
if (!rotating) {
return;
}
var rot = projection.rotate();
projection.rotate([rot[0] + speed, rot[1], rot[2]]);
svg.selectAll("path").attr("d", path);
});
}
if (window.parent !== window) {
var myheight = document.documentElement.scrollHeight || document.body.scrollHeight;
window.parent.postMessage({height: myheight}, '*');
}
}
}
if (window.location.hash === "") {
d3.csv("life_expectancy.csv", function(err, data) {
main({}, data);
});
}
</script>
country data
Monaco 86.5
Japan 84.6
Andorra 84.2
Singapore 84
Hong Kong 83.8
San Marino 83.5
Iceland 83.3
Italy 83.1
Australia 83
Sweden 83
Switzerland 82.8
Canada 82.5
France 82.3
Israel 82.1
Spain 82
Luxembourg 82
Norway 81.9
New Zealand 81.7
Austria 81.5
Netherlands 81.5
Ireland 81.4
Cyprus 81.2
Finland 81
Germany 81
Greece 81
South Korea 81
Malta 81
Belgium 81
United Kingdom 81
Liechtenstein 80.7
Taiwan 80.6
Portugal 80
Slovenia 80
Costa Rica 79.8
United States 79.8
Chile 79.5
Denmark 79.5
Cuba 79.4
United Arab Emirates 79.2
Brunei 79
Barbados 78.5
Kuwait 78.2
Czech Republic 78
Panama 77.8
Poland 77
Croatia 77.5
Dominica 77.5
Uruguay 77.3
Mexico 77.2
Maldives 77.2
Bahrain 77
Belize 76.9
Slovakia 76.8
Bahamas 76.5
Grenada 76.5
Brazil 76.2
Estonia 76.1
Ecuador 76
Argentina 76
Saint Vincent and the Grenadines 76
Oman 76
Bosnia and Herzegovina 76
Lithuania 75.9
Antigua and Barbuda 75.8
Malaysia 75.7
Saint Lucia 75.5
Qatar 75.5
Mauritius 75.2
Saint Kitts and Nevis 75.1
Vietnam 75
Hungary 75
Venezuela 75
Macedonia 75
Syria 75
Thailand 74.9
Trinidad and Tobago 74.8
Seychelles 74.7
Sri Lanka 74.7
Paraguay 74.7
Peru 74.7
El Salvador 74.6
Jordan 74.6
Colombia 74.6
Tonga 74.5
Cape Verde 74.5
Latvia 74.5
Nicaragua 74.5
Libya 74.5
Georgia 74.5
Tunisia 74.5
Montenegro 74.5
Bulgaria 74.5
Suriname 74.5
Turkey 74.4
Armenia 74.4
Saudi Arabia 74.3
China 74.2
Samoa 74
Lebanon 74
Palau 74
Romania 74
Honduras 74
Albania 74
Serbia 74
Jamaica 73.5
Iran 73.5
Marshall Islands 73.5
Algeria 73.3
Egypt 73.2
Dominican Republic 73.2
Fiji 73
Philippines 73
Solomon Islands 73
Nauru 73
Morocco 73
Belarus 72.5
Indonesia 72
Sao Tome and Principe 72
Vanuatu 72
Azerbaijan 71.5
Guatemala 71.5
Ukraine 71
Moldova 71
Russia 70
Bhutan 70.8
Guyana 70.5
Micronesia 70
India 70
Bangladesh 70
Kyrgyzstan 69
Iraq 69
North Korea 69
Nepal 69
Mongolia 69
Bolivia 69
Uzbekistan 68.5
Laos 68
Myanmar 68
Kazakhstan 68
Comoros 68
Kiribati 68
Tajikistan 68
Papua New Guinea 67.5
Namibia 67.2
Pakistan 67
Turkmenistan 66.5
Cambodia 66
Ghana 66
Madagascar 66
Botswana 66
Gabon 64
Yemen 64
Timor-Leste 64
Senegal 64
Haiti 63
Sudan 62
Eritrea 61.5
Cameroon 61.5
South Africa 61
Djibouti 61
Ethiopia 60.5
Kenya 60
Rwanda 60
Afghanistan 60
Mauritania 59.5
Liberia 59
Tanzania 59
Benin 59
Gambia 59
Malawi 58
Republic of the Congo 58
Togo 57
Burkina Faso 56.5
Côte d'Ivoire 56.5
Uganda 56
Niger 56
Zambia 55.5
Guinea 55
Equatorial Guinea 54
Zimbabwe 54
Burundi 53
Nigeria 53
Mozambique 52.5
Angola 52
Chad 51
Mali 51
Lesotho 51
Guinea Bissau 50
Swaziland 50
Somalia 50
Democratic Republic of the Congo 49.5
Central African Republic 48.5
Sierra Leone 47.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment