Skip to content

Instantly share code, notes, and snippets.

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

A reference gist for the implementation of a general purpose map of the world in d3 version 4.

The projection of choice is the Winkel Tripel projection, which has a very low average error in representing area, direction, and distance (see this article for more details).

Data is obtained from Mike Bostock's topojson world atlas, which is in turn based on the Natural Earth dataset.

Zooming and panning is enabled via SVG transforms, so no reprojection and clipping operations happen during user interaction.

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
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 'https://unpkg.com/world-atlas@1/world/50m.json', (geo_data) ->
contents.append 'path'
.datum topojson.feature(geo_data, geo_data.objects.countries)
.attrs
class: 'countries'
d: path
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;
}
.countries {
fill: black;
stroke: white;
stroke-width: 0.5;
vector-effect: non-scaling-stroke;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>World map</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() {
return zoomable_layer.attrs({
transform: d3.event.transform
});
});
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('https://unpkg.com/world-atlas@1/world/50m.json', function(geo_data) {
return contents.append('path').datum(topojson.feature(geo_data, geo_data.objects.countries)).attrs({
"class": 'countries',
d: path
});
});
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment