Skip to content

Instantly share code, notes, and snippets.

@patricksurry
Last active April 4, 2022 17: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 patricksurry/e9b91eed27bb2eacd379777a050df9d2 to your computer and use it in GitHub Desktop.
Save patricksurry/e9b91eed27bb2eacd379777a050df9d2 to your computer and use it in GitHub Desktop.
Cartoonish map styles with D3 and NaturalEarth
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Architects Daughter', cursive;
font-size: 4pt;
margin: 0;
}
svg {
border: 1px solid #666;
}
.country {
stroke-width: 8;
stroke-linejoin: round;
fill-opacity: 0.33;
}
.city path{
fill: #33;
}
.country text {
font-size: 120%;
}
text {
fill: #666;
fill-opacity: 1;
stroke: none;
text-anchor: middle;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
var width = 1440,
height = 800;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("filter")
.attr("id", "blur")
.append("feGaussianBlur")
.attr("in", "SourceGraphic")
.attr("stdDeviation", 4);
var defs = svg.append("defs");
Promise.all([
d3.json("https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_countries.geojson"),
d3.json("https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_populated_places_simple.geojson"),
]).then(
function([countrygeo, citygeo]) {
let
cities = citygeo.features.filter(d =>
d.megacity || d.properties.scalerank <= 2
),
a3s = cities.map(d => d.properties.adm0_a3),
countries = countrygeo.features //.filter(d => a3s.includes(d.properties.adm0_a3));
let bounds = {
type: "MultiPoint",
coordinates: [[180, 0], [0, 65], [0, -55], [-160, 0]]
}
var projection = d3.geoEqualEarth()
.fitSize([width, height], {type: 'FeatureCollection', features: countries})
.center([10,0])
//.center([0, 5])
//.scale(150)
//.rotate([-180,0]);
var path = d3.geoPath()
.projection(projection);
defs.selectAll("path")
.data(countries)
.enter().append("path")
.attr("id", d => d.properties.adm0_a3)
.attr("d", path)
defs.selectAll("clipPath")
.data(countries)
.enter().append("clipPath")
.attr("id", d => "inside-" + d.properties.adm0_a3)
.append("use")
.attr("xlink:href", d => "#" + d.properties.adm0_a3)
let shapes = svg.append('g')
.attr("class", "countries")
.selectAll('.country')
.data(countries)
.enter().append('g')
.attr("class", "country");
shapes
.append("use")
.attr("xlink:href", d => "#" + d.properties.adm0_a3)
.attr("clip-path", d => `url(#inside-${d.properties.adm0_a3})`)
.attr("filter", "url(#blur)")
.style("stroke", d => d3.schemeTableau10[d.properties.mapcolor7])
.style("fill", d => d3.schemeTableau10[d.properties.mapcolor7]);
/*
shapes
.append("text")
.attr("transform", d => {
let [x, y] = path.centroid(d.geometry);
return `translate(${x}, ${y})`;
})
.text(d => d.properties.name);
*/
let dots = svg.append('g')
.attr("class", "cities")
.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city")
.attr('transform', d => {
let [x, y] = projection(d.geometry.coordinates);
return `translate(${x}, ${y})`;
})
.datum(d => d.properties);
dots.append('path')
.attr("d", d => d3.symbol(d.adm0cap ? d3.symbolStar: d3.symbolCircle, 2*(3 - Math.sqrt(d.scalerank)))());
dots.append('text')
.attr('dy', -2)
.attr('font-size', d => (100-(1-d.adm0cap)*d.scalerank*10) + '%')
.text(d => d.name);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment