block by patricksurry e9b91eed27bb2eacd379777a050df9d2

Cartoonish map styles with D3 and NaturalEarth

Full Screen

index.html

<!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>