Create, Process, and Export Digital Surface Model from Lidar Data
This example shows how to process aerial lidar data received from an airborne lidar system into a GeoTIFF file. Import a LAZ file containing aerial lidar data, create a spatially referenced digital surface model (DSM) from the data, crop the DSM to an area of interest, and export the cropped DSM to a GeoTIFF file.
When you export a DSM to a GeoTIFF file, you also export the projected coordinate reference system (CRS) for the data. Projected CRSs associate x- and y-coordinates to locations on Earth. Specifying the projected CRS is important when creating a model because the same coordinates in different projected CRSs can refer to different locations.
Read Aerial Lidar Data
Read 3-D point cloud data for an area near Tuscaloosa, Alabama from a LAZ file [1]. The area includes roads, trees, and buildings.
lazFileName = fullfile(toolboxdir("lidar"),"lidardata","las","aerialLidarData.laz"); lasReader = lasFileReader(lazFileName); ptCloud = readPointCloud(lasReader);
Display the data.
figure pcshow(ptCloud.Location)
Create DSM
A DSM includes the elevations of ground points, the elevations of natural features such as trees, and the elevations of artificial features such as buildings. Create a DSM from the point cloud data by using the pc2dem
function. Use the maximum point from each element of the point cloud, which corresponds to the first return pulse of the lidar data, by specifying the CornerFillMethod
as "max"
. The function returns an array of elevation values and the x- and y-limits of the data.
gridRes = 1;
[Z,xlimits,ylimits] = pc2dem(ptCloud,gridRes,CornerFillMethod="max");
Spatially Reference DSM
Spatially reference the DSM by creating a map reference object.
R = maprefpostings(xlimits,ylimits,size(Z))
R = MapPostingsReference with properties: XWorldLimits: [429745.02 430146.02] YWorldLimits: [3679830.75 3680114.75] RasterSize: [285 402] RasterInterpretation: 'postings' ColumnsStartFrom: 'south' RowsStartFrom: 'west' SampleSpacingInWorldX: 1 SampleSpacingInWorldY: 1 RasterExtentInWorldX: 401 RasterExtentInWorldY: 284 XIntrinsicLimits: [1 402] YIntrinsicLimits: [1 285] TransformationType: 'rectilinear' CoordinateSystemType: 'planar' ProjectedCRS: []
The reference object contains information such as the limits, the distance between the points, and the directions of the columns and rows. By default, the reference object assumes that columns start from the south and rows start from the west. These default values are consistent with the output of the pc2dem
function, which creates the elevation array such that the first element represents the southwesternmost point.
The ProjectedCRS
property of the reference object is empty, which means the DSM is not associated with a projected CRS. Read the CRS from the LAZ file and update the ProjectedCRS
property.
p = readCRS(lasReader); R.ProjectedCRS = p; disp(p)
projcrs with properties: Name: "NAD83 / UTM zone 16N" GeographicCRS: [1×1 geocrs] ProjectionMethod: "Transverse Mercator" LengthUnit: "meter" ProjectionParameters: [1×1 map.crs.ProjectionParameters]
A projected CRS consists of a geographic CRS and several parameters that are used to transform coordinates to and from the geographic CRS. A geographic CRS consists of a datum (including a reference ellipsoid), a prime meridian, and an angular unit of measurement. View the geographic CRS and its reference ellipsoid.
g = p.GeographicCRS
g = geocrs with properties: Name: "NAD83" Datum: "North American Datum 1983" Spheroid: [1×1 referenceEllipsoid] PrimeMeridian: 0 AngleUnit: "degree"
g.Spheroid
ans = referenceEllipsoid with defining properties: Code: 7019 Name: 'GRS 1980' LengthUnit: 'meter' SemimajorAxis: 6378137 SemiminorAxis: 6356752.31414036 InverseFlattening: 298.257222101 Eccentricity: 0.0818191910428158 and additional properties: Flattening ThirdFlattening MeanRadius SurfaceArea Volume
Display the spatially referenced DSM as an overhead surface by using the mapshow
function.
figure mapshow(Z,R,DisplayType="surface") axis image title("Digital Surface Model (DSM) from Aerial Lidar Data")
Crop DSM to Region of Interest
Represent the DSM region as a polygon by using a mappolyshape
object. Update the ProjectedCRS
property to match the CRS of the DSM.
bboxx = xlimits([1 1 2 2 1]); bboxy = ylimits([1 2 2 1 1]); bboxshape = mappolyshape(bboxx,bboxy); bboxshape.ProjectedCRS = p;
View the region using satellite imagery. You can visually confirm that the satellite imagery aligns with the DSM visualization created using the mapshow
function.
regioncolors = lines(2); geoplot(bboxshape, ... EdgeColor=regioncolors(1,:), ... FaceAlpha=0.2, ... LineWidth=2, ... DisplayName="Aerial Lidar Data Region") hold on geobasemap satellite legend
Select and display a region of interest. To use a predefined region that is bounded by roads on the east, north, and west, specify interactivelySelectPoints
as false
. Alternatively, you can interactively select four points that define a region by specifying interactivelySelectPoints
as true
.
interactivelySelectPoints = false; if interactivelySelectPoints [cropbboxlat,cropbboxlon] = ginput(4); %#ok<UNRCH> else cropbboxlat = [33.2571550; 33.2551982; 33.2551982; 33.2571125]; cropbboxlon = [-87.7530648; -87.7530139; -87.7509086; -87.7509086]; end cropbboxlat(end+1) = cropbboxlat(1); cropbboxlon(end+1) = cropbboxlon(1); cropbboxshape = geopolyshape(cropbboxlat,cropbboxlon); geoplot(cropbboxshape, ... EdgeColor=regioncolors(2,:), ... FaceAlpha=0.2, ... LineWidth=2, ... DisplayName="Selected Region of Interest")
Transform the latitude and longitude limit coordinates for the region to x- and y-limit coordinates. The geographic CRS underlying the satellite
basemap is WGS84, while the geographic CRS underlying the DSM data is NAD83. NAD83 and WGS84 are similar, but not identical. As a result, there can be discrepancies in coordinates between the satellite imagery and DSM.
[cropbboxx,cropbboxy] = projfwd(p,cropbboxlat(:),cropbboxlon(:));
Create the crop limits by finding the bounds of the x- and y-coordinates.
[cropxlimmin,cropxlimmax] = bounds(cropbboxx); cropxlimits = [cropxlimmin cropxlimmax]; [cropylimmin,cropylimmax] = bounds(cropbboxy); cropylimits = [cropylimmin cropylimmax];
Create a new spatially referenced DSM that contains data within the region of interest.
[Zcrop,Rcrop] = mapcrop(Z,R,cropxlimits,cropylimits);
Export DSM to GeoTIFF File
Write the cropped DSM to a GeoTIFF file called lidardsm.tif
. Specify the projected CRS by using the CoordRefSysCode
argument. The metadata for the LAZ file [1] indicates the projected CRS is UTM Zone 16N, specified by EPSG authority code 26916.
datafile = "lidardsm.tif";
epsgCode = 26916;
geotiffwrite(datafile,Zcrop,Rcrop,CoordRefSysCode=epsgCode)
You can also find the authority code by displaying the well-known text (WKT) string for the projected CRS. For this WKT, the authority code is in the last line.
wktstring(p,"Format","formatted")
ans = "PROJCRS["NAD83 / UTM zone 16N", BASEGEOGCRS["NAD83", DATUM["North American Datum 1983", ELLIPSOID["GRS 1980",6378137,298.257222101, LENGTHUNIT["metre",1]]], PRIMEM["Greenwich",0, ANGLEUNIT["degree",0.0174532925199433]], ID["EPSG",4269]], CONVERSION["UTM zone 16N", METHOD["Transverse Mercator", ID["EPSG",9807]], PARAMETER["Latitude of natural origin",0, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8801]], PARAMETER["Longitude of natural origin",-87, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8802]], PARAMETER["Scale factor at natural origin",0.9996, SCALEUNIT["unity",1], ID["EPSG",8805]], PARAMETER["False easting",500000, LENGTHUNIT["metre",1], ID["EPSG",8806]], PARAMETER["False northing",0, LENGTHUNIT["metre",1], ID["EPSG",8807]]], CS[Cartesian,2], AXIS["easting",east, ORDER[1], LENGTHUNIT["metre",1]], AXIS["northing",north, ORDER[2], LENGTHUNIT["metre",1]], ID["EPSG",26916]]"
One way to validate the GeoTIFF file is to return information about the file as a RasterInfo
object. For example, verify that the projected CRS is in the file by querying the CoordinateReferenceSystem
property of the RasterInfo
object.
info = georasterinfo(datafile); info.CoordinateReferenceSystem
ans = projcrs with properties: Name: "NAD83 / UTM zone 16N" GeographicCRS: [1×1 geocrs] ProjectionMethod: "Transverse Mercator" LengthUnit: "meter" ProjectionParameters: [1×1 map.crs.ProjectionParameters]
Another way to validate the GeoTIFF file is by displaying it. Read the new DSM as an array and a reference object by using the readgeoraster
function. Then, display the DSM.
[Z2,R2] = readgeoraster(datafile); figure mapshow(Z2,R2,DisplayType="surface") axis image title("Cropped DSM from Aerial Lidar Data")
You can use the GeoTIFF file in other applications that import GIS data. For example, RoadRunner enables you to add elevation data from GeoTIFF files to scenes.
References
[1] OpenTopography. “Tuscaloosa, AL: Seasonal Inundation Dynamics And Invertebrate Communities,” 2011. https://doi.org/10.5069/G9SF2T3K.