Skip to content

Instantly share code, notes, and snippets.

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

This iteration of a general purpose world map (see the previous attempt) adds coloring according to the color classes defined in Natural Earth's dataset, making it more like a political map.

The mapcolor7 property is used in conjunction with a desaturated version of d3's schemeCategory10 scale.

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})"
# LOD & OVERLAPPING
lod(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()
# COLORS
desaturate = (color) ->
hcl = d3.hcl color
hcl.c -= 10
return hcl
color = d3.scaleOrdinal(d3.schemeCategory10.map desaturate)
zoomable_layer.append 'path'
.datum graticule.outline()
.attrs
class: 'sphere_fill'
d: path
contents = zoomable_layer.append 'g'
zoomable_layer.append 'path'
.datum graticule
.attrs
class: 'graticule'
d: path
zoomable_layer.append 'path'
.datum graticule.outline()
.attrs
class: 'sphere_stroke'
d: path
d3.json 'ne_50m_admin_0_countries.topo.json', (geo_data) ->
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features
# label each polygon instead of each multipolygon (to help with islands etc.)
labels_data = []
countries_data.forEach (d) ->
if d.geometry.type is 'Polygon'
labels_data.push d
else if d.geometry.type is 'MultiPolygon'
d.geometry.coordinates.forEach (p) ->
labels_data.push {
coordinates: p
properties: d.properties
type: 'Polygon'
}
# compute area to aid label hiding
labels_data.forEach (d) -> d.area = d3.geoArea(d)
# labels_data.sort (a,b) -> a.area - b.area
# countries
countries = contents.selectAll '.country'
.data countries_data
en_countries = countries.enter().append 'path'
.attrs
class: 'country'
d: path
fill: (d) -> color(d.properties.mapcolor7)
en_countries.append 'title'
.text (d) -> d.properties.name_long
# labels
labels = contents.selectAll '.label'
.data labels_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
# lod
lod(1)
lod = (z) ->
zoomable_layer.selectAll '.label'
.classed 'hidden', (d) -> d.area < Math.pow(0.2/z,2)
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-opacity: 0.3;
stroke: white;
stroke-width: 0.5;
vector-effect: non-scaling-stroke;
}
.country:hover {
fill-opacity: 0.6;
}
.label {
font-family: sans-serif;
font-size: 10px;
pointer-events: none;
text-anchor: middle;
}
.label.no_iso_code {
font-style: italic;
}
.label.hidden {
display: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>World map VI</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 color, contents, desaturate, graticule, height, lod, 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
});
zoomable_layer.selectAll('.label > text').attrs({
transform: "scale(" + (1 / d3.event.transform.k) + ")"
});
return lod(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();
desaturate = function(color) {
var hcl;
hcl = d3.hcl(color);
hcl.c -= 10;
return hcl;
};
color = d3.scaleOrdinal(d3.schemeCategory10.map(desaturate));
zoomable_layer.append('path').datum(graticule.outline()).attrs({
"class": 'sphere_fill',
d: path
});
contents = zoomable_layer.append('g');
zoomable_layer.append('path').datum(graticule).attrs({
"class": 'graticule',
d: path
});
zoomable_layer.append('path').datum(graticule.outline()).attrs({
"class": 'sphere_stroke',
d: path
});
d3.json('ne_50m_admin_0_countries.topo.json', function(geo_data) {
var countries, countries_data, en_countries, en_labels, labels, labels_data;
countries_data = topojson.feature(geo_data, geo_data.objects.countries).features;
labels_data = [];
countries_data.forEach(function(d) {
if (d.geometry.type === 'Polygon') {
return labels_data.push(d);
} else if (d.geometry.type === 'MultiPolygon') {
return d.geometry.coordinates.forEach(function(p) {
return labels_data.push({
coordinates: p,
properties: d.properties,
type: 'Polygon'
});
});
}
});
labels_data.forEach(function(d) {
return d.area = d3.geoArea(d);
});
countries = contents.selectAll('.country').data(countries_data);
en_countries = countries.enter().append('path').attrs({
"class": 'country',
d: path,
fill: function(d) {
return color(d.properties.mapcolor7);
}
});
en_countries.append('title').text(function(d) {
return d.properties.name_long;
});
labels = contents.selectAll('.label').data(labels_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';
});
en_labels.append('text').text(function(d) {
return d.properties.name_long;
});
return lod(1);
});
lod = function(z) {
return zoomable_layer.selectAll('.label').classed('hidden', function(d) {
return d.area < Math.pow(0.2 / z, 2);
});
};
}).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