Plotting Regional Topographic Maps from Scratch in Python | by Fouad Faraj | Towards Data Science

Topographic maps represent Earth’s 3D landscape as a 2D map and are used to visualize both anthropogenic and natural features in their geographical context. The specific applications for topographic maps vary depending on the scale of the map. Some uses include planning transportation routes, guiding travelers, and delineating the location and extent of specific areas. Topographic maps also serve as excellent basemaps for plotting other geospatial datasets.
Due to the lack of extensive programming experience, most earth scientists and geographers rely on geographic information systems (GIS) to generate topographic maps. Unfortunately, most GIS software requires expensive commercial licenses and abstracts several mapping features, limiting the changes a user can make to a map. Plotting maps can also be done using any standard programming languages such as Python, R, or Matlab which offer added flexibility and fewer abstractions in map modifications.
Presented here is a simple methodology for generating topographic maps in python followed by an application in the oil & gas industry to highlight the use of elevation data. Various disciplines related to Earth Sciences or the natural resource industries often use elevation data for both generating topographic maps and cross-sections which visualize a linear profile of surface elevation.
While the example consists of plotting elevation data, the processing and visualization techniques discussed can be used to map similar geospatial datasets such as meteorological data consisting of surface temperatures, precipitation, or wind speed.
The methodology for generating a regional topographic map can be broken down into three general steps: (1) Retrieving the data, (2) Data processing, and (3) Data visualization. The flowchart below illustrates the approach for creating the map with the only input being a region of interest defined by a pair of latitude and longitude coordinates.
In this example, we are going to generate a topographic map for the country of Madagascar. Our rectangular region of interest surrounding the island will be defined by 10°S, 40°E in the northwest corner and 28°S, 52°E in the southeast corner.
First, we need to retrieve the elevation data for our region of interest. We will use this link by [1] where the elevation dataset was generated using satellite altimetry and ship depth soundings at a resolution of 1 arcminute, or one data point for every 1/60 degrees. We simply have to enter the pair of latitude and longitude coordinates for our region, download the elevation data, and save it to a text file which we will call Madagascar_Elevation.txt for this example.
Smaller regions will require higher resolution datasets to see finer details in the topographic map but since we are working at the relatively larger scale of an entire country, the 1 arcminute resolution data offered by [1] is more than enough.
Now that we’ve downloaded the data we can import it into Python and take an initial look at how it is formatted.
import numpy as npdata = np.loadtxt('Madagascar_Elevation.txt'); #Load datadata #Display a few rows of the dataset
Running the code above gives us the following output.
The three columns in our data show longitude, latitude, and elevation for all 828,429 points. As discussed in [1], the elevation data is in meters and measured relative to sea level so we expect negative values as this would be the depth of the Indian Ocean surrounding Madagascar at those coordinates.
Next, we have to create a grid of points spread uniformly across the site of interest and interpolate the elevation values for each point based on the downloaded data. We interpolate the data on a grid so that we can control how many points we are plotting. Based on the size of the figure or map we want to plot, a reasonable number of points will need to be set to cover the entire area.
Interpolating data onto a grid is quite easy to do in Python, requiring only a couple of libraries as shown below.
import numpy as npfrom scipy.interpolate import griddata
Now we can load our data and define how many points of latitude, longitude, and elevation we want for our grid. As mentioned before, a reasonable number of points should be used and this depends on the size of the map. Higher point counts are more computationally demanding but allow for larger, higher-quality figures to be drafted.
Generally, around 100K points are more than enough for the default-sized matplotlib figures of 6 by 4 inches when using a marker size equal to one. However, in this example, we will use 1M points so that we can create larger, higher quality maps.
data = np.loadtxt('Madagascar_Elevation.txt'); #Load dataLong = data[:,0]; Lat = data[:,1]; Elev = data[:,2]; #Variablespts=1000000; #Input the desired number of points here
Now we can create the grid based on the specified amount of points using meshgrid, and we can use griddata to interpolate the elevation values.
[x,y]=np.meshgrid(np.linspace(np.min(Long),np.max(Long),np.sqrt(pts)),np.linspace(np.min(Lat),np.max(Lat),np.sqrt(pts)));z = griddata((Long, Lat), Elev, (x, y), method='linear');x = np.matrix.flatten(x); #Gridded longitudey = np.matrix.flatten(y); #Gridded latitudez = np.matrix.flatten(z); #Gridded elevation
Now that we have our x, y, and z values which correspond to the gridded longitude, latitude, and elevation data respectively, we can get started with the mapping.
We can start visualizing our gridded data by simply plotting the latitude and longitude coordinates as the axes of our plot and use color to show the elevation values. A preliminary map can be plotted using the code below. We will later style this plot into a finalized topographic map.
import matplotlib.pyplot as pltplt.scatter(x,y,1,z)plt.colorbar(label='Elevation above sea level [m]')plt.xlabel('Longitude [°]')plt.ylabel('Latitude [°]')
Note that the axes are not to scale here and matplotlib’s default colormap is used which is not the best for visualizing elevation data to generate a topographic map.
We know everything is working since we were able to generate the plot but there are still several things we need to do to style and finalize the map such as scaling the axes and using a more appropriate colormap for the terrain. Plotting parameters such as the min and max elevation range may need to be modified depending on which region is being plotted.
We can improve and finalize the map by: (1) Setting the axes scales equal using plt.gca().set_aspect('equal'), (2) Plotting the data using a more representative colormap for terrain by replacing the old scatter function with scatter(x,y,1,z,cmap='terrain'), and (3) Manipulating the colormap, and range of elevation values to better visualize the topography in the region.
The exact code used to generate the final topographic map below is long as the map was resized, formatted, and styled to be more presentable. The complete script can be seen in this GitHub link.
The topographic map above could be used as a standalone map showing the elevation data in the region or it could be used as a basemap for plotting additional geospatial datasets in their geographic context. Furthermore, the elevation data can be extracted from a straight line across the map in any orientation to produce a cross-sectional view of the surface elevation throughout the linear profile.
Since the Earth resembles a sphere, representing its 3D features on a 2D surface will involve some form of distortion. This effect will only be noticed on larger regional maps as the difference is not significant in smaller maps that only cover cities or local areas. A one degree difference in latitude is always 111 km. A one degree difference in longitude is only 111 km at the equator and decreases to 0 as you get closer to either pole.
Thus, when plotting regional maps closer to the poles, making the axes equal in a cylindrical projection, as shown in this example, would not be representative of the actual region.
We can work around this issue using standard mapping libraries that allow us to easily plot maps with adjusted coordinate systems for different projections. These should be pursued when: (1) A simple basemap is required rather than a full topographical map with elevation data, or (2) The region of interest is far enough away from the equator where the plotted map is no longer representative.
If you’re interested in plotting a region far from the equator or just require a simple basemap, refer to the Matplotlib Basemap Toolkit Documentation for plotting basemaps in Python. Matplotlib’s basemap toolkit is well documented with various examples and sample codes for using different projections to appropriately handle the distortion effect based on the region of interest.
In a joint academia-industry collaborative project between Queen’s University (Kingston, Ontario) and Halliburton Landmark, topographical maps and the underlying elevation data were used in part for a hydrocarbon exploration task. The hydrocarbon maturity was assessed for the offshore Morondava Basin located along the Western Madagascan Margin and the elevation data was used to draft a basemap and cross-section of the basin [2].
The topographical basemap was used to delineate the spatial extent and geographic location of different data such the Morondava Basin boundaries, drilled well coordinates, cross-section profiles, geological features such as the Davie Ridge, and the license block boundaries as shown below.
Plotting the different data on the map provided various insights such as:
The drilled wells are either on land or in shallow water proving the offshore portion of the basin to be a frontier for hydrocarbon exploration
The license blocks within the Morondava Basin extends from the shallow offshore in the North East to the deep offshore in the South West
Additionally, the Neftex cross-section from [2] in the map shown above was used to generate a profile throughout the basin and license block boundaries. The elevation data allowed the authors of [2] to map out the first line above the Cenozoic layer, the complete cross-section is shown below.
The elevation data was used in conjunction with seismic data to create the above cross-section to show the hydrocarbon maturity results of the basin modeling. The authors from [2] were able to draw conclusions such as:
Hydrocarbon maturity in the basin decreases further offshore
The license blocks in the shallow offshore are more likely gas prone while those in the deeper offshore are more likely oil prone
Operations in the deep offshore license blocks would require infrastructure that can handle the additional ~1 km of water depth compared to the shallow offshore license blocks
Regional topographic maps are incorporated into most applications within the Earth Sciences and natural resource industries for a variety of different uses such as visualizing anthropogenic and natural features, mapping elevation distributions for surveying, or as basemaps for plotting additional geospatial data. Maps are used extensively, not only in academia or industry but also for practical purposes such as navigation aid for the common person.
Although the application shown here was for an oil & gas project, the same elevation data is also used to generate maps for a variety of other disciplines and uses such as environmental land planning, geotechnical foundation constructions, and hydrological studies mapping water flow.
Having a license for GIS software is not required to generate these topographic maps as they can be drafted with any standard programming language by following the methodology presented here. For those with access to GIS software, the acquisition and processing steps of the workflow could be useful for obtaining and preparing geospatial datasets to visualize using the GIS software.
[1] W. H. F. Smith, and D. T. Sandwell, Global seafloor topography from satellite altimetry and ship depth soundings (1997), Science, v. 277, p. 1957–1962
[2] F. Faraj, A. Braun, C. Yallup, and C. Saunders, Evaluating Source Rock Maturity Offshore Madagascar: Integrating the Tectonostratigraphic History with Basin Modeling (2020), Halliburton Landmark Exploration Insights Magazine Article, v. 3, p. 14–20