Skip to content

Instantly share code, notes, and snippets.

@TWIAV
Last active May 14, 2022 18:30
Show Gist options
  • Save TWIAV/94be9cb4ddf3a2e65602 to your computer and use it in GitHub Desktop.
Save TWIAV/94be9cb4ddf3a2e65602 to your computer and use it in GitHub Desktop.
D3/TopoJSON: map wit SVG Text labels

My very first exercise with TopoJSON and D3 - just a municipal map showing some basic information when you hover over the individual municipalities. Nothing really fancy, but, hey, you have got to start somewhere...

###Steps followed to create the file nlgemeenten2009.json:

Step 1. download input file gem_2009_gn3.shp from the Dutch Statistical Office: Wijk- en Buurtkaart 2009

Step 2. convert shapefile to GeoJSON with ogr2ogr:

    ogr2ogr \
        -f GeoJSON \
        -t_srs EPSG:4326 \
        gemeenten.json \
        gem_2009_gn3.shp

In this step we convert the projection from the Netherlands National System (EPSG:28992) to WGS84 (EPSG:4326)

The output file is 2.6 MB in size.

Step 3. change the encoding of the GeoJSON file to UTF-8:

I did do this in a manual step by opening the file gemeenten.json in Notepad++ and modify the encoding via Encoding > Convert to UTF-8 and save the file.

Step 4. convert (and compress) the GeoJSON file with topojson:

    topojson \
        -o nlgemeenten2009.json \
        --id-property GM_CODE \
        --properties gemeente=GM_NAAM \
        --properties inwoners=AANT_INW \
        gemeenten.json

The resulting file nlgemeenten2009.json is much smaller, at only 237 KB.

Why do I use a relatively outdated dataset for this exercise, 2009 is almost a decade ago? The main reason for this is: file size! Until 2009 the Dutch Statistical Office (CBS) provided the data with generalized geometry (i.e. simplified municipal borders). This reults in relatively small GeoJSON (2.6 MB) en TopoJSON (237 KB) files. The resulting TopoJSON file is managable in a web map application. And the level of detail is perfect for the display of statistical data at national level.

Nowadays the Statistical Office offers datasets with the full boundary geometry. And they even added - as an additional service - highly detailed boundaries between water bodies and land areas within municipalites.

The same exercise with the official 2014 dataset resulted in a whopping 54 MB (!!!) GeoJSON file. And after compression the TopoJSON file was also relatively large, at 2 MB. That's why I decided to rewrite the exercise with an older, more compact dataset.

<html>
<head>
<title>TWIAV - Research &amp; Consultancy</title>
<link rel="SHORTCUT ICON" href="http://twiav.nl/img/twiav.ico"/>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<style>
body {
font-family: Arial, Helvetica, sans-serif;
}
h1 {
font-size: 16px;
font-weight: bold;
}
h2 {
font-size: 14px;
}
p {
font-size: 12px;
}
#title {
position:absolute;
top: 10px;
left: 10px;
}
.land {
fill: #85BB65;
stroke: #9B65BA;
}
.selected {
fill: #FF0000;
}
.info {
font-size: 14px;
font-weight: bold;
}
</style>
</head>
<body>
<div id="info">
<h1>Municipalities in The Netherlands</h1>
<h2>Status: January 1st, 2009</h2>
<p>(hover for name and number of inhabitants)</p>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var nl_NL = {
"decimal": ",",
"thousands": ".",
"grouping": [3],
"currency": ["€", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%-d-%-m-%Y",
"time": "%-H:%M:%S",
"periods": ["vm", "nm"],
"days": ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"],
"shortDays": ["zo", "ma", "di", "wo", "do", "vr", "za"],
"months": ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"],
"shortMonths": ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"]
};
var NL = d3.locale(nl_NL);
var thsd = NL.numberFormat("n");
var width = 1100,
height = 900;
var projection = d3.geo.mercator()
.scale(10700)
.translate([width / 2, height / 2])
.center([5.4, 52.2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("nlgemeenten2009.json", function(error, nlgemeenten2009) {
if (error) return console.error(error);
console.log(nlgemeenten2009);
var gemeenten = topojson.feature(nlgemeenten2009, nlgemeenten2009.objects.gemeenten);
svg.append("g")
.attr("class", "land")
.selectAll("path")
.data(gemeenten.features)
.enter().append("path")
.attr("d", path)
.attr("title", function(d) { return d.properties.gemeente; })
.on("mouseover", function(d) {
var xPosition = d3.mouse(this)[0];
var yPosition = d3.mouse(this)[1] - 30;
svg.append("text")
.attr("class", "info")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.text(d.properties.gemeente + " (" + thsd(d.properties.inwoners) + " inwoners)");
d3.select(this)
.attr("class", "selected");
})
.on("mouseout", function(d) {
d3.select("#tooltip").remove();
d3.select(this)
.transition()
.attr("class", "land")
.duration(250)
});
});
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
<html>
Display the source blob
Display the rendered blob
Raw
Loading
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