The post basically explains what the LAS file is, the format of LAS file and how to work with it in Python.

Introduction to LAS File 1

From Wikipedia:

The LAS format is a file format designed for the interchange and archiving of lidar point cloud data. It is an open, binary format specified by the American Society for Photogrammetry and Remote Sensing (ASPRS).

The LAS file contains a number of fields for each point that can be useful for analysis and for display:

  • x, y coordinates, most often in the UTM projection.
  • z, the elevation.
  • Classification.
  • The intensity of the returned pulse.
  • RGB value, merged from a camera flown with the laser scanner.
  • The return number, and the total number of returns from the pulse.
  • The scan angle, which indicates how far from nadir the scanner was pointed; this is typically up to about 20-25 degrees, positive and negative depending on which side of nadir. Away from nadir, smooth water surfaces will act like mirrors and there will be no returned energy.
  • Overlap points, where two flight lines covered the same area.
  • The LAS format allows distribution of the full waveform data, but that option is rarely used in practice. The resulting file sizes greatly increase the data storage, and few ultimate end users want or need that data.

The LAS file has a header with metadata about the file.

  • It is supposed to have the datum/projection information embedded, but it may be missing.
  • It has the elevation range, but usually has elevations that are both excessively high and excessively low. When used to color the elevations, this leads to a very compressed color scale. There is a sense in the lidar community that you should never delete returns, but just code them as noise (the older LAS versions had a classification code for low noise, and 1.4 added high noise), but this does not help in determining the true elevatioin range. I think the never delete a point may make sense for the data producers, but not the end users.

Read LAS File (with libLAS)

To read LAS file, libLAS package must be installed for Python. To install it, simply type the following command in the terminal:

# For Python 3
python3 -m pip install liblas
# For Python 2
python2 -m pip install liblas

Some code snippets come from the tutorial on libLAS website. There is also a Jupyter notebook version here.

Reading

  1. Use the iterator to clug through the points.
import numpy as np
from liblas import file
f = file.File("points.las", mode="r")
print(np.size(f))

# to loop through the points
count = 0
for p in f:
    count += 1
print(count)
  1. Or we can also read a specific points from a file
p = f.read(0)
print(p)

Headers, points, VLRs, colors, and GUIDs are copies, not references in the libLAS Python bindings. We can fetch the header with the following property:

h = f.header
print(h)

There are many properties of the header that we can get and set:

print("Major version and minor version: (%i, %i)" % (h.major_version, h.minor_version))
print("Data format id:", h.dataformat_id)
print("Min:", h.min)
print("Max:", h.max)
print("Scale:", h.scale)
print("Offset:", h.offset)
print("Point records count:", h.point_records_count)

from liblas import guid
print("Project id:", h.project_id)
print("GUID:", h.guid)

g = guid.GUID()
print("Create a GUID:", g)
h.guild = g
print("Modified GUID:", h.guid)
print("Modified project id:", h.project_id)

Point

The liblas.point module contains a Point class that allows us to manipulate LAS point data. It is fairly basic and contains a number of properties we can set and get:

p = f.read(1)

print("Coordinate values:", (p.x, p.y, p.z))
print("scan angle:", p.scan_angle)
print("scan direction:", p.scan_direction)
print("return number:", p.return_number)
print("number of returns:", p.number_of_returns)
print("flightline edge:", p.flightline_edge)
print("classification:", p.classification)
print("time:", p.time)
print("intensity:", p.intensity)
c = p.color
print("rgb: (%i, %i, %i)" % (c.red, c.blue, c.green))

Writing

To write a new LAS file, first we need to have a header. The header will have a number of default values, but it is important to set the dataformat_id and version_minor if we want 1.1 files or records that also include time values.

from liblas import header
h = header.Header()

# support storing time values
h.dataformat_id = 1

# Store a 1.1 version file
h.minor_version = 1

Another important item to note is possible to have the same file open for read and write at the same time because LAS files are sequential. For example, the following will fail:

f = file.File('junk.las', mode='w', header=h)
f2 = file.File('junk.las')

Writing to a LAS file is as simple as opening the file for write mode with the header you want to write and issuing the write() command with some liblas.point.Point instances:

f.close()
import liblas.point
f = file.File('junk.las', mode='w', header=h)
pt = liblas.point.Point()
f.write(pt)
f.close()

1 The intro part is mostly referenced from here.