Skip to content

Instantly share code, notes, and snippets.

@martgnz
Created January 3, 2018 13:17
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 martgnz/34880f7320eb5a6745e2ed7de7914223 to your computer and use it in GitHub Desktop.
Save martgnz/34880f7320eb5a6745e2ed7de7914223 to your computer and use it in GitHub Desktop.
Demers Cartogram II
border: no
height: 560
license: gpl-3.0

A cartogram of Catalan comarques sized by population. These administrative divisions are roughly equivalent to the UK or US counties.

The map can serve as a base for a visualization which emphasizes population density. Data retrieved from the Catalan Cartographic Institute, and Idescat, their statistics portal.

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.
id name abbr pop
01 Alt Camp A. C. 44038
02 Alt Empordà Alt Emp. 136522
03 Alt Penedès A. Pen. 106438
04 Alt Urgell Alt Urgell 20220
05 Alta Ribagorça Alta Ribagorça 3840
06 Anoia Anoia 118405
07 Bages Bages 173724
08 Baix Camp B. Camp 188657
09 Baix Ebre B. Ebre 77606
10 Baix Empordà B. Emp. 130737
11 Baix Llobregat B. Llobregat 807796
12 Baix Penedès B. Pen. 100366
13 Barcelonès Barcelonès 2226828
14 Berguedà Berguedà 38926
15 Cerdanya Cerdanya 17756
16 Conca de Barberà Conca de Barberà 20061
17 Garraf Garraf 145257
18 Garrigues Garrigues 19017
19 Garrotxa Garr. 55141
20 Gironès Gironès 183693
21 Maresme Maresme 438447
22 Montsià Montsià 68028
23 Noguera Noguera 38300
24 Osona Osona 154783
25 Pallars Jussà Pallars Jussà 13075
26 Pallars Sobirà Pallars Sobirà 6896
27 Pla d'Urgell Pla d'Urgell 36850
28 Pla de l'Estany Pla de l'Estany 31577
29 Priorat Priorat 9285
30 Ribera d'Ebre Ribera d'Ebre 21885
31 Ripollès Ripollès 24889
32 Segarra Segarra 22374
33 Segrià Segrià 204549
34 Selva Selva 165763
35 Solsonès Solsonès 13463
36 Tarragonès Tarragonès 250795
37 Terra Alta Terra Alta 11530
38 Urgell Urgell 35870
39 Aran Aran 9850
40 Vallès Occidental V. Occident. 908026
41 Vallès Oriental V. Oriental 401820
42 Moianès Moianès 13193
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v3.min.js"></script>
<script>
const padding = 2;
const margin = { top: 10, right: 10, bottom: 10, left: 10 };
const width = 560 - margin.left - margin.right;
const height = 560 - margin.top - margin.bottom;
const svg = d3
.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
const pop = d3.map();
const name = d3.map();
const size = d3.scaleSqrt().range([5, 120]);
const font = d3.scaleLinear().range([6, 20]);
d3
.queue()
.defer(d3.json, 'comarques.json')
.defer(d3.csv, 'data.csv', d => {
d.pop = +d.pop;
pop.set(d.id, d.pop);
name.set(d.id, d.abbr);
return d;
})
.await(ready);
function ready(error, cat, data) {
if (error) throw error;
size.domain(d3.extent(data, d => d.pop));
font.domain(d3.extent(data, d => d.pop));
const comarques = topojson.feature(cat, cat.objects.comarques);
const features = comarques.features;
// File is already projected
const projection = d3
.geoIdentity()
.reflectY(true)
.fitSize([width, height], comarques);
const path = d3.geoPath().projection(projection);
features.forEach(function(d) {
d.pos = path.centroid(d);
d.area = size(pop.get(d.properties.CODICOMAR));
[d.x, d.y] = d.pos;
});
const simulation = d3
.forceSimulation(features)
.force('x', d3.forceX(d => d.x).strength(0.1))
.force('y', d3.forceY(d => d.y).strength(0.1))
.force('collide', collide);
for (let i = 0; i < 120; ++i) simulation.tick();
const rect = svg
.selectAll('g')
.data(features)
.enter()
.append('g')
.attr('transform', d => `translate(${d.x}, ${d.y})`);
rect
.append('rect')
.attr('width', d => d.area)
.attr('height', d => d.area)
.attr('x', d => -d.area / 2)
.attr('y', d => -d.area / 2)
.attr('fill', '#ccc')
.attr('stroke', 'white')
.attr('rx', 2);
rect
.append('text')
.filter(d => d.area > 18) // Only labeling after a threshold
.style('font-family', 'sans-serif')
.style('font-size', d => `${font(pop.get(d.properties.CODICOMAR))}px`)
.attr('text-anchor', 'middle')
.attr('dy', 2)
.text(d => name.get(d.properties.CODICOMAR));
// From https://bl.ocks.org/mbostock/4055889
function collide() {
for (var k = 0, iterations = 4, strength = 0.5; k < iterations; ++k) {
for (var i = 0, n = features.length; i < n; ++i) {
for (var a = features[i], j = i + 1; j < n; ++j) {
var b = features[j],
x = a.x + a.vx - b.x - b.vx,
y = a.y + a.vy - b.y - b.vy,
lx = Math.abs(x),
ly = Math.abs(y),
r = a.area / 2 + b.area / 2 + padding;
if (lx < r && ly < r) {
if (lx > ly) {
lx = (lx - r) * (x < 0 ? -strength : strength);
(a.vx -= lx), (b.vx += lx);
} else {
ly = (ly - r) * (y < 0 ? -strength : strength);
(a.vy -= ly), (b.vy += ly);
}
}
}
}
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment