Inkscape and D3.js for Map Makers
Creating fictional maps is a lengthy, sometimes tedious process. But you do end up with a nice and usefult piece of work. Then you wonder, how would this look in a sphere?
I started this journey when making a world atlas for Lauz, an ocean planet set in a sci-fi world. My goal was to be able to visualize the entire planet in different projections, the azimuthal-equidistant for the poles, and the cylindrical for the regions near the equator. I created three maps, using the projection best suited for each region.
Three maps for a single planet. nice, right? But then I asked myself, how would this actually look on a sphere? What if I want to show a small region of the map? Would I need to draw it all over again? These questions led me to find a solution to a personal problem of converting maps to data, and data to map, a problem that only automation could solve.
I wanted several things from my map:
- Vector based. Obviously, I wanted it to be scalable and easily modifiable.
- Automated projections. I should be able to transform a single, master map into any other projection, and be vector based too. No manual drawing in the middle.
- Multiple features. Similar features (land, locations, biomes, etc.) should be displayed as layers that I can show or hide depending on the purpose of my map.
I use Inkscape for all my vector-based work so it had to be part of the equation. I began by drawing three maps:
- The north and south poles, with the azimuthal equidistant projection centered at 90°N, with a radius of 40°.
- The tropical region, with the mercator projection centered on 0° from 50°S to 50°N.
For these maps, the colors didn’t matter much, as everything would be redrawn later.
Making the maps was the easy part. Now I need a way to transform them.
During my research I came across with D3.js, a great library used for displaying and manipulating documents and graphics on the web. It’s used to show interactive charts, animations, and of course, maps on the web. Just what I needed.
D3.js has a nice module made for displaying maps using multiple projections with svg graphics. That was good to hear, most of the work and math was already done by somebody else. Its great deal of features allowed to get more ambitious with the things I wanted for my map. Projections, zooming, rotating, statistics, it’s all there.
But D3.js was intented for real world maps, using real world data. the module uses GeoJSON, which stores geographic data (areas, lines, points) using JSON encoding. So it can easily encode data for countries, islands, borders, roads, cities and much more. However getting GeoJSON data of the real world is very easy.
At this point I had a clear Idea of the workflow I envisioned for my map
- Draw the map using Inkscape.
- Write a script to transform to a different projection using D3.
- If I need to fix, revision or add something, I just need to modify the Inkscape map and run the script again.
The was just one missing link. Converting my Inkscape map to GeoJSON data.
At first I tried to manually create the data but then I realized how slow and tedious it would be, besides, my main goal was to automate the map-making process. Some coding needed to be done. This way, whenever I change a feature of my map, the script will automatically generate the geographic data and update the map with D3.
GeoJSON takes in coordinates, or sets of coordinates that represent the vertices of a line or polygon.
For polygons, clockwise paths represent, for example, land, and anticlockwise paths can be used to create holes in these shapes (for example, lakes).
Since Inkscape is vector-based, and vectors are based on nodes. The script had to take each node, convert its x and y coordinates to latitude and longitude according to a given projection, and create a GeoJSON object of such feature.
Inkscape has a Python module made to easily read and manipulate svg documents. Going through the documentation was probably the hardest thing about writing this script. The documentation was scarce and obscure. This extension that extracts the x, y positions of svg objects was very helpful for understanding how to used the inkex module.
I made a GitHub repository containing the script that can be run in the command line. An extension is also available to export it directly from Inkscape. How the script works is explained further in the repo.
I added support for the following projections:
- Azimuthal Equidistant
Now with GeoJSON data of the same map I drew it was easy to make d3.js display the same map in any projection, without drawing anything. Yet again I had to dig and read through d3.js’s documentation which was much better than Inkscape’s. There are many examples on the web on how to display real world data, which can be easily modified to display the data generated with the extension.
Below are some examples of Lauz’s geographic proyected using the Azimuthal, Mercator and Orthographic projections.