How to create a 3D web map of contour lines

Raluca Nicola
5 min readJul 30, 2019

--

Zurich’s hills — see the live version

Some weeks ago I worked on a 3D map of contour lines and water bodies. It turned out pretty nice, so I decided to write a tutorial on how I built it. Before we dive into the tutorial you can view a live version on Glitch and you can explore the code here.

So without further ado, let’s get started!

Part 1: Prepare the data

The data sources for this map are contour lines generated from SRTM data, water bodies from OpenStreetMap and hills as points from swissNAMES3D, a dataset published by Swisstopo, the Swiss Federal Office of Topography. The first two sources cover areas all around the world, while the third is specific to Switzerland, so you will need to find other sources for your area of interest.

Contour lines

Let’s navigate to the OpenDEM website, where they have contour lines for the whole world generated from SRTM data. Navigate to your region of interest and download the tile that includes it.

Load the data in ArcGIS Pro and reproject it to a projected coordinate system (for Switzerland I used LV95). Then clip the contour lines to a smaller area containing only your region of interest. Generally a region that is too big might not be very performant. For Zurich I decided to clip them based on a circle polygon I created in another layer. You could also clip them to some administrative border or some other shape. It’s important to keep the elevation field, because we will use it to align the features to the ground. In the end, the contour lines look like this:

Water bodies

Next we’ll download the OpenStreetMap data (I downloaded the whole dataset for Switzerland from Geofabrik). Let’s load the polygon water bodies in ArcGIS Pro. First we’ll reproject it to LV95 and then we’ll clip it to the circle polygon shape. Then we start filtering the main water bodies. We want to include only the features with fclass set to water. From the rivers I only kept the Limmat, which is the largest one. Then I selected the main water bodies by calculating the area in an attribute using Calculate Geometry and selecting all the features with an area bigger than a certain threshold. Some features appeared twice in the dataset, so I removed the duplicates by applying the Delete Identical tool with osm_id field. After this strict selection I only had 9 features in my dataset:

Hills

The original swissNAMES3D data can be found here. I downloaded and clipped the data to the circle shape. Then I started filtering the points: I only kept the hills OBJEKTART = Huegeland I manually removed the points that are not dominant hills, like the selected ones in this image:

The points in this dataset have z values which we will use for elevation alignment. I removed most of the fields that weren’t needed, but kept the NAME field to use it for labeling. After the selection, the dataset contains 45 hill peaks:

After these preprocessing steps, we can now publish the three datasets as feature layers to ArcGIS Online. Here are the items after publishing:

Part 2: Visualization

Let’s move into JavaScript land to create a web visualization with the data. First of all, we create a WebScene and we display it in a SceneView:

Notice that the WebScene has a transparent ground and the SceneView has a transparent background. This will provide a nice effect once we apply the CSS shadow to the canvas element where the webscene is rendered. Add this filter effect in your css file:

Next, we start adding the data and applying renderers. The main thing about this visualization is that it uses 2 colors: a light brown for the background rgb(244,239,227) and a dark brown rgb(86,72,31)for the features and the text. All the other colors are obtained by blending these two.

To create a smooth transition from lower areas to higher ones, we continuously change the opacity from lower contour lines to the higher ones. With ArcGIS API for JavaScript, this can be done using the opacity visual variable:

Notice the elevationInfo using an Arcade expression that exaggerates the actual height of the contour lines.

Next we’ll add the water polygon and as a symbol we will use an extruded polygon to enhance the 3D effect:

Then we render the hills with a PointSymbol with an IconSymbol3DLayer. The important part here is labeling these points. When visualizing labels in 3D, we often use callouts to avoid occlusion and to clearly show where the label is pointing at. Also notice again an added elevation info that exaggerates z-values:

This map looks nice, but it would be much nicer to guide the user to the most famous hills around Zurich. To do that we’ll add a panel on the right side that contains some more information about the area. We will also place buttons that users can click to navigate easier to the main viewpoints. For each viewpoint we store a camera position that we can print from the app by running this code in the browser console:

Now we can bind those camera positions to the buttons in the panel to guide the user to the areas of interest:

Last, but not least, I always make sure to add credits for the data I used.

And those were the main steps for creating this web app. If you follow this tutorial feel free to improvise, combine it with other data, go wild with the colors… and share it in the comments, I’d love to see what you made!

Have fun mapping and coding!

Raluca

--

--

Raluca Nicola

Web cartographer exploring the third dimension @ArcGISJSAPI