Now, let's look at another way to visualize the elevation better using contours. A contour is an isoline along the same elevation in a dataset. Contours are usually stepped at intervals to create an intuitive way to represent elevation data, both visually and numerically, using a resource-efficient vector dataset.
The input to generate contours is our DEM and the output is a shapefile. The algorithm to generate contours is fairly complex and very difficult to implement using NumPy's linear algebra. So, our solution in this case is to fall back on the GDAL library, which has a contouring method available through the Python API. In fact, the majority of this script is just setting up the OGR library code that is needed to output a shapefile. The actual contouring is a single method call named gdal.ContourGenerate()
. Just before this call, there are comments defining the method's arguments. The most important ones are as follows:
contourInterval
: It is the distance in the dataset units between contourscontourBase
: It is the starting elevation for the contouringfixedLevelCount
: It specifies a fixed number of contours as opposed to distanceidField
: It is a name for a required shapefile dbf
field, usually just called IDelevField
: It is a name for a required shapefile dbf
field for the elevation value that is useful for the labeling in mapsYou should have GDAL and OGR installed from the Installing GDAL section in Chapter 4, Geospatial Python Toolbox. In the following code, we will define the input DEM filename, output shapefile name, create the shapefile data source with OGR, get the OGR layer, open the DEM, and generate contours on the OGR layer:
import gdal import ogr # Elevation DEM source = "dem.asc" # Output shapefile target = "contour" ogr_driver = ogr.GetDriverByName("ESRI Shapefile") ogr_ds = ogr_driver.CreateDataSource(target + ".shp") ogr_lyr = ogr_ds.CreateLayer(target, # wkbLineString25D is the type code for geometry with a z # elevation value. geom_type=ogr.wkbLineString25D) field_defn = ogr.FieldDefn("ID" ogr.OFTInteger) ogr_lyr.CreateField(field_defn) field_defn = ogr.FieldDefn("ELEV" ogr.OFTReal) ogr_lyr.CreateField(field_defn) # gdal.ContourGenerate() arguments # Band srcBand, # double contourInterval, # double contourBase, # double[] fixedLevelCount, # int useNoData, # double noDataValue, # Layer dstLayer, # int idField, # int elevField ds = gdal.Open(source) # EPGS:3157 gdal.ContourGenerate(ds.GetRasterBand(1), 400, 10, [], 0, 0, ogr_lyr, 0, 1)) ogr_ds = None
Now, let's draw the contour shapefile that we just created using PNGCanvas
, introduced in the PNGCanvas section of Chapter 4, Geospatial Python Toolbox:
import shapefile import pngcanvas # Open the contours r = shapefile.Reader("contour.shp") # Setup the world to pixels conversion xdist = r.bbox[2] - r.bbox[0] ydist = r.bbox[3] - r.bbox[1] iwidth = 800 iheight = 600 xratio = iwidth/xdist yratio = iheight/ydist contours = [] # Loop through all shapes for shape in r.shapes(): # Loop through all parts for i in range(len(shape.parts)): pixels = [] pt = None if i < len(shape.parts) - 1: pt = shape.points[shape.parts[i]:shape.parts[i+1]] else: pt = shape.points[shape.parts[i]:] for x, y in pt: px = int(iwidth - ((r.bbox[2] - x) * xratio)) py = int((r.bbox[3] - y) * yratio) pixels.append([px, py]) contours.append(pixels) # Set up the output canvas canvas = pngcanvas.PNGCanvas(iwidth, iheight) # PNGCanvas accepts rgba byte arrays for colors red = [0xff, 0, 0, 0xff] canvas.color = red # Loop through the polygons and draw them for c in contours: canvas.polyline(c) # Save the image with open("contours.png", "wb") as f: f.write(canvas.dump())
We will end up with the following image:
If we bring our shaded relief ASCIIGRID and the shapefile into a GIS, such as QGIS, we can create a simple topographic map as follows. You can use the elevation (that is ELEV) dbf
field that you specified in the script to label the contour lines with the elevation:
The techniques in these NumPy grid examples provide the building blocks for all kinds of elevation products. The USGS has an excellent web page with sample elevation-based data layers, including the examples that we created as well as some more advanced types:
http://edna.usgs.gov/datalayers.asp
Next, we'll work with one of the most complex elevation data types: LIDAR data.