Seeking tool to generate Mesh from DTM?

A DTM raster can be represented by triangle meshes by finding a set of non-overlapping triangles that covers the entire mesh and approximates the elevation field. There are two different types of triangle meshes that can be used for this purpose:

  • a triangulated regular network (TRN), in which every pixel of the raster is represented by a vertex, and all triangles have the same size and shape. All the original information of the DTM raster is present in the TRN, but the memory required for storing the mesh is typically quite high.
  • a triangulated irregular network (TIN), in which there are fewer vertices than raster pixels and the triangles have different shapes and sizes. The vertices and the triangulation are chosen in such a way that the resulting surface approximates the original DTM raster up to a specified error. This typically results in much smaller files, since plane or nearly plane areas can be represented using only a couple of vertices.

In most applications, if you need to deal with elevation meshes, you'd go with a TIN since throwing out redundant or nearly redundant information allows for more efficient computations. However, creating TINs from rasters isn't straightforward, since there are many different triangulations that approximate a grid with the same error, but using different vertex sets.

Software for creating TINs

  • Michael Garland's Terra software.
  • ArcGIS: Raster to TIN function from the 3D Analyst toolbox.
  • SAGA GIS: Grid to TIN (Surface Specific Points) function, followed by Export TIN to Stereo Lithography File (STL) function to export the TIN to a mesh format readable by Meshlab.

Software for creating TRNs

  • VTBuilder, which is part of the Virtual Terrain Project: load the DTM raster using "Layer | Import Layer" and then convert it to a TRN using "Elevation | Convert grid to TIN". Then you select the newly generated layer in the Layer overview and select "Elevation | Export To...". VTBuilder can read all raster formats that GDAL supports, and exports the TRN to OBJ, PLY, GMS, DXF, DAE or WRL formats.
  • SAGA GIS: Grid to TIN function.
  • Roll your own solution: Implementing it isn't particularly hard. Here's a Python script that uses the GDAL library to read a raster DTM, and then writes out a binary PLY mesh.

    Save the script as gdal_rastertotrn.py, then call it using python gdal_rastertotrn.py <inputraster> <outputply>.

    Here's an example. Converting a raster DTM of Crater Lake called crater_lake.tif...

    ... by calling python gdal_rastertotrn.py crater_lake.tif crater_lake.ply, and opening the resulting crater_lake.ply in Meshlab:

    Here's the (unpolished) script. Since it uses the GDAL library, it can convert all raster types supported by GDAL. It only writes PLY files.

    #!/usr/bin/python
    
    import sys
    import numpy as np
    from osgeo import gdal
    
    def write_ply(filename, coordinates, triangles, binary=True):
        template = "ply\n"
        if binary:
            template += "format binary_" + sys.byteorder + "_endian 1.0\n"
        else:
            template += "format ascii 1.0\n"
        template += """element vertex {nvertices:n}
    property float x
    property float y
    property float z
    element face {nfaces:n}
    property list int int vertex_index
    end_header
    """
    
        context = {
         "nvertices": len(coordinates),
         "nfaces": len(triangles)
        }
    
        if binary:
            with  open(filename,'wb') as outfile:
                outfile.write(template.format(**context))
                coordinates = np.array(coordinates, dtype="float32")
                coordinates.tofile(outfile)
    
                triangles = np.hstack((np.ones([len(triangles),1], dtype="int") * 3,
                    triangles))
                triangles = np.array(triangles, dtype="int32")
                triangles.tofile(outfile)
        else:
            with  open(filename,'w') as outfile:
                outfile.write(template.format(**context))
                np.savetxt(outfile, coordinates, fmt="%.3f")
                np.savetxt(outfile, triangles, fmt="3 %i %i %i")
    
    def readraster(filename):
        raster = gdal.Open(filename)
        return raster
    
    
    def createvertexarray(raster):
        transform = raster.GetGeoTransform()
        width = raster.RasterXSize
        height = raster.RasterYSize
        x = np.arange(0, width) * transform[1] + transform[0]
        y = np.arange(0, height) * transform[5] + transform[3]
        xx, yy = np.meshgrid(x, y)
        zz = raster.ReadAsArray()
        vertices = np.vstack((xx,yy,zz)).reshape([3, -1]).transpose()
        return vertices
    
    
    def createindexarray(raster):
        width = raster.RasterXSize
        height = raster.RasterYSize
    
        ai = np.arange(0, width - 1)
        aj = np.arange(0, height - 1)
        aii, ajj = np.meshgrid(ai, aj)
        a = aii + ajj * width
        a = a.flatten()
    
        tria = np.vstack((a, a + width, a + width + 1, a, a + width + 1, a + 1))
        tria = np.transpose(tria).reshape([-1, 3])
        return tria
    
    
    def main(argv):
        inputfile = argv[0]
        outputfile = argv[1]
    
        raster = readraster(inputfile)
        vertices = createvertexarray(raster)
        triangles = createindexarray(raster)
    
        write_ply(outputfile, vertices, triangles, binary=True)
    
    if __name__ == "__main__":
        main(sys.argv[1:])
    

We have just created an open source project that will help you do exactly what you want. Tin-Terrain is a simple command line tool that will generate a mesh from DEM files either as TIN or TRN.

The output can be saved as .obj file, so can be directly drag-and-dropped into meshlab. It will also support tiling with zoom levels in quantised mesh format.