Building Custom Interactive Maps with Datamaps, TopoJSON, and ogr2ogr

Published by John on May 9, 2025 Under Javascript, Web Design

Recently, one of my clients requested an interactive map for their sales representatives. I’ve done this a few times using various JavaScript and JQuery map tools, and went with Datamaps, which is an older library, but still works well and I’ve used it before with good results.

Using this, I was able to quickly get a map prepared for the United States sales representatives that allowed people to click on their state to see their dedicated sales representative. However, I ran into some difficulties with my clients other maps, due to the way they wanted them to be broken up.

For example, they wanted one map to include some, but not all Asian countries, and then also Australia. And then another map that included Europe, Africa, and a couple Asian countries.

As I started searching around for a few map sets that would work with this, I found that most either showed the entire world or broke it out into separate maps for each continent, like Asia, Australia (Oceania), Europe, Africa, etc.

One option would have been to just use the world map and then zoom in on a selected location, but getting this right is sort of tricky, so I decided to see how involved it would be to make my own map and I discovered it wasn’t too difficult to generate my own TopoJSON map with only the countries I needed in it.

There are three basic steps:

1) Get some map data
2) Convert the map data to TopoJSON Format
3) Setup Datamaps to use the TopoJSON Map Data

1) Get Some Map Data

There is no need to reinvent the wheel, so lets start with some ready made map data. One source of this is Natural Earth, which provides a number of data sets. For this example, I downloaded the Admin 0 – Countries map here: www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/

This provides a Shape File called ne_10m_admin_0_countries.shp that we can use to generate our TopoJSON

2) Convert the map data to TopoJSON Format

Here we are going to use ogr2ogr and TopoJSON to generate the map data in the format needed for Datamaps.

First, we will install GDAL, which stands for the Geospatial Data Abstraction Library, which includes the ogr2ogr program.

dnf install gdal

Next, we are going to install an older version of topojson. If you are doing more advanced mapping, you will likely want to look into the newer version of topojson

npm install topojson@1

Now, we need to create a new GeoJSON file using the Natural Earth data and filtering out the countries using their three digit country codes, so that we only select the countries that we need to include.

This will create a file called custom_map.json that we will use in the next step.


ogr2ogr -f GeoJSON -where "ADM0_A3 IN ('CHN', 'IND', 'IDN', 'MYS', 'PHL', 'SGP', 'THA', 'VNM', 'JPN', 'TWN', 'KOR', 'PRK', 'AUS', 'NPL', 'BTN', 'BGD', 'LAO', 'LKA', 'KHM', 'TWN', 'PNG' )" custom_map.json ./ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp

Next, we need to convert this to a TopoJSON file:

./node_modules/topojson/bin/topojson -o custom_map_topo.json --id-property SU_A3 --properties name=NAME -- custom_map.json

This will create a new file called custom_map_topo.json and in this example, reduced the size from 4.2MB to .5MB.

We will need this file when we use Datamaps.

3) Setup Datamaps to use the TopoJSON Map Data

Now that we have the TopoJSON map, we can plug this data into Datamaps to generate our own interactive map.

You will likely want to start with the No preset topojson file. This is where you will add your map data:

/**************************************
                  TopoJSON
***************************************/

Datamap.prototype.customTopo = {"type":"Topology","objects":{"custom":{"type":"GeometryCollection","name":"ne_10m_admin_0_countries" [...]};

/**************************************
                  End TopoJSON
***************************************/

Now, when you want to initialize your map, you can do something like this:

var custom_map = new Datamap({
	scope: 'custom',
	element: document.getElementById('my_custom_map'),

	setProjection: function (element) {
		var projection = d3.geo.mercator()
		.center([0, 0])
		.scale(100);
		var path = d3.geo.path().projection(projection);
		return { path: path, projection: projection };
	}
});

Note that in the above, setProjection is used to position the map. You will need to play with the scaling and positioning here to get it looking correctly.


No Comments |

Add a Comment