Map visualization in D3.js with GeoJSON/TopoJSON cartography

Anirudh K Mahant
Geek Culture
Published in
5 min readAug 24, 2021

Prelude

Visualization is a term coined around “the representation of an object, situation, or set of information as a chart or other image”. In it’s true glory, a visualization can be just makeup, less than it’s visual beauty; more about underlying data.

Visualizations provide wealth of visual aid in information architecture, accounting, analytical data, touchscreen displays, even your smart watches.

Map visualizations

Imagine sketching a Mercator projection on a piece of paper in 15th century, where the remote concept of free spirit was a far-cry, let alone the tools of trade in that era.

Mercator 1569 world map (Nova et Aucta Orbis Terrae Descriptio ad Usum Navigantium Emendate Accommodata) showing latitudes 66°S to 80°N. Attribution: Wikipedia.org
Mercator 1569 world map (Nova et Aucta Orbis Terrae Descriptio ad Usum Navigantium Emendate Accommodata) showing latitudes 66°S to 80°N. Attribution: Wikipedia.org

Map visualizations have evolved ever since, likewise we’re here to talk of D3.js (Data driven documents), GeoJSON and TopoJSON. Sitting at the top D3.js is a library to visualize data in GeoJSON/TopoJSON through SVG, HTML5 and CSS.

D3.js in a nutshell — but you already know?

D3.js Library
D3.js Library

D3.js can create complex, 2D interactive and animated visualizations for Tree-maps, Heat-maps, Plots, Charts, and geographic Maps, the possibilities are limitless. Under the hood D3.js uses SVG or Canvas object to draw visualization, playing graciously along side your data.

When it comes to Maps, D3.js ships with predefined map projections for USA, also referred to as d3.geo.albersUsa() which begs the question, why projections? Here’s why:

In cartography, a map projection is a way to flatten a globe’s surface into a plane in order to make a map. This requires a systematic transformation of the latitudes and longitudes of locations from the surface of the globe into locations on a plane. Attribution: Wikipedia.org

In layman’s term “Projections can outline a geographic topology into it’s flattened (plane) version”. Enough geeking out, let’s take a practical example from D3.js to draw a map of US counties:

Example of US Counties

With the fluent syntactic sugar, it can be combined in one liner. Now D3.js can draw SVG paths for all US counties using the GeoJSON features collection.

D3.js teams up rather well with TopoJSON to simplify Map visualizations. All the data magic happens by calling topojson.feature(us, us.objects.counties).features() function which returns US counties as GoeJSON FeatureCollection. With one change you can draw US states instead, the only required change is topojson.feature(us, us.objects.states).features()because the TopoJSON file has both US counties and states defined as geometry collection.

What’s GeoJSON and TopoJSON?

GeoJSON is an open standards format to store and interchange geographic data in JSON format, along with non-spatial attributes. The format gained traction with advent of GIS (Geographic Information System) systems and devices. Due to its high precision a GeoJSON format is not streamlined for web or bandwidth friendly.

Working as an extension on top of GeoJSON, TopoJSON format compensates these cons by using several algorithms to eliminate redundant and duplicate data. Concisely, simplifying paths as arcs and combining boundaries between topologies that might produce two-folds.

TopoJSON comes bundled with a set of useful CLI tools to make it easier. You can install them using NPM package manager. Some notable CLI tools:

They come in very handy for advanced CLI cartography, which is the aim. If it felt like cakewalk by now, the same cannot be said when manipulating maps, like choropleth maps, 3D maps, map interactions, or, combining/merging topologies.

Anatomy of GeoJSON/TopoJSON

Addressing the theme around this article, let’s understand how GeoJSON/TopoJSON store geographic topologies. GeoJSON uses FeatureCollection and Feature objects, to test it let’s take a Feature object for Delaware (notice no arcs):

Look closely at co-ordinates object, they’re longitudes and latitudes (reverse them in Google Maps) defining the boundary of Delaware. Now let’s see the same map object in TopoJSON format (click view raw):

Pay attention to bbox or bounding box and arcs object, also there are no geometry objects. That’s how TopoJSON overcomes the exhausting payload size of GeoJSON, by encoding GeoJSON feature collection into arcs, optionally quantize and simplify geometry, resulting in much simpler and smaller format with somewhat precision trade-off.

Advanced cartography with CLI

Merging US states into regions — starting with northwest, midwest, southwest, southeast and northeast. Several ideas pumped in from Observable HQ out of which two of them really stood out. Explore more on Observable HQ, there’s abundant information on D3.js with matching examples.

There are two methods to do it, one is using topomerge CLI, and the other is using topojson.merge() in Browser. I went with the Browser approach first, quick and dirty, but proves effective!

In case you missed the id: 10 in above formats, then it stands for Delaware FIPS (Federal Information Processing Standards) code. Each US state has a unique FIPS code embedded in GeoJSON/TopoJSON, and relevant postal code, both central in merging and filtering. Beginning with a lookup (FIPS code as key and postal code as value):

Example of US States merged using topojson.merge()

It works flawlessly, yay! But what just happened? The function topojson.merge() manages to merge and filter states based on their FIPS code, if a region (datum <- data(regions)) contains one of the matching postal codes in lookup; it then returns a geometry collection object of multi-polygons or merged states:

Feature collection of merged geometries
Feature collection of merged geometries

Peek into browser console, and it gives you a Feature collection of merged geometries. Now hop over to GeoJSON.io and start adding each feature object from console to the feature collection array. You’ll see live changes as you add:

Live preview GeoJSON.io
Live preview GeoJSON.io

From there on, you can practically save it in various formats like TopoJSON, CSV, KML or Shapefile. Now it’s moderately clear how to do it similarly using topomerge CLI, a task better suited with shell script:

Doing essentially the same thing, the script should generate a valid TopoJSON file which can then be used directly with D3.js or DataMaps for added interactions. NOTE: This script is not tried or tested.

Why not do it straight away in CLI?

There are no guarantees it will succeed in one go. Besides this was more of a web based solution, and breaking down challenges into more manageable undertaking. Sometimes, even a hint of success gets you motivated enough to ascend.

Strikingly, I had to run toposimplify after generating TopoJSON file from GeoJSON.io to make it work. There are few honorable mentions:

--

--

Anirudh K Mahant
Geek Culture

Full-stack Developer — Globetrotter across platforms.