The Great Escape – 3D Fantasy Map Tutorial

The COVID-19 lockdown has brought with it an abundance of online professional development opportunities – a welcome escape from the terrors caused by the novel coronavirus (or by the house arrest and social distancing regime itself, if you concur with my view ;). On April 29, cartographer Daniel P. Huffman of Madison, Wisconsin, organized “How to do Map Stuff: A Live Community Sharing Event” with virtual workshops offered by volunteers from around the world, see https://somethingaboutmaps.wordpress.com/2020/03/19/how-to-do-map-stuff/.

Along with several interesting presentations, I listened in to Minnesota-based cartographer Ross Thorn, who went through the process of “Creating an Interactive Fantasy Map” using QGIS and MapBox. The recording is now posted on Youtube at https://www.youtube.com/watch?v=2nmLibB3lGs (starts around minute 9:30). Rather than create a set of islands from scratch, Ross “floods” a digital elevation model (DEM) so that mountains or hills turn into islands while lower elevations are transformed into the open seas… The remainder of that tutorial focused on vectorizing the island boundaries and adding land-use polygons as well as settlement locations with attached information.

During the April 29 live session, a chat participant asked whether the original elevations could be preserved as terrain on the islands. In this post, I would like to show how this can be done and how the result become an interactive 3D map within the QGIS and Web environment. I am using QGIS 3.4.12 and QGIS 3.10 with the Qgis2threejs plugin. The plugin does not correctly install on my Windows 8.1 office computer, thus the second part of this post is completed using the newer Windows and QGIS versions on my laptop. Also, this is certainly not the first time someone has created a 3D fantasy map, nor the only way in which it can be done; it just happens to be the first time I was motivated to try this myself using one of the GIS tools I am familiar with.

To begin the process outlined in Ross Thorn’s tutorial, I downloaded the southern portion of Ontario’s provincial DEM from https://geohub.lio.gov.on.ca/datasets/mnrf::provincial-digital-elevation-model-pdem. Attention: This file is almost 2 GB large! You can likely find smaller DEMs from your local or regional government that can be used equally well, or work with the data that Ross used.

The above screenshots show the PDEM displayed in QGIS using the default black-to-white gradient for increasing elevations. The Niagara Escarpment is visible in the south adjacent to the Greater Toronto Area, while the remaining high altitudes are all part of the Canadian Shield. I zoomed in to an area west of Lake Temiskaming near the eastern border of Ontario with Quebec.

A further zoom yielded an area with several distinct peaks above 500m in elevation. Also shown above is the symbology dialog to distinguish pixels by elevation above and below 500m and the resulting “islands”. Using the menu sequence Raster | Extraction | Clip Raster by Extent | Use Canvas Extent | Save to File “PDEM_Escape.tif”, I clipped the province-wide DEM to the area shown. I renamed the new layer and used copy & paste to apply the same blue-red style from PDEM_South.

Under layer properties, you can view a histogram of raster grid values. The above screenshot is zoomed in to values above 500m to get a sense of their frequency distribution; I didn’t know what to expect, but the fast decline of higher elevation certainly makes sense.

We now use Raster | Raster Calculator for what is the key “contribution” of this post. In his tutorial, Ross used the following formula to set cells with elevation below the threshold (here: 500m) to zero (= water) and cells above threshold to one (= land):

(("PDEM_South@1" <= 500) * 0) + (("PDEM_South@1" > 500) * 1)

Here, we do not want to condense the higher elevations to a categorical value representing land, but keep them as land elevations above the new “sea level” of 0. We achieve this by multiplying the values selected in the second term of the sum by the original elevation value, from which we subtract the threshold. Thus, while values up to 500m will be converted to 0m, the value of 501m will be converted to 1m (= 501m – 500m) and so on:

(("PDEM_South@1" <= 500) * 0) + (("PDEM_South@1" > 500) * ("PDEM_South@1" - 500))

After completing the remainder of the tutorial, I realized that the configuration of my area’s elevations did not yield sufficient terrain variation. I went back to the current step to introduce a vertical exaggeration factor of 5. Therefore, the final raster calculation is as follows:

(("PDEM_South@1" <= 500) * 0) + (("PDEM_South@1" > 500) * ("PDEM_South@1" - 500) * 5)

The following screenshot shows a colour scheme I created for the symbology property of the new LDEM_Escape5 layer, to allude to the different land cover classes that may be associated with increasing elevations, i.e. water, coastal sand, grasslands, forests, and bare rock. Another important change I made here is under Project | Properties | CRS tab, where you want to find and set “WGS 84 / Pseudo-Mercator” (EPSG:3857) to set the map units to metres instead of degrees (if that’s what it was before).

For the 3D version of our fantasy world, we will primarily use the raster dataset. However, we will also complete the instructions from Ross Thorn’s tutorial to create a vector dataset with the coastlines of our islands. The command sequence Raster | Conversion | Polygonize (Raster to Vector) turns the grid cells of the raster layer into square polygons. Unfortunately, cells with different elevation values on the islands will result in separate polygons, as shown in the first of the following screenshots (I clicked to select and highlight the large water polygon as well as a few small in-land squares for illustration). Ross did not have this issue, since all of his land pixels had the same value of 1 and formed contiguous polygons for each island. We will achieve this with the command sequence Vector | Geoprocessing Tools | Dissolve. This generates the situation shown in the second screenshot, with all land pixels combined and the water polygon removed. However, different islands are now all combined into one, multi-part polygon (I clicked to select only one island, which results in the highlighting of all land areas, since they are part of this multi-part feature). We now use Vector | Geometry Tools | Multipart to Singleparts in order to separate the land mass into independent entities, as shown in the third screenshot where a small island in the northwest is highlighted without also selecting the remaining islands. After adapting the symbology and project background colour, and adding a text field with a few island names for labelling, we achieve the status shown in the fourth screenshot below.

Our last steps in QGIS include making the island polygon fill transparent and give its borders a sand colour. For island labelling, I selected the three islands that actually had a name assigned and used right-click on the layer name | Export | Save Selected Features to create a new Shapefile layer with only these three islands; otherwise, there would be a lot of NULL labels down the road (not in QGIS but in the 3D export). I also created two new point layers according to Ross’ video: one for named cities and another for a mountain peak. Labels are activated under each layer’s properties and I am using “buffers” in the label options to make the label text better visible. The end result is shown in the following screenshot.

At this point we are starting to work with the Qgis2threejs plugin, which will convert our 2D map into an interactive 3D scene that is exportable to the Web. As noted above, I had to migrate the project to my Windows 10 laptop using QGIS 3.10. To find the Qgis2threejs plugin, go to Plugins | Manage and Install Plugins and let the system fetch the latest plugins. In the search field, type “threejs” to find the plugin and install it. If all goes well, you should have a new item in the “Web” menu and be able to run the “Exporter” from there. The tool opens with all project layers available for display and customization.

Check to activate the (vertically exaggerated) island terrain layer (LDEM_Escape5) and right-click for properties. I enabled “Surrounding blocks” to extend the seas beyond the map canvas and deactivated “Build sides” so that the water level is a plane rather than a block. The default material setting to display “Map canvas image” drapes the 2D map from QGIS over the elevation model. While this is what we want for the topographic colour scheme, it does not work for the points of interest and labels, which we’d rather turn into 3D objects. The QGIS window underneath the plugin window is still actionable and we thus uncheck the visibility of the places, mountain, and island names layers (see background/left side of the following screenshot).

So for example, we see the places layer unchecked in the QGIS project/map canvas but checked in the Qgis2threejs Explorer. In the corresponding 3D layer properties, we set the object for cities to a box sized 400m x 400m x 100m (assuming you set a coordinate reference system with metres as the map unit, as outlined above). Under “Features”, I should have checked “All features” to ensure that points that aren’t included in the current QGIS map extent are still exported. Lastly, activating “Export attributes” creates the 3D “airborne” labels at a set height that you can see in the scene. All this is done for the cities, island labels (only names are visible, coastlines of this layer remain invisible but come from the other island polygon layer), and the mountain peak (also no object symbol, just the name/label).

Under File | Export Settings, you have the option to save the Qgsi2threejs settings if you need to close your session and load them to restore it.

I did not play with the other Explorer settings but proceeded straight to File | Export to Web, using a temporary directory, the “3D Viewer” template, and “Enable the Viewer to Run Locally”. The template “3D Viewer with dat-gui panel” may be of interest in other applications where controlling layer visibility and opacity is useful (try it!). The above screenshot of the 3D Web map shows our fantasy world from the northwest. A live version can be accessed in my Github repository at https://crinner.github.io/escape5/.

Future work should include checking out the HTML and Javascript code that was generated. Another participant in Ross Thorn’s tutorial asked about putting units in the fantasy world, and while I was not sure whether they were referring to map units or game avatars, it would certainly be neat to add animated objects (or subjects!?) to the three.js scene! A quick Web search suggests that this is perfectly possible. I can think of other customizations of the geospatial objects, their appearance, or the functionality of the 3D viz, some of which are inspired by student work posted at https://spatial.blog.ryerson.ca/, e.g. flight simulation or vintage map design.