Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

GIS & Geospatial Data (QGIS, GeoPandas, PostGIS)  Module 7 – GNSS, field data and point clouds

LiDAR and point clouds

Read LiDAR points, their returns, intensity and classes, store them as LAS, LAZ or COPC, classify the ground and build a terrain model from it.

  • Advanced
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 8 of 20 minutes free
Free: A LiDAR point keeps its return, its intensity and its class · LAS, LAZ and COPC store the same points for different jobs. With a pass: 2 in-depth parts and 3 interview answers, about 12 minutes.

What you will learn

  • Describe a LiDAR point's attributes, its return number, number of returns, intensity and ASPRS class
  • Decode a LAS point record, and explain what LAZ and COPC add to LAS
  • Classify ground points with a morphological filter and choose its window and slope
  • Build a terrain model and canopy heights from classified points, and check them against known ground
  • Explain which fine-scale surveys India's 2021 guidelines reserve for Indian Entities

Before you start

What you’ll learn · Before you start

What you will learn

  • Describe a LiDAR point's attributes, its return number, number of returns, intensity and ASPRS class
  • Decode a LAS point record, and explain what LAZ and COPC add to LAS
  • Classify ground points with a morphological filter and choose its window and slope
  • Build a terrain model and canopy heights from classified points, and check them against known ground
  • Explain which fine-scale surveys India's 2021 guidelines reserve for Indian Entities

Before you start

On this page

One laser pulse sent down through a tree on this lesson’s hill came back three times: from the crown at 564.36 m, from a branch at 560.94 m and from the ground at 558.09 m. LiDAR records millions of such returns, and point clouds store them with the attributes that let software tell the ground from everything above it.

Here you read the attributes of a LiDAR point, decode a LAS record byte by byte, and see what LAZ and COPC add. Then you classify a synthetic survey’s ground with a morphological filter, watch it fail with the wrong settings, build a terrain model and tree heights from it, and read the rules India sets for surveys this fine.

A LiDAR point keeps its return, its intensity and its class

An airborne LiDAR: Light detection and ranging: measuring distances with laser pulses, timed from the moment each leaves the scanner to the moment each echo comes back. Airborne scanners turn the echoes into points of a point cloud. scanner fires laser pulses and times their echoes, so each echo becomes a point with x, y and z. A pulse that meets a tree can come back several times, and the point cloud: A set of points in three dimensions, each with x, y and z and attributes such as its return number, intensity and class, as a LiDAR scanner or photogrammetry produces them. numbers each echo: its return number and the pulse’s number of returns. The LAS specification records also an intensity, the echo’s strength, and a classification, whose codes include 1 “Unclassified”, 2 “Ground”, 3 to 5 low, medium and high vegetation, 6 “Building”, 7 “Low Point (Noise)”, 9 “Water” and 18 “High Noise” (LAS 1.5). Follow one pulse of the survey:

Interactive · message by message One laser pulse and its three returns Step through one pulse of the survey, from the scanner down through a tree crown to the ground and back.
One laser pulse and its three returns, step 5 of 5. Messages so far: Scanner to Tree crown: laser pulse; Tree crown to Scanner: return 1: 564.36 m; Tree crown to Scanner: return 2: 560.94 m; Ground to Scanner: return 3: 558.09 m.Scanner3 points: 1, 2 and 3 of 3Tree crownleaves and branchesGroundunder the treelaser pulsereturn 1: 564.36 mreturn 2: 560.94 mreturn 3: 558.09 m

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The pulse leaves. The scanner fires a short pulse of laser light down towards the hill's flank and starts its clock. It knows its own position and the pulse's direction at that instant.
  2. First return. Part of the light reflects off the top of the crown and comes back first. Its travel time gives a height of 564.36 m, return 1 of 3, with a strong intensity of 174.
  3. Through the leaves. The rest of the pulse passes between the leaves and meets a branch lower down, which sends back a second, weaker echo at 560.94 m, intensity 99.
  4. The ground. What is left reaches the ground and comes back last, at 558.09 m, 6.27 m below the crown's top. This last return is the one a terrain model needs.
  5. Three points. The scanner stores three points, numbered 1, 2 and 3 of 3, with the same x and y because the lesson's pulses point straight down. Classification comes later, by software that decides which points are ground.

The survey covers 120 m by 120 m of the hill’s eastern flank, with two buildings, 18 trees and some noise. In a LAS file of point format 6 each point is a record of 30 bytes; the program encodes that pulse’s three returns and reads them back:

cloud.py: a synthetic airborne survey of the hill's flank Python · lidar/cloud.py
"""A synthetic airborne LiDAR survey of 120 m by 120 m on the eastern flank of the module's hill, in metres of UTM zone
43N with heights above the geoid: about two pulses a square metre, each giving one to three returns.

Every point is (x, y, z, intensity, return number, number of returns, true class), with the true class an ASPRS code:
2 ground, 5 high vegetation, 6 building, 7 low point (noise), 18 high noise. The truth lets a classifier be measured.
"""
import math
import random

WEST, SOUTH, SIZE = 374_700.0, 2_042_940.0, 120.0
BUILDINGS = [(374_712.0, 2_043_010.0, 30.0, 24.0, 9.0), (374_760.0, 2_042_950.0, 48.0, 40.0, 7.0)]  # x, y, width, depth, height
rng0 = random.Random(5)
TREES = [(WEST + rng0.uniform(5, 115), SOUTH + rng0.uniform(70, 115), rng0.uniform(3, 5), rng0.uniform(8, 15)) for _ in range(18)]


def ground(x, y):
    """The hill's bare ground (m above the geoid), as in the elevation models lesson."""
    u = 1 - ((x - 374_000.0) / 1_000.0) ** 2 - ((y - 2_043_000.0) / 800.0) ** 2
    return 520.0 + 150.0 * max(0.0, u) ** 2


def roof(x, y):
    """The height of a flat roof over (x, y), or None: each roof is level, its height above the ground's highest corner."""
    for bx, by, w, d, h in BUILDINGS:
        if bx <= x < bx + w and by <= y < by + d:
            return max(ground(bx, by), ground(bx + w, by), ground(bx, by + d), ground(bx + w, by + d)) + h
    return None


def canopy(x, y):
    """The height of a tree crown over (x, y), or None (crowns are domes)."""
    best = None
    for tx, ty, r, h in TREES:
        d = math.hypot(x - tx, y - ty)
        if d < r:
            top = ground(tx, ty) + h - (h * 0.4) * (d / r) ** 2
            best = top if best is None else max(best, top)
    return best


def survey(seed=3, spacing=0.7):
    """All points of the survey: pulses on a jittered grid, returns from roofs, crowns and the ground, and noise."""
    rng = random.Random(seed)
    points = []
    steps = int(SIZE / spacing)
    for i in range(steps):
        for j in range(steps):
            x = WEST + (i + rng.random()) * spacing
            y = SOUTH + (j + rng.random()) * spacing
            g = ground(x, y) + rng.gauss(0, 0.05)
            if (r := roof(x, y)) is not None:
                points.append((x, y, r + rng.gauss(0, 0.05), 820 + rng.randint(-40, 40), 1, 1, 6))
            elif (c := canopy(x, y)) is not None:
                n = 3 if rng.random() < 0.3 else 2 if rng.random() < 0.7 else 1  # the pulse may not reach the ground
                points.append((x, y, c + rng.gauss(0, 0.1), 160 + rng.randint(-30, 30), 1, n, 5))
                if n == 3:
                    points.append((x, y, (c + g) / 2 + rng.gauss(0, 0.5), 90 + rng.randint(-20, 20), 2, 3, 5))
                if n > 1:
                    points.append((x, y, g, 120 + rng.randint(-30, 30), n, n, 2))
            else:
                points.append((x, y, g, 300 + rng.randint(-50, 50), 1, 1, 2))
    for _ in range(40):  # low noise: echoes that come back from below the ground
        x, y = WEST + rng.uniform(0, SIZE), SOUTH + rng.uniform(0, SIZE)
        points.append((x, y, ground(x, y) - rng.uniform(3, 10), rng.randint(5, 40), 1, 1, 7))
    for _ in range(25):  # high noise: birds and haze far above the surface
        x, y = WEST + rng.uniform(0, SIZE), SOUTH + rng.uniform(0, SIZE)
        points.append((x, y, ground(x, y) + rng.uniform(40, 80), rng.randint(5, 40), 1, 1, 18))
    return points
One pulse's three returns as LAS records Python · lidar/record.py
"""One pulse through a tree crown, its three returns as LAS point records of format 6 (30 bytes each), and back."""
import struct

from cloud import survey

SCALE, OFFSET = (0.01, 0.01, 0.01), (374_700.0, 2_042_900.0, 500.0)  # from the file's header
FORMAT6 = "<iiiHBBBBhHd"  # X Y Z intensity returns flags class user-data scan-angle source gps-time


def encode(p, gps_time):
    x, y, z, intensity, ret, n, cls = p
    ix, iy, iz = (round((v - o) / s) for v, o, s in zip((x, y, z), OFFSET, SCALE))  # coordinates as scaled integers
    return struct.pack(FORMAT6, ix, iy, iz, intensity, ret | n << 4, 0, cls, 0, 0, 1, gps_time)


def decode(raw):
    ix, iy, iz, intensity, returns, _, cls, *_rest, gps_time = struct.unpack(FORMAT6, raw)
    x, y, z = (i * s + o for i, s, o in zip((ix, iy, iz), SCALE, OFFSET))
    return (ix, iy, iz), (x, y, z), intensity, returns & 0x0F, returns >> 4, cls


points = survey()
k = next(i for i, p in enumerate(points) if p[5] == 3)  # the first pulse that came back three times
pulse = points[k : k + 3]
records = [encode(p, 1_000.0 + 0.000_01 * k) for p in pulse]
print(f"record size: {struct.calcsize(FORMAT6)} bytes; the first return as stored:\n  {records[0].hex(' ')}")
for raw in records:
    ints, (x, y, z), intensity, ret, n, cls = decode(raw)
    print(f"return {ret} of {n}: stored {ints}, so x {x:,.2f}, y {y:,.2f}, z {z:.2f} m; intensity {intensity}, class {cls}")
print(f"crown to ground along this pulse: {pulse[0][2] - pulse[2][2]:.2f} m")

Output

record size: 30 bytes; the first return as stored:
  fe 00 00 00 41 36 00 00 24 19 00 00 ae 00 31 00 05 00 00 00 01 00 b2 4b 54 6f 0d 40 8f 40
return 1 of 3: stored (254, 13889, 6436), so x 374,702.54, y 2,043,038.89, z 564.36 m; intensity 174, class 5
return 2 of 3: stored (254, 13889, 6094), so x 374,702.54, y 2,043,038.89, z 560.94 m; intensity 99, class 5
return 3 of 3: stored (254, 13889, 5809), so x 374,702.54, y 2,043,038.89, z 558.09 m; intensity 96, class 2
crown to ground along this pulse: 6.27 m

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 record.py

The coordinates are stored as whole numbers, x = stored × scale + offset, with the scale and offset in the file’s header, so 254 becomes 374,702.54 m at a scale of 0.01 and an offset of 374,700. One byte holds both the return number and the number of returns, 4 bits each.

Checkpoint

Check: Returns, intensity and classes

  1. Question 1 of 2 A pulse through a tree gives three returns. Which one is most likely the ground under the tree?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Return 3 of 3, the last echo

    Echoes come back in the order the pulse meets surfaces. The last of several is the best candidate for ground, which is why ground filters work on last and only returns.

  2. Question 2 of 2 Which ASPRS class codes mark ground and building points?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 2 for ground and 6 for building

    2 Ground, 3 to 5 vegetation by height, 6 Building, 7 Low Point (Noise), 9 Water and 18 High Noise are among the codes; software that reads LAS reads these numbers.

LAS, LAZ and COPC store the same points for different jobs

LAS is ASPRS’s format, an OGC Community Standard since 2018; version 1.5, the current one, keeps only point formats 6 to 10 and has a header of 393 bytes (LAS 1.5, definition). LAZ compresses LAS without losing anything, to “only 7-20% of the original size” by its makers’ account (LASzip). COPC (Cloud Optimized Point Cloud): A LAZ 1.4 file whose points are organised in a clustered octree, with a hierarchy that tells a reader which chunk of the file holds which part of the cloud. is “a LAZ 1.4 file that stores point data organized in a clustered octree”, with a hierarchy that tells a reader which chunk holds which part of the cloud, so that viewers can fetch only what they show, much as a COG does for rasters (COPC). The program counts the survey’s points by class and works out the size of its LAS file:

The survey's points by class, and its size as LAS Python · lidar/classes.py
"""The survey's points by class and by return: what a LAS file of them holds, and what it would weigh."""
from cloud import BUILDINGS, TREES, survey

CODES = {2: "ground", 5: "high vegetation", 6: "building", 7: "low point (noise)", 18: "high noise"}
points = survey()
print(f"{len(points):,} points over 120 m by 120 m: {len(points) / 14_400:.2f} a square metre")
for code, name in CODES.items():
    n = sum(p[6] == code for p in points)
    print(f"  class {code:>2} {name:<18} {n:>6,}")
first = sum(p[4] == 1 for p in points)
last_ground = sum(p[6] == 2 and p[5] > 1 for p in points)
print(f"first returns: {first:,}; ground points that were the last of several returns: {last_ground:,}")
print(f"{len(BUILDINGS)} buildings and {len(TREES)} trees")
print(f"as LAS 1.5, point format 6: a 393-byte header and {len(points):,} records of 30 bytes, {393 + 30 * len(points):,} bytes before the VLRs")

Output

30,952 points over 120 m by 120 m: 2.15 a square metre
  class  2 ground             23,595
  class  5 high vegetation     1,922
  class  6 building            5,370
  class  7 low point (noise)      40
  class 18 high noise             25
first returns: 29,306; ground points that were the last of several returns: 1,200
2 buildings and 18 trees
as LAS 1.5, point format 6: a 393-byte header and 30,952 records of 30 bytes, 928,953 bytes before the VLRs

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 classes.py

30,952 points, 2.15 a square metre, make 928,953 bytes of LAS before the file’s variable-length records, such as its CRS. QGIS opens LAS and LAZ files directly and builds an index the first time it loads one (QGIS); PDAL processes them in pipelines, JSON arrays of stages that read, filter and write (PDAL).

One such filter classifies the ground: it keeps the points of the lowest surface and removes what stands on it, such as roofs and crowns, and the morphological kind does so with windows of growing size. Gridding the ground points then gives a terrain model, and gridding the first returns gives a surface model of the crowns and roofs.

Checkpoint

Check: LAS, LAZ and COPC

  1. Question 1 of 2 What does COPC add to a LAZ file?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: An octree that clusters the points, with a hierarchy telling a reader which chunk holds which part

    A COPC file is a LAZ 1.4 file whose points are organised in a clustered octree, so a reader can stream the part of the cloud it needs.

  2. Question 2 of 2 A LAS header gives an x scale of 0.01 and an x offset of 374,700. A record stores X as 254. What is the point's x coordinate in metres?

    Type a number.

    A hint

    x = stored × scale + offset.

    Show the answer: Question 2 of 2

    Answer: 374702.54 m (anything from 374702.539 to 374702.541 counts)

    254 × 0.01 + 374,700 = 374,702.54 m, the lesson's pulse. Storing whole numbers with a scale keeps every record at a fixed size and a known precision, 1 cm here.

Classifying ground with a morphological filter, measured Pro: opens with a Pro, Premium or Ultimate pass

A terrain model needs the ground points, and a ground filter must find them under trees and roofs on sloping ground. Here a filter after the idea of SMRF runs on the survey with known truth, and its settings are pushed until it fails.

In this part:

  • How a morphological filter decides what is ground
  • Windows too small keep roofs, and slopes too strict lose ground
  • The same steps in PDAL and QGIS

2 runnable examples (Python) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

From ground points to a terrain model, and India's rules for fine surveys Pro: opens with a Pro, Premium or Ultimate pass

Classified points become surfaces: a terrain model from the ground, a surface model from the first returns, and heights of trees and buildings from the difference. Here each is checked against the survey’s truth, and the rules for such data in India are set out.

In this part:

  • A 1 m terrain model from the ground points, checked
  • Canopy heights from the surface model minus the terrain
  • A terrain model from classified ground points
    • Results of the sample tests
  • Tiles, thinning and storage, with numbers
  • India: national programmes and the rules for fine data

1 runnable example (Python) · 1 exercise with sample tests · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: decode a LAS point

The exercise turns the record layout of this lesson into a function that reads one point of format 6, and checks it on the pulse through the tree crown. Then the self-check covers the first two sections:

Exercise · Easy · Python

Decode a LAS point record of format 6

A LAS point record of format 6 is 30 bytes, little-endian: X, Y and Z as 32-bit signed integers, the intensity as a 16-bit unsigned integer, then one byte holding the return number in its low 4 bits and the number of returns in its high 4 bits, one byte of flags, the classification as one unsigned byte, and 13 more bytes this exercise does not need. A coordinate is stored × scale + offset, with the scale and the offset of each axis from the file's header.

Write decode(record, scale, offset), where record is 30 bytes and scale and offset are (x, y, z) tuples. Return (x, y, z, intensity, return_number, number_of_returns, classification), with x, y and z rounded to 2 decimals.

The sample tests decode the lesson's pulse through a tree crown, three records with the lesson's scale and offset.

Starter code · point.py

import struct


def decode(record, scale, offset):
    """(x, y, z, intensity, return_number, number_of_returns, classification) of a 30-byte record of format 6."""
    # Replace this line: unpack the integers, then apply each axis's scale and offset.
    return (0.0, 0.0, 0.0, 0, 0, 0, 0)
The sample tests · test_point.py
from point import decode

SCALE, OFFSET = (0.01, 0.01, 0.01), (374_700.0, 2_042_900.0, 500.0)
PULSE = [bytes.fromhex(h) for h in (
    "fe0000004136000024190000ae003100050000000100b24b546f0d408f40",
    "fe00000041360000ce17000063003200050000000100b24b546f0d408f40",
    "fe00000041360000b116000060003300020000000100b24b546f0d408f40",
)]


def test_first_return():
    """the crown's return: 1 of 3, high vegetation"""
    assert decode(PULSE[0], SCALE, OFFSET) == (374702.54, 2043038.89, 564.36, 174, 1, 3, 5)


def test_last_return_is_ground():
    """the last return of the pulse is ground, class 2"""
    x, y, z, intensity, ret, n, cls = decode(PULSE[2], SCALE, OFFSET)
    assert (ret, n, cls, z) == (3, 3, 2, 558.09)


def test_offsets_matter():
    """another header's offset moves every coordinate by the same amount"""
    assert decode(PULSE[1], SCALE, (0.0, 0.0, 0.0))[:3] == (2.54, 138.89, 60.94)
A hint

struct.unpack_from("<iiiHBBB", record) reads the first 17 bytes: X, Y, Z, the intensity, the returns byte, the flags and the class. The return number is byte & 0x0F and the number of returns byte >> 4.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Practice that adapts to you

Practise this lesson

7 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 7 What does a LiDAR point's intensity record?

    Choose one answer.

    Show the answer: Question 1 of 7

    Answer: The strength of the echo that made the point

    Intensity helps tell surfaces apart, roofs from grass for example, but it depends on the sensor and the range, so it is compared within one survey.

  2. Question 2 of 7 Most of a flat roof's points are return 1 of 1. What does that say?

    Choose one answer.

    Show the answer: Question 2 of 7

    Answer: The pulses hit a solid surface and came back once

    Several returns mean the pulse passed through something, such as leaves; one return means it stopped at a solid surface.

  3. Question 3 of 7 A single point floats 60 m above an open field, with no other points near it. Which class should it get?

    Choose one answer.

    Show the answer: Question 3 of 7

    Answer: Class 18, high noise, such as a bird or haze

    Noise has classes of its own, 7 below the ground and 18 above it, so that terrain and surface models can leave it out.

  4. Question 4 of 7 Which point data record formats does LAS 1.5 keep?

    Choose one answer.

    Show the answer: Question 4 of 7

    Answer: Formats 6 to 10

    Format 6 is the base of the kept formats, 30 bytes a point; formats 7 to 10 add colour, near-infrared and waveform data.

  5. Question 5 of 7 A team stores a survey as LAZ to save space. Does that change any point?

    Choose one answer.

    Show the answer: Question 5 of 7

    Answer: No, LAZ compresses LAS losslessly, to about 7 to 20 % of its size

    LAZ is the usual way to store and exchange LAS; the space it saves costs only decompression time.

  6. Question 6 of 7 How is a PDAL pipeline written?

    Choose one answer.

    Show the answer: Question 6 of 7

    Answer: As a JSON array of stages, readers, filters and writers, in the order they run

    Stages run in order, so a pipeline reads a file, filters it and writes the result, all described in one JSON document.

  7. Question 7 of 7 A web viewer must show a 40 GB survey without downloading it. Which form of the data fits?

    Choose one answer.

    Show the answer: Question 7 of 7

    Answer: COPC, whose octree lets the viewer fetch only the chunks for the view, coarse levels first

    Cloud-optimised forms put an index first and the data in chunks. COPC does for point clouds what COG does for rasters.

Interview questions

Warm-up (fresher): What do a LiDAR point’s return number and number of returns tell you?

A pulse can echo several times on its way down. The number of returns says how many echoes the pulse gave, and the return number which of them this point is: 1 of 3 is the first, usually a crown or a roof edge, and 3 of 3 the last, often the ground under a tree. A single return, 1 of 1, is a solid surface: open ground, a roof or water.

Three longer questions start from this lesson, from a terrain model with a flat-topped hill to a survey that must stay in India; the bank gives what each tests and a first step:

  1. Mid to senior

    A terrain model made from a city's LiDAR survey has flat-topped hills where the largest factories stand, and the drainage model built on it sends water round them. What went wrong, and how do you fix the classification?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    Tree heights from a LiDAR canopy height model of a plantation come out 2 to 4 m lower than heights measured in the field. Where can the difference come from, and how do you tell which source it is?

    Model answer, Pro. First step and follow-ups

  3. Senior to staff

    A foreign consultancy asks your Indian firm to fly drone LiDAR over a town, classify the points and upload the cloud to the consultancy's storage abroad for analysis. What do you tell them, and how do you deliver the project?

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What do a LiDAR point's return number and number of returns tell you?

    The number of returns says how many echoes the pulse gave, and the return number which echo this point is: 1 of 3 is usually a crown or a roof edge, 3 of 3 often the ground under a tree, and 1 of 1 a solid surface such as open ground, a roof or water.

  2. Which ASPRS class codes mark ground, vegetation, buildings, noise and water in a LAS file?

    1 unclassified, 2 ground, 3 to 5 low, medium and high vegetation, 6 building, 7 low point (noise), 9 water and 18 high noise.

  3. How does a LAS file store a point's coordinates?

    As whole numbers turned into metres by the header's scale and offset, x = stored × scale + offset: 254 becomes 374,702.54 m at a scale of 0.01 and an offset of 374,700. In point format 6 a record takes 30 bytes, and one byte holds the return number and the number of returns, 4 bits each.

  4. What do LAZ and COPC add to LAS?

    LAZ compresses LAS without loss, to only 7 to 20 % of its size by its makers' account. COPC is a LAZ 1.4 file whose points are clustered in an octree with a hierarchy, so a viewer fetches only the parts it shows, much as a COG does for rasters.

  5. Which tools read and process point clouds directly?

    QGIS opens LAS and LAZ files and builds an index the first time it loads one; PDAL processes them in pipelines, JSON arrays of stages that read, filter and write.

  6. What does a ground filter do, and what is built from its output?

    It keeps the points of the lowest surface and removes what stands on it, such as roofs and crowns; the morphological kind uses windows of growing size. Gridding the ground points gives a terrain model, and gridding the first returns a surface model of crowns and roofs.

Key takeaways

  • A LiDAR point keeps x, y and z with its return number, the pulse’s number of returns, an intensity and an ASPRS class such as 2 for ground and 6 for building.
  • LAS stores coordinates as scaled integers in records of 30 bytes for point format 6; LAZ compresses LAS without loss, and COPC orders LAZ in an octree for reading in parts.
  • Tools read point clouds directly: QGIS opens LAS and LAZ, and PDAL runs pipelines of readers, filters and writers.
  • A ground filter keeps the lowest surface and removes what stands on it, and gridding the ground points gives a terrain model.

Terms in this lesson

COPC (Cloud Optimized Point Cloud)
A LAZ 1.4 file whose points are organised in a clustered octree, with a hierarchy that tells a reader which chunk of the file holds which part of the cloud.
LiDAR
Light detection and ranging: measuring distances with laser pulses, timed from the moment each leaves the scanner to the moment each echo comes back. Airborne scanners turn the echoes into points of a point cloud.
point cloud
A set of points in three dimensions, each with x, y and z and attributes such as its return number, intensity and class, as a LiDAR scanner or photogrammetry produces them.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.