Skip to content

Instantly share code, notes, and snippets.

@curran
Last active November 21, 2018 13:17
Show Gist options
  • Save curran/29d8ad97087177924b759b1f1db85a0f to your computer and use it in GitHub Desktop.
Save curran/29d8ad97087177924b759b1f1db85a0f to your computer and use it in GitHub Desktop.
Map with circles

Visualizing population by country using circles on a map! The area of each circle corresponds to the population of the country it represents. You can also pan & zoom, and hover over each country for more information.

<iframe width="560" height="315" src="https://www.youtube.com/embed/c0a02WHjgEs?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
(function (topojson,d3) {
'use strict';
const loadAndProcessData = () =>
Promise
.all([
d3.csv('https://vizhub.com/curran/datasets/un-population-estimates-2017-medium-variant.csv'),
d3.json('https://unpkg.com/visionscarto-world-atlas@0.0.4/world/50m.json')
])
.then(([unData, topoJSONdata]) => {
const rowById = unData.reduce((accumulator, d) => {
accumulator[d['Country code']] = d;
return accumulator;
}, {});
const countries = topojson.feature(topoJSONdata, topoJSONdata.objects.countries);
countries.features.forEach(d => {
Object.assign(d.properties, rowById[+d.id]);
});
const featuresWithPopulation = countries.features
.filter(d => d.properties['2018'])
.map(d => {
d.properties['2018'] = +d.properties['2018'].replace(/ /g, '') * 1000;
return d;
});
return {
features: countries.features,
featuresWithPopulation
};
});
const sizeLegend = (selection, props) => {
const {
sizeScale,
spacing,
textOffset,
numTicks,
tickFormat
} = props;
const ticks = sizeScale.ticks(numTicks)
.filter(d => d !== 0)
.reverse();
const groups = selection.selectAll('g').data(ticks);
const groupsEnter = groups
.enter().append('g')
.attr('class', 'tick');
groupsEnter
.merge(groups)
.attr('transform', (d, i) =>
`translate(0, ${i * spacing})`
);
groups.exit().remove();
groupsEnter.append('circle')
.merge(groups.select('circle'))
.attr('r', sizeScale);
groupsEnter.append('text')
.merge(groups.select('text'))
.text(tickFormat)
.attr('dy', '0.32em')
.attr('x', d => sizeScale(d) + textOffset);
};
const svg = d3.select('svg');
const projection = d3.geoNaturalEarth1();
const pathGenerator = d3.geoPath().projection(projection);
const radiusValue = d => d.properties['2018'];
const g = svg.append('g');
const colorLegendG = svg.append('g')
.attr('transform', `translate(40,310)`);
g.append('path')
.attr('class', 'sphere')
.attr('d', pathGenerator({type: 'Sphere'}));
svg.call(d3.zoom().on('zoom', () => {
g.attr('transform', d3.event.transform);
}));
const populationFormat = d3.format(',');
loadAndProcessData().then(countries => {
const sizeScale = d3.scaleSqrt()
.domain([0, d3.max(countries.features, radiusValue)])
.range([0, 33]);
g.selectAll('path').data(countries.features)
.enter().append('path')
.attr('class', 'country')
.attr('d', pathGenerator)
.attr('fill', d => d.properties['2018'] ? '#e8e8e8' : '#fecccc')
.append('title')
.text(d =>
isNaN(radiusValue(d))
? 'Missing data'
: [
d.properties['Region, subregion, country or area *'],
populationFormat(radiusValue(d))
].join(': ')
);
countries.featuresWithPopulation.forEach(d => {
d.properties.projected = projection(d3.geoCentroid(d));
});
g.selectAll('circle').data(countries.featuresWithPopulation)
.enter().append('circle')
.attr('class', 'country-circle')
.attr('cx', d => d.properties.projected[0])
.attr('cy', d => d.properties.projected[1])
.attr('r', d => sizeScale(radiusValue(d)));
g.append('g')
.attr('transform', `translate(45,215)`)
.call(sizeLegend, {
sizeScale,
spacing: 45,
textOffset: 10,
numTicks: 5,
tickFormat: populationFormat
})
.append('text')
.attr('class', 'legend-title')
.text('Population')
.attr('y', -45)
.attr('x', -30);
});
}(topojson,d3));
//# sourceMappingURL=bundle.js.map
{"version":3,"file":"bundle.js","sources":["loadAndProcessData.js","sizeLegend.js","index.js"],"sourcesContent":["import { feature } from 'topojson';\nimport { csv, json } from 'd3';\n\nexport const loadAndProcessData = () => \n Promise\n .all([\n csv('https://vizhub.com/curran/datasets/un-population-estimates-2017-medium-variant.csv'),\n json('https://unpkg.com/visionscarto-world-atlas@0.0.4/world/50m.json')\n ])\n .then(([unData, topoJSONdata]) => {\n \n const rowById = unData.reduce((accumulator, d) => {\n accumulator[d['Country code']] = d; \n return accumulator;\n }, {});\n\n const countries = feature(topoJSONdata, topoJSONdata.objects.countries);\n\n countries.features.forEach(d => {\n Object.assign(d.properties, rowById[+d.id]);\n });\n \n const featuresWithPopulation = countries.features\n .filter(d => d.properties['2018'])\n .map(d => {\n d.properties['2018'] = +d.properties['2018'].replace(/ /g, '') * 1000;\n return d;\n });\n\n return {\n features: countries.features,\n featuresWithPopulation\n };\n });\n","export const sizeLegend = (selection, props) => {\n const {\n sizeScale,\n spacing,\n textOffset,\n numTicks,\n tickFormat\n } = props;\n \n const ticks = sizeScale.ticks(numTicks)\n .filter(d => d !== 0)\n .reverse();\n\n const groups = selection.selectAll('g').data(ticks);\n const groupsEnter = groups\n .enter().append('g')\n .attr('class', 'tick');\n groupsEnter\n .merge(groups)\n .attr('transform', (d, i) =>\n `translate(0, ${i * spacing})`\n );\n groups.exit().remove();\n \n groupsEnter.append('circle')\n .merge(groups.select('circle'))\n .attr('r', sizeScale);\n \n groupsEnter.append('text')\n .merge(groups.select('text'))\n .text(tickFormat)\n .attr('dy', '0.32em')\n .attr('x', d => sizeScale(d) + textOffset);\n \n}","import {\n select,\n geoPath,\n geoCentroid,\n geoNaturalEarth1,\n zoom,\n event,\n scaleSqrt,\n max,\n format\n} from 'd3';\nimport { loadAndProcessData } from './loadAndProcessData';\nimport { sizeLegend } from './sizeLegend';\n\nconst svg = select('svg');\n\nconst projection = geoNaturalEarth1();\nconst pathGenerator = geoPath().projection(projection);\nconst radiusValue = d => d.properties['2018'];\n\nconst g = svg.append('g');\n\nconst colorLegendG = svg.append('g')\n .attr('transform', `translate(40,310)`);\n\ng.append('path')\n .attr('class', 'sphere')\n .attr('d', pathGenerator({type: 'Sphere'}));\n\nsvg.call(zoom().on('zoom', () => {\n g.attr('transform', event.transform);\n}));\n\nconst populationFormat = format(',');\n\nloadAndProcessData().then(countries => {\n \n const sizeScale = scaleSqrt()\n .domain([0, max(countries.features, radiusValue)])\n .range([0, 33]);\n \n g.selectAll('path').data(countries.features)\n .enter().append('path')\n .attr('class', 'country')\n .attr('d', pathGenerator)\n .attr('fill', d => d.properties['2018'] ? '#e8e8e8' : '#fecccc')\n .append('title')\n .text(d => \n isNaN(radiusValue(d))\n ? 'Missing data'\n : [\n d.properties['Region, subregion, country or area *'],\n populationFormat(radiusValue(d))\n ].join(': ')\n );\n \n countries.featuresWithPopulation.forEach(d => {\n d.properties.projected = projection(geoCentroid(d));\n });\n \n g.selectAll('circle').data(countries.featuresWithPopulation)\n .enter().append('circle')\n .attr('class', 'country-circle')\n .attr('cx', d => d.properties.projected[0])\n .attr('cy', d => d.properties.projected[1])\n .attr('r', d => sizeScale(radiusValue(d)));\n\n g.append('g')\n .attr('transform', `translate(45,215)`)\n .call(sizeLegend, {\n sizeScale,\n spacing: 45,\n textOffset: 10,\n numTicks: 5,\n tickFormat: populationFormat\n })\n .append('text')\n .attr('class', 'legend-title')\n .text('Population')\n .attr('y', -45)\n .attr('x', -30);\n\n});\n"],"names":["csv","json","feature","select","geoNaturalEarth1","geoPath","zoom","event","format","scaleSqrt","max","geoCentroid"],"mappings":"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"}
export const colorLegend = (selection, props) => {
const {
colorScale,
circleRadius,
spacing,
textOffset,
backgroundRectWidth
} = props;
const backgroundRect = selection.selectAll('rect')
.data([null]);
const n = colorScale.domain().length;
backgroundRect.enter().append('rect')
.merge(backgroundRect)
.attr('x', -circleRadius * 2)
.attr('y', -circleRadius * 2)
.attr('rx', circleRadius * 2)
.attr('width', backgroundRectWidth)
.attr('height', spacing * n + circleRadius * 2)
.attr('fill', 'white')
.attr('opacity', 0.8);
const groups = selection.selectAll('.tick')
.data(colorScale.domain());
const groupsEnter = groups
.enter().append('g')
.attr('class', 'tick');
groupsEnter
.merge(groups)
.attr('transform', (d, i) =>
`translate(0, ${i * spacing})`
);
groups.exit().remove();
groupsEnter.append('circle')
.merge(groups.select('circle'))
.attr('r', circleRadius)
.attr('fill', colorScale);
groupsEnter.append('text')
.merge(groups.select('text'))
.text(d => d)
.attr('dy', '0.32em')
.attr('x', textOffset);
}
<!DOCTYPE html>
<html>
<head>
<title>Circles on a Map</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/d3@5.6.0/dist/d3.min.js"></script>
<script src="https://unpkg.com/topojson@3.0.2/dist/topojson.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="bundle.js"></script>
</body>
</html>
import {
select,
geoPath,
geoCentroid,
geoNaturalEarth1,
zoom,
event,
scaleSqrt,
max,
format
} from 'd3';
import { loadAndProcessData } from './loadAndProcessData';
import { sizeLegend } from './sizeLegend';
const svg = select('svg');
const projection = geoNaturalEarth1();
const pathGenerator = geoPath().projection(projection);
const radiusValue = d => d.properties['2018'];
const g = svg.append('g');
const colorLegendG = svg.append('g')
.attr('transform', `translate(40,310)`);
g.append('path')
.attr('class', 'sphere')
.attr('d', pathGenerator({type: 'Sphere'}));
svg.call(zoom().on('zoom', () => {
g.attr('transform', event.transform);
}));
const populationFormat = format(',');
loadAndProcessData().then(countries => {
const sizeScale = scaleSqrt()
.domain([0, max(countries.features, radiusValue)])
.range([0, 33]);
g.selectAll('path').data(countries.features)
.enter().append('path')
.attr('class', 'country')
.attr('d', pathGenerator)
.attr('fill', d => d.properties['2018'] ? '#e8e8e8' : '#fecccc')
.append('title')
.text(d =>
isNaN(radiusValue(d))
? 'Missing data'
: [
d.properties['Region, subregion, country or area *'],
populationFormat(radiusValue(d))
].join(': ')
);
countries.featuresWithPopulation.forEach(d => {
d.properties.projected = projection(geoCentroid(d));
});
g.selectAll('circle').data(countries.featuresWithPopulation)
.enter().append('circle')
.attr('class', 'country-circle')
.attr('cx', d => d.properties.projected[0])
.attr('cy', d => d.properties.projected[1])
.attr('r', d => sizeScale(radiusValue(d)));
g.append('g')
.attr('transform', `translate(45,215)`)
.call(sizeLegend, {
sizeScale,
spacing: 45,
textOffset: 10,
numTicks: 5,
tickFormat: populationFormat
})
.append('text')
.attr('class', 'legend-title')
.text('Population')
.attr('y', -45)
.attr('x', -30);
});
import { feature } from 'topojson';
import { csv, json } from 'd3';
export const loadAndProcessData = () =>
Promise
.all([
csv('https://vizhub.com/curran/datasets/un-population-estimates-2017-medium-variant.csv'),
json('https://unpkg.com/visionscarto-world-atlas@0.0.4/world/50m.json')
])
.then(([unData, topoJSONdata]) => {
const rowById = unData.reduce((accumulator, d) => {
accumulator[d['Country code']] = d;
return accumulator;
}, {});
const countries = feature(topoJSONdata, topoJSONdata.objects.countries);
countries.features.forEach(d => {
Object.assign(d.properties, rowById[+d.id]);
});
const featuresWithPopulation = countries.features
.filter(d => d.properties['2018'])
.map(d => {
d.properties['2018'] = +d.properties['2018'].replace(/ /g, '') * 1000;
return d;
});
return {
features: countries.features,
featuresWithPopulation
};
});
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"@types/node": {
"version": "10.12.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.9.tgz",
"integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==",
"dev": true
},
"rollup": {
"version": "0.67.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-0.67.3.tgz",
"integrity": "sha512-TyNQCz97rKuVVbsKUTXfwIjV7UljWyTVd7cTMuE+aqlQ7WJslkYF5QaYGjMLR2BlQtUOO5CAxSVnpQ55iYp5jg==",
"dev": true,
"requires": {
"@types/estree": "0.0.39",
"@types/node": "*"
}
}
}
}
{
"scripts": {
"build": "rollup -c"
},
"devDependencies": {
"rollup": "latest"
}
}
export default {
input: 'index.js',
external: ['d3', 'topojson'],
output: {
file: 'bundle.js',
format: 'iife',
sourcemap: true,
globals: {
d3: 'd3',
topojson: 'topojson'
}
}
};
export const sizeLegend = (selection, props) => {
const {
sizeScale,
spacing,
textOffset,
numTicks,
tickFormat
} = props;
const ticks = sizeScale.ticks(numTicks)
.filter(d => d !== 0)
.reverse();
const groups = selection.selectAll('g').data(ticks);
const groupsEnter = groups
.enter().append('g')
.attr('class', 'tick');
groupsEnter
.merge(groups)
.attr('transform', (d, i) =>
`translate(0, ${i * spacing})`
);
groups.exit().remove();
groupsEnter.append('circle')
.merge(groups.select('circle'))
.attr('r', sizeScale);
groupsEnter.append('text')
.merge(groups.select('text'))
.text(tickFormat)
.attr('dy', '0.32em')
.attr('x', d => sizeScale(d) + textOffset);
}
body {
margin: 0px;
overflow: hidden;
}
.sphere {
fill: #f2f2f2;
}
.country {
stroke: black;
stroke-width: 0.08px;
}
.country:hover {
fill: #74e1d8;
}
.tick text {
font-size: 1.3em;
fill: #444444;
font-family: sans-serif;
}
.tick circle, .country-circle {
fill: #00d1bf;
fill-opacity: 0.489216;
stroke: black;
stroke-width: 0.3;
pointer-events: none;
}
.legend-title {
font-size: 2.2em;
fill: #444444;
font-family: sans-serif;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment