Open a las file

Open RStudio then create a new Project

Create a New Directory, and New Project. Name it Lab4 and save it in your GEOG457 Folder.

Create a new script to put your work into (The page with the green + is the new file button, choose R Script as type).

Start by entering the following code into your new file. This code will
  • Open the library lidR
  • Read a file using readLAS() and save it into a variable named las
  • Print information about the file we opened to the console.
#las <- readLAS("L:/GEOG457/Lab4/PrinceGeorge/pg_2010/pg_2010_093G08644.las")
# Open decimated version instead to prevent computer crash
las <- readLAS("L:/GEOG457/Lab4/1m_las_tile/pg_2010/pg_2010_093G08644.las")

To run your code click on the first line and press [Ctrl] + [Enter] or press the run button

This will perform the action of the current line then progress the cursor to the next line, run all three lines.

We will wait here and get everyone caught up to having information about the file printed to the screen

This data shows it’s coordinate reference information, a lot of LiDAR data may not, and in fact the source data using in the lab did not, this is how it was fixed it took about 72hr for this to process the 4 catalogs (catalogs are explained later) we are using for today’s lab so we will not be running this now:
# Open a directory with LASCatalog
ctg <- readLAScatalog("K:/GEOG457/AlezaLake/las_tile_reproject/")

# For every las file in the catalog perform an operation
for (i in 1:(length(ctg))) {
  tmp <- ctg[i,]
  # Computationally determin new file name
  # (Technically you could just overwrite the original file if you live dangerous)
  part_path = strsplit(tmp$filename, "las_tile_reproject/aleza")[[1]]
  new_path = paste("L:/GEOG457/Lab4/AcientForest/las_tile/af", part_path[2], sep = "")
  # Open the Las File
  las <- readLAS(tmp$filename)
  # Set the projection (This does not modify points only helps 
  # the computer to know what the locations mean!)
  # The projection of 32610 (UTM 10N, WGS 84) is prior knowlege 
  # must be obtained from producer of .las file
  epsg(las) <- 32610 
  # Save the file, also lets add a .lax or index file 
  # to make future catalog operations faster
  writeLAS(las, new_path, index = TRUE)

Many times LiDAR data is processed as a Local Coordinate System (LCS), this is to say the computer does not care where the points are in the world only in the cloud, you will notice that extent has no units attached only numbers. One easy wat to see this is in an open-source program that we will not be using today called Cloud Compare, when you open a LiDAR file it typically will shift all points on import, allow you to do your work, then apply the reverse shift when you export the file, this is intended to help keep precision by reducing the number of significant digits.

To get a 3D representation of the point cloud you can simply plot it. Have some patience it may take some time to think and may open in the background (watch your taskbar for a new icon to appear)

When you are done looking at the file it is very important that you close the RGL Device, and clean up RAM. You will see the amount of RAM R is using near the upper right corner of RStudio. RAM can be freed by running the garbage collector
gc() #Free up RAM used by plot()

Digital Elevation Models

Digital Elevation Models or DEM’s are something you should have interacted with by now and are often generated from LiDAR. However when possible avoid saying DEM there are multiple types, and at the scales, we are working with it matters.

The two most common are Digital Terrain Models (DSM): These are DEM’s where the elevation is the ground without any Vegetation or Structures. On the other hand Digital Surface Models are created from the first return (highest point) and show trees and buildings.

There are more forms of DEM’s you may make for domain speific purposes, for example a Canopy Height Model (CHM) which is the difference between the DSM and DTM, (Technically a CHM should also filture out buildings, however that is extra effort rarely required).

In your assignment please do not refer to speific products as DEM’s they should all be DTM, DSM, or CHM.

Generate a DTM:

dtm <- grid_terrain(las, res = 1, algorithm = tin())

Generate a DSM:

thr <- c(0,2,5,10,15)
edg <- c(0, 1.5)
dsm <- grid_canopy(las, res = 1, pitfree(thr, edg))

Generate a CHM

For this we have two options, first is the simple way. Though could potentially be more affected by the effects of raster resolution.

chm <- dsm - dtm

The second way is to make a normalized point cloud then produce the CHM from that. We will revisit this after switching over the Ancient forest where we will be using higher resolution data and more of it.


LAS files are very large (so much so we have concerns about crashing the lab computers, however, we also want to do tree detection today so want the full resolution.

As a rough overview of what this does, we will open a folder instead of a file, and then the computer can work on the entire area one piece at a time so we hopefully have enough ram. Additionally, many options can be run in parallel on a multicore computer in a catalogue. Below is an example of what processing looks like.

Open the Acient Forest data

Be very careful here attempting to work with the point data of the entire region will crash your computer (que reminder to save your script!)

# Enample Multicore processing using future package

#open data
acient_forest_ctg <- readLAScatalog("L:/GEOG457/Lab4/AcientForest/las_tile/")

Would you like a map with that?

plot(acient_forest_ctg, map=TRUE)

You can use the catalog like a las file in most lidR functions. And often time with better results:

It is also convient, not sure exactly what tile you need? Just extract an area from the catalog, lets take a look at UNBC

pg_2010_ctg <- readLAScatalog("L:/GEOG457/Lab4/1m_las_tile/pg_2010/")
plot(clip_circle(pg_2010_ctg, x = 512179, y = 5971394, radius = 500), size = 2)

Tree Detection

acient_forest_ctg <- readLAScatalog("L:/GEOG457/Lab4/AcientForest/las_tile/")

# Subset
acient_forest_subset <- clip_rectangle(acient_forest_ctg, xleft = 616950, xright = 617500, ytop = 5958786, ybottom = 5958100)
# Generate Products for later
dtm <- grid_terrain(acient_forest_subset, algorithm = tin())

thr <- c(0,2,5,10,15)
edg <- c(0, 1.5)
dsm <- grid_canopy(acient_forest_subset, 1, pitfree(thr, edg))

# Normalize Data
af_normalized <- normalize_height(acient_forest_subset, dtm)

# Better Canopy height model
chm <- grid_canopy(af_normalized, 1, pitfree(thr, edg))
plot(chm, col = height.colors(50))

#Find Trees
ttops <- find_trees(af_normalized, lmf(ws = 10))
plot(chm, col = height.colors(50))
plot(ttops, add = TRUE, col = 'Red')

#Segement Trees
af_trees <- segment_trees(af_normalized, li2012(R = 3, speed_up = 5))
col <- random.colors(200)
tree_segments <- plot(af_trees, color = "treeID")
add_treetops3d(tree_segments, ttops)

# Deliniate Crowns
crowns <- delineate_crowns(af_trees)

Assignment (5 points)

For this assignment create an R script, each question should start with a comment of the question number. There is no need to submit any plots, just the R file, please put written answers in comments using the # symbol.

# Boilerplate ####

# Lab4 ####
## Question 1 ####

## Question 2 ####

## Question 3 ####

## Question 4 ####

Tip: The #### at the end of a comment denotes it as part of an outline, the number of leading #’s sets the heading level. You can click on the outline to automatically scroll to the code in question.


  • (0.5 Points) There is a bridge over the Fraser River at the coordinates 53°54’36″N 122°43’24″W, what is the file name of the full resolution Prince George 2018 las file that contains this location?
    Expected Computation Time: less than 1 minute
  • (1 Point) Create a DEM of Prince George (yes all of it). Your output should be at a 25m resolution and should be created from the 2014 data from the 10m LiDAR, use the algorithm knnidw(), tin() will run but takes about 3x as long though with a few less holes in the river. Note: you can safely ignore the warnings about points outside of the convex hull.

    Export your image as a GeoTIFF (See line 2 of code at bottom of page). No need to submit image so long as code is working.
    Expected Computation Time: 5-6 minutes

  • (1.5 Points) Make a subset of the 1 metre resolution 2018 Prince George data in Lheidli T’enneh Memorial Park, roughly the same area as the blue rectangle in the picture below. If you are not familiar with the area it is 53°54’23″N 122°44’03″W. TIPs: At some point in this question you will want this equitation: lmf(ws = 15, hmin = 20), when running the tree_segmentation() use dalponte2016 it runs substantially faster than li2012; if you do not know how to use dalponte2016 as this is the first time seeing it, fear not we have the book.
    Finally I would advise against plotting the point clouds, and only plotting the raster products.
    • Make a Canopy Height Model (from a normalized point cloud)
      You may get an excessive amount of warnings, again these again be ignored for our purposes.
    • Make a plot with markings over the tree tops
    • Make a 2nd plot with the crowns outlined

Expected Computation Time: 10-15 minutes

  • (1 Point) Create a Plot showing the change between 2018 and 2010 of the Prince George Cutbanks 53°55’45″N 122°45’34″W
    Expected Computation Time: 2-3 Minutes.
  • (0.5 Points) Why do we prefer LASCatalog over individual large LAS files? And why do we prefer it over simply applying operations to each file individually.
  • (0.5 Point) Well organized code; part of being a skilled professional is making your work understandable to others (or yourself in a few months), clean code is also easier to debug. Before submitting your assignment make sure that the R file is well organized, and variables have logical easy to understand names.

Not part of the lab but possibly useful commands

# Save a geoTIFF
writeRaster(dtm, "dtm.tif", options=c('TFW=YES'))

# Generate Hill shade
slope <- terrain(dtm, opt="slope")
aspect <- terrain(dtm, opt="aspect")
hill_shade <- hillShade(slope, aspect) # hill_shade variable has underscore, don't name variables same as function
plot(hill_shade, col = grey.colors(256))

Categories: GEOG 457Labs