Upgrading LAS 1.2 Files to LAS 1.4
TL;DR: Rewrite with writers.las using "minor_version": 4 and "dataformat_id": 6 (or 7 if the source is PDRF 2 or 3 with colour), set a_srs so a WKT CRS record is written, and make sure GPS time is adjusted standard time before writing — LAS 1.4 formats 6–10 require it. Then verify point counts, class histograms and coordinates are unchanged.
# Context and Motivation
This guide is part of LAS/LAZ File Structure. Archives are full of LAS 1.2 data in formats 1 and 3: the standard for airborne deliveries for a decade. Modern specifications and tools expect LAS 1.4 with formats 6–10, for good reasons: the 8-bit classification makes room for classes 19–22 and beyond, the overlap flag replaces the old habit of abusing class 12, WKT replaces GeoTIFF keys for CRS, and extra bytes carry custom attributes cleanly. Upgrading is mostly mechanical, but four details — class mapping, flags, GPS time and CRS — decide whether the result is a correct LAS 1.4 file or just a relabelled 1.2 file.
# Prerequisites and Assumptions
- PDAL 2.x, or laspy 2.x, and pyproj.
- Knowledge of the source’s quirks: whether class 12 was used for overlap, whether GPS time is week time or adjusted standard time (global encoding bit 0), and what CRS it is really in.
- For week-time data, the GPS week of the flight, from the flight log or acquisition dates.
# Step-by-Step Implementation
# Step 1 — Inspect the source
pdal info --metadata gives minor_version, dataformat_id, global_encoding (bit 0 = 1 means adjusted standard time) and the CRS; pdal info --stats --enumerate Classification lists classes.
# Step 2 — Resolve overlap class 12
If class 12 marks overlap, you cannot recover the true class from the file. Keep it as 1 (unclassified) with the overlap flag set, or reclassify those points, then set the flag.
# Step 3 — Convert GPS time
If bit 0 of global encoding is 0, GPS time is seconds of the week. Convert with the flight’s GPS week, as described in converting GPS week time to adjusted standard time.
# Step 4 — Write LAS 1.4
writers.las with minor_version: 4, dataformat_id: 6 (or 7 with RGB), a_srs set to the correct compound CRS, and forward to carry other header settings.
# Step 5 — Verify
Counts, class histogram (apart from deliberate 12 → 1 changes), coordinate ranges and GPS time ranges must match expectations.
# Complete Working Example
A PDAL pipeline for a source that used class 12 for overlap and already has adjusted standard time:
{
"pipeline": [
{ "type": "readers.las", "filename": "legacy/t_0431_12.las" },
{ "type": "filters.assign", "value": [
"Overlap = 1 WHERE Classification == 12",
"Classification = 1 WHERE Classification == 12"
]},
{ "type": "writers.las", "filename": "upgraded/t_0431.laz",
"minor_version": 4, "dataformat_id": 6,
"a_srs": "EPSG:26915+5703",
"global_encoding": 17,
"forward": "scale_x,scale_y,scale_z,offset_x,offset_y,offset_z,system_id,software_id" }
]
}global_encoding: 17 sets bit 0 (adjusted standard GPS time) and bit 4 (WKT CRS), both expected for PDRF 6–10. The assignment order matters: set the flag while the class is still 12, then change the class.
A laspy version with a GPS-week conversion, for sources in week time:
"""Upgrade LAS 1.2 (PDRF 1/3) to LAS 1.4 (PDRF 6/7) with laspy."""
from __future__ import annotations
import laspy
import numpy as np
from pyproj import CRS
SECONDS_PER_WEEK = 604_800
def upgrade(src: str, dst: str, crs: str, gps_week: int | None = None) -> None:
las = laspy.read(src)
target = 7 if las.header.point_format.id in (2, 3, 5) else 6
new = laspy.convert(las, point_format_id=target, file_version="1.4")
overlap = np.asarray(las.classification) == 12
new.overlap = overlap.astype(np.uint8)
new.classification = np.where(overlap, 1, np.asarray(las.classification)).astype(np.uint8)
week_time = (int(las.header.global_encoding.gps_time_type) == 0)
if week_time:
if gps_week is None:
raise ValueError("source uses GPS week time; supply the flight's GPS week")
new.gps_time = gps_week * SECONDS_PER_WEEK + np.asarray(las.gps_time) - 1e9
new.header.global_encoding.gps_time_type = laspy.header.GpsTimeType.STANDARD
new.header.add_crs(CRS.from_user_input(crs))
new.write(dst)
if __name__ == "__main__":
upgrade("legacy/t_0431_12.las", "upgraded/t_0431.laz", "EPSG:26915+5703", gps_week=1987)# Key Parameter Table
| Setting | Value | Why |
|---|---|---|
minor_version |
4 | LAS 1.4 header |
dataformat_id |
6, 7 or 8 | 6 from PDRF 1, 7 from PDRF 3, 8 if NIR exists |
a_srs |
compound EPSG | Writes a WKT CRS record |
global_encoding |
17 | Adjusted standard time + WKT bits |
forward |
scales, offsets, IDs | Keeps precision and provenance fields |
| class 12 handling | overlap flag + class 1 | LAS 1.4 reserves 12 and uses the flag |
# Verification
- Counts identical. Source and output point counts match exactly.
- Histogram diff. Only class 12 → 1 moves; every other class count is unchanged.
- GPS time plausible. Adjusted standard time for flights in the 2010s and 2020s falls roughly between 0 and a few hundred million seconds; week time falls between 0 and 604,800. Check which range the output is in.
- CRS present.
pdal info --metadatashows a WKT CRS on the output.
# Gotchas and Edge Cases
Class 12 was not always overlap. Some vendors used 12 for other purposes. Confirm with the delivery documentation before converting it to an overlap flag.
Unknown GPS week. Week time cannot be converted without the week. If the flight date is unknown, the GPS times can still be kept consistent within the file, but they cannot be aligned with trajectories or other flights. Document the limitation.
CRS from GeoTIFF keys. Some 1.2 files carry incomplete GeoTIFF keys — horizontal only, or a user-defined projection. Resolve the true CRS from the delivery metadata, and set it explicitly rather than trusting the automatic translation.
Laspy convert drops fields. Converting PDRF 3 to PDRF 6 drops RGB. Choose 7 to keep colour; laspy will not warn.
# Frequently Asked Questions
How do I convert LAS 1.2 to LAS 1.4?
Rewrite the file with PDAL’s writers.las using minor_version 4 and a LAS 1.4 point format such as 6 or 7, or with laspy’s convert function. Handle class 12 overlap, GPS time type and the CRS explicitly while doing so.
Why does LAS 1.4 need adjusted standard GPS time?
Point formats 6 to 10 are defined with adjusted standard GPS time, a continuous timescale, so points from different weeks and flights can be ordered and matched to trajectories. Week time resets every week and is ambiguous without the week number.
What happens to class 12 overlap points?
In LAS 1.4, overlap is a flag rather than a class. Set the overlap flag on those points and assign them a real class, or class 1 if the true class is unknown.
Do I lose anything by upgrading?
Not if the target format holds every source field: 6 for format 1, 7 for format 3. Choosing a format without colour for a coloured source drops RGB silently, so check before converting.
# Related
- LAS/LAZ File Structure — the file layout
- Understanding LAS Point Data Record Formats — what each format holds
- Converting GPS Week Time to Adjusted Standard Time — the time conversion in detail
- Flagging Overlap and Withheld Points — flags in LAS 1.4
- Converting LAS to LAZ with PDAL — compression during the upgrade