Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 3, 2017 20:47
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 nitaku/ec5453f2e5c7f540b02ccd9cb085e40f to your computer and use it in GitHub Desktop.
Save nitaku/ec5453f2e5c7f540b02ccd9cb085e40f to your computer and use it in GitHub Desktop.
World map III

An improvement upon the previous example. A Natural Earth dataset is transformed into topoJSON while keeping the original properties. This lets it possible to display labels even for the countries that have no ISO code (stylized in italics in the map).

These are the commands used to prepare the data:

wget http://naciscdn.org/naturalearth/50m/cultural/ne_50m_admin_0_countries.zip
unzip ne_50m_admin_0_countries.zip
ogr2ogr -f GeoJSON ne_50m_admin_0_countries.geo.json ne_50m_admin_0_countries.shp
geo2topo -q 1e5 countries=ne_50m_admin_0_countries.geo.json > ne_50m_admin_0_countries.topo.json

Label placement is far from ideal. The centroid is not suitable in some cases (e.g., France). There is also a substantial overlap.

svg = d3.select 'svg'
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
# ZOOM
zoomable_layer = svg.append 'g'
zoom = d3.zoom()
.scaleExtent [-Infinity, Infinity]
.on 'zoom', () ->
zoomable_layer
.attrs
transform: d3.event.transform
# SEMANTIC ZOOM
# scale back all objects that have to be semantically zoomed
zoomable_layer.selectAll '.label > text'
.attrs
transform: "scale(#{1/d3.event.transform.k})"
svg.call(zoom)
# PROJECTION
projection = d3.geoWinkel3()
.rotate [0, 0]
.center [0, 0]
.scale (width - 3) / (2 * Math.PI)
.translate [width/2, height/2]
path = d3.geoPath projection
# GRATICULE and OUTLINE
graticule = d3.geoGraticule()
svg.append 'defs'
.append 'path'
.datum graticule.outline()
.attrs
id: 'sphere'
d: path
zoomable_layer.append 'use'
.attrs
class: 'sphere_fill'
'xlink:href': '#sphere'
contents = zoomable_layer.append 'g'
zoomable_layer.append 'path'
.datum graticule
.attrs
class: 'graticule'
d: path
zoomable_layer.append 'use'
.attrs
class: 'sphere_stroke'
'xlink:href': '#sphere'
d3.json 'ne_50m_admin_0_countries.topo.json', (geo_data) ->
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features
# countries
countries = contents.selectAll '.country'
.data countries_data
countries.enter().append 'path'
.attrs
class: 'country'
d: path
# labels
labels = contents.selectAll '.label'
.data countries_data
en_labels = labels.enter().append 'g'
.attrs
class: 'label'
transform: (d) ->
[x,y] = projection d3.geoCentroid(d)
return "translate(#{x},#{y})"
en_labels
.classed 'no_iso_code', (d) -> d.properties.iso_a2 is '-99'
en_labels.append 'text'
.text (d) -> d.properties.name_long
body, html {
padding: 0;
margin: 0;
height: 100%;
}
svg {
width: 100%;
height: 100%;
background: white;
}
.sphere_stroke {
fill: none;
stroke: black;
stroke-width: 2px;
vector-effect: non-scaling-stroke;
}
.sphere_fill {
fill: white;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: 0.5px;
stroke-opacity: 0.5;
vector-effect: non-scaling-stroke;
pointer-events: none;
}
.country {
fill: #DDD;
stroke: white;
stroke-width: 0.5;
vector-effect: non-scaling-stroke;
}
.country:hover {
fill: #CCC;
}
.label {
font-family: sans-serif;
font-size: 10px;
pointer-events: none;
text-anchor: middle;
}
.label.no_iso_code {
font-style: italic;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>World map III</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="//d3js.org/topojson.v2.min.js"></script>
</head>
<body>
<svg></svg>
<script src="index.js"></script>
</body>
</html>
// Generated by CoffeeScript 1.10.0
(function() {
var contents, graticule, height, path, projection, svg, width, zoom, zoomable_layer;
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
zoomable_layer = svg.append('g');
zoom = d3.zoom().scaleExtent([-Infinity, Infinity]).on('zoom', function() {
zoomable_layer.attrs({
transform: d3.event.transform
});
return zoomable_layer.selectAll('.label > text').attrs({
transform: "scale(" + (1 / d3.event.transform.k) + ")"
});
});
svg.call(zoom);
projection = d3.geoWinkel3().rotate([0, 0]).center([0, 0]).scale((width - 3) / (2 * Math.PI)).translate([width / 2, height / 2]);
path = d3.geoPath(projection);
graticule = d3.geoGraticule();
svg.append('defs').append('path').datum(graticule.outline()).attrs({
id: 'sphere',
d: path
});
zoomable_layer.append('use').attrs({
"class": 'sphere_fill',
'xlink:href': '#sphere'
});
contents = zoomable_layer.append('g');
zoomable_layer.append('path').datum(graticule).attrs({
"class": 'graticule',
d: path
});
zoomable_layer.append('use').attrs({
"class": 'sphere_stroke',
'xlink:href': '#sphere'
});
d3.json('ne_50m_admin_0_countries.topo.json', function(geo_data) {
var countries, countries_data, en_labels, labels;
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features;
countries = contents.selectAll('.country').data(countries_data);
countries.enter().append('path').attrs({
"class": 'country',
d: path
});
labels = contents.selectAll('.label').data(countries_data);
en_labels = labels.enter().append('g').attrs({
"class": 'label',
transform: function(d) {
var ref, x, y;
ref = projection(d3.geoCentroid(d)), x = ref[0], y = ref[1];
return "translate(" + x + "," + y + ")";
}
});
en_labels.classed('no_iso_code', function(d) {
return d.properties.iso_a2 === '-99';
});
return en_labels.append('text').text(function(d) {
return d.properties.name_long;
});
});
}).call(this);
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment