pylas: Python library for lidar LAS/LAZ IO.¶
LAS (and it’s compressed counterpart LAZ), is a popular format for lidar pointcloud and full waveform, pylas reads and writes these formats and provides a Python API via Numpy Arrays.
Here is an example of reading in LAZ data and getting some simple summaries of the pointcloud:
import numpy as np
import pylas
with pylas.open('pylastests/simple.laz') as fh:
print('Points from Header:', fh.header.point_count)
las = fh.read()
print(las)
print('Points from data:', len(las.points))
ground_pts = las.classification == 2
bins, counts = np.unique(las.return_number[ground_pts], return_counts=True)
print('Ground Point Return Number distribution:')
for r,c in zip(bins,counts):
print(' {}:{}'.format(r,c))
Would output:
Points from Header: 1065
<LasData(1.2, point fmt: <PointFormat(3)>, 1065 points, 0 vlrs)>
Points from data: 1065
Ground Point Return Number distribution:
1:239
2:25
3:11
4:1
User Guide¶
Installation¶
Installing from PyPi¶
pip install pylas
Optional dependencies for LAZ support¶
pylas does not support LAZ (.laz) file by itself but can use one of several optional dependencies to support compressed LAZ files.
The 2 supported options are:
- lazrs [lazrs PyPi]
- laszip-python (bindings to laszip)
When encountering LAZ data, pylas will try to use one of the backend in the order described above. (Example: if lazrs is not installed or if it fails during, the process, pylas will try laszip)
lazrs is a Rust port of the laszip compression and decompression. Its main advantage is that it is able to compress/decompress using multiple threads which can greatly speed up things.
laszip is the official and original LAZ implementation by Martin Isenburg. The advantage of the laszip backend is that its the official implementation, but does not offer multi-threaded compression/decompression.
Both the laszip bindings and lazrs are available on pip.
To install pylas with one of its supported backend use one of the following commands
# To install with lazrs only
pip install pylas[lazrs]
# To install with laszip only
pip install pylas[laszip]
# To install with both
pip install pylas[lazrs,laszip]
What is a LAS file ?¶
LAS is a public file format meant to exchange 3D point data, mostly used to exchange lidar point clouds. LAZ is a lossless compression of the LAS format.
The latest LAS specification is the LAS 1.4. pylas supports LAS files from Version 1.2 to 1.4.
LAS files are organized in 3 main parts:
- Header
- VLRs
- Point Records
Header¶
The header contains information about the data such as its version, the point format (which tells the different dimensions stored for each points).
See Creating
VLRs¶
After the header, LAS files may contain VLRs (Variable Length Record). VLRs are meant to store additional information such as the SRS (Spatial Reference System), description on extra dimensions added to the points.
VLRs are divided in two parts:
- header
- payload
The payload is limited to 65,535 bytes (Because in the header, the length of the payload is stored on a uint16).
Point Records¶
The last chunk of data (and the biggest one) contains the point records. In a LAS file, points are stored sequentially.
The point records holds the point cloud data the LAS Spec specifies 10 point formats. A point format describe the dimensions stored for each point in the record.
Each LAS specification added new point formats, the table below describe the compatibility between point formats and LAS file version.
LAS file version | Compatible point formats |
---|---|
1.2 | 0, 1, 2, 3 |
1.3 | 0, 1, 2, 3, 4, 5 |
1.4 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 |
The names written in the tables below are the one you will have to use in your code.
Note
The dimensions ‘X’, ‘Y’, ‘Z’ are signed integers without the scale and offset applied. To access the coordinates as doubles simply use ‘x’, ‘y’ , ‘z’
Point Format 0¶
Dimensions | Type | Size (bit) |
---|---|---|
X | signed | 32 |
Y | signed | 32 |
Z | signed | 32 |
intensity | unsigned | 16 |
return_number | unsigned | 3 |
number_of_returns | unsigned | 3 |
scan_direction_flag | bool | 1 |
edge_of_flight_line | bool | 1 |
classification | unsigned | 5 |
synthetic | bool | 1 |
key_point | bool | 1 |
withheld | bool | 1 |
scan_angle_rank | signed | 8 |
user_data | unsigned | 8 |
point_source_id | unsigned | 8 |
The point formats 1, 2, 3, 4, 5 are based on the point format 0, meaning that they have the same dimensions plus some additional dimensions:
Point Format 1¶
Added dimensions | Type | Size (bit) |
---|---|---|
gps_time | Floating | 64 |
Point Format 2¶
Added dimensions | Type | Size (bit) |
---|---|---|
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
Point Format 3¶
Added dimensions | Type | Size (bit) |
---|---|---|
gps_time | Floating | 64 |
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
Point Format 4¶
Added dimensions | Type | Size (bit) |
---|---|---|
gps_time | Floating | 64 |
wavepacket_index | unsigned | 8 |
wavepacket_offset | unsigned | 64 |
wavepacket_size | unsigned | 32 |
return_point_wave_location | unsigned | 32 |
x_t | floating | 32 |
y_t | floating | 32 |
z_t | floating | 32 |
Point Format 5¶
Added dimensions | Type | Size (bit) |
---|---|---|
gps_time | Floating | 64 |
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
wavepacket_index | unsigned | 8 |
wavepacket_offset | unsigned | 64 |
wavepacket_size | unsigned | 32 |
return_point_wave_location | unsigned | 32 |
x_t | floating | 32 |
y_t | floating | 32 |
z_t | floating | 32 |
Point Format 6¶
The Point Format 6, is the new base point format (6, 7, 8, 9, 10) introduced in the LAS specification 1.4. The main modifications from point format 0 and point format 6 are that now the gps_time is baseline and some fields takes more bits, for example the classification is now stored on 8 bits (previously 5).
Dimensions | Type | Size (bit) |
---|---|---|
X | signed | 32 |
Y | signed | 32 |
Z | signed | 32 |
intensity | unsigned | 16 |
return_number | unsigned | 4 |
number_of_returns | unsigned | 4 |
synthetic | bool | 1 |
key_point | bool | 1 |
withheld | bool | 1 |
overlap | bool | 1 |
scanner_channel | unsigned | 2 |
scan_direction_flag | bool | 1 |
edge_of_flight_line | bool | 1 |
classification | unsigned | 8 |
user_data | unsigned | 8 |
scan_angle_rank | signed | 16 |
point_source_id | unsigned | 8 |
gps_time | Floating | 64 |
Point Format 7¶
Add RGB to point format 6.
Added dimensions | Type | Size (bit) |
---|---|---|
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
Point Format 8¶
Adds RGB and Nir (Near Infrared) to point format 6.
Added dimensions | Type | Size (bit) |
---|---|---|
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
nir | unsigned | 16 |
Point Format 9¶
Add waveform data to points
Added dimensions | Type | Size (bit) |
---|---|---|
wavepacket_index | unsigned | 8 |
wavepacket_offset | unsigned | 64 |
wavepacket_size | unsigned | 32 |
return_point_wave_location | unsigned | 32 |
x_t | floating | 32 |
y_t | floating | 32 |
z_t | floating | 32 |
Point Format 10¶
Adds RGB, Nir (near infrared), waveform data to point format 6
Added dimensions | Type | Size (bit) |
---|---|---|
red | unsigned | 16 |
green | unsigned | 16 |
blue | unsigned | 16 |
nir | unsigned | 16 |
wavepacket_index | unsigned | 8 |
wavepacket_offset | unsigned | 64 |
wavepacket_size | unsigned | 32 |
return_point_wave_location | unsigned | 32 |
x_t | floating | 32 |
y_t | floating | 32 |
z_t | floating | 32 |
EVLRs¶
Version 1.4 of the LAS specification added a last block following the point records: EVLRs (Extended Variable Length Record) which are the same thing as VLRs but they can carry a higher payload (length of the payload is stored on a uint64)
Basic Manipulation¶
Opening & Reading¶
Reading¶
Reading is done using pylas.read()
function.
This function will read everything in the file (Header, vlrs, point records, …) and return an object
that you can use to access to the data.
import pylas
las = pylas.read('somefile.las')
print(np.unique(las.classification))
Opening¶
pylas can also pylas.open()
files reading just the header and vlrs but not the points, this is useful
if you are interested in metadata that are contained in the header and do not need to read the points.
import s3fs
import pylas
fs = s3fs.S3FileSystem()
with fs.open('my-bucket/some_file.las', 'rb') as f:
if f.header.point_count < 100_000_000:
las = pylas.read(f)
Chunked reading¶
Sometimes files are big, too big to be read entirely and fit into your RAM.
The object returned by the pylas.open()
function, LasReader
can also be used to read points chunk by chunk by using LasReader.chunk_iterator()
, which will allow you to do some
processing on large files (splitting, filtering, etc)
import pylas
with pylas.open("some_big_file.laz") as f:
for points in f.chunk_iterator(1_000_000):
do_something_with(points)
Writing¶
To be able to write a las file you will need a LasData
.
You obtain this type of object by using one of the function described in the section above
use its method LasData.write()
to write to a file or a stream.
Chunked Writing¶
Similar to LasReader
there exists a way to write a file
chunk by chunk.
import pylas
with pylas.open("some_big_file.laz") as f:
with pylas.open("grounds.laz", mode="w", header=f.header) as writer:
for points in f.chunk_iterator(1_234_567):
writer.write_points(points[points.classification == 2])
Creating¶
Creating a new Las from scratch is simple.
Use pylas.create()
.
Converting¶
pylas also offers the ability to convert a file between the different version and point format available (as long as they are compatible).
To convert, use the pylas.convert()
Accessing the file header¶
You can access the header of a las file you read or opened by retrieving the ‘header’ attribute:
>>> import pylas
>>> las = pylas.read('pylastests/simple.las')
>>> las.header
<LasHeader(1.2, <PointFormat(3, 0 bytes of extra dims)>)>
>>> las.header.point_count
1065
>>> with pylas.open('pylastests/simple.las') as f:
... f.header.point_count
1065
you can see the accessible fields in LasHeader
.
Accessing Points Records¶
To access point records using the dimension name, you have 2 options:
- regular attribute access using the las.dimension_name syntax
- dict-like attribute access las[dimension_name].
>>> import numpy as np
>>> las = pylas.read('pylastests/simple.las')
>>> np.all(las.user_data == las['user_data'])
True
Point Format¶
The dimensions available in a file are dictated by the point format id. The tables in the introduction section contains the list of dimensions for each of the point format. To get the point format of a file you have to access it through the las object:
>>> point_format = las.point_format
>>> point_format
<PointFormat(3, 0 bytes of extra dims)>
>>> point_format.id
3
If you don’t want to remember the dimensions for each point format, you can access the list of available dimensions in the file you read just like that:
>>> list(point_format.dimension_names)
['X', 'Y', 'Z', 'intensity', 'return_number', 'number_of_returns', 'scan_direction_flag', 'edge_of_flight_line', 'classification', 'synthetic', 'key_point', 'withheld', 'scan_angle_rank', 'user_data', 'point_source_id', 'gps_time', 'red', 'green', 'blue']
This gives you all the dimension names, including extra dimensions if any. If you wish to get only the extra dimension names the point format can give them to you:
>>> list(point_format.standard_dimension_names)
['X', 'Y', 'Z', 'intensity', 'return_number', 'number_of_returns', 'scan_direction_flag', 'edge_of_flight_line', 'classification', 'synthetic', 'key_point', 'withheld', 'scan_angle_rank', 'user_data', 'point_source_id', 'gps_time', 'red', 'green', 'blue']
>>> list(point_format.extra_dimension_names)
[]
>>> las = pylas.read('pylastests/extrabytes.las')
>>> list(las.point_format.extra_dimension_names)
['Colors', 'Reserved', 'Flags', 'Intensity', 'Time']
You can also have more information:
>>> point_format[3].name
'intensity'
>>> point_format[3].num_bits
16
>>> point_format[3].kind
<DimensionKind.UnsignedInteger: 1>
>>> point_format[3].max
65535
Manipulating VLRs¶
To access the VLRs stored in a file, simply access the vlr member of the las object.
>>> las = pylas.read('pylastests/extrabytes.las')
>>> las.vlrs
[<ExtraBytesVlr(extra bytes structs: 5)>]
>>> with pylas.open('pylastests/extrabytes.las') as f:
... f.header.vlrs
[<ExtraBytesVlr(extra bytes structs: 5)>]
To retrieve a particular vlr from the list there are 2 ways: VLRList.get()
and
VLRList.get_by_id()
Examples¶
Filtering¶
This example shows how you can extract points from a file and write them into a new one. We use the classification field to filter points, but this can work with the other fields.
import pylas
las = pylas.read('pylastests/simple.las')
new_file = pylas.create(point_format=las.header.point_format_id, file_version=las.header.version)
new_file.points = las.points[las.classification == 1]
new_file.write('extracted_points.las')
Creating from scratch¶
This example shows how you can create a new LAS file from scratch.
import pylas
import numpy as np
las = pylas.create()
array = np.linspace(0.0, 15.0, 10000)
las.x = array
las.y = array
las.z = array
las.write('diagonal.las')
Using chunked reading & writing¶
This example shows how to use the ‘chunked’ reading and writing feature to split potentially large LAS/LAZ file into multiple smaller file.
import argparse
import sys
from pathlib import Path
from typing import List
from typing import Optional
import numpy as np
import pylas
def recursive_split(x_min, y_min, x_max, y_max, max_x_size, max_y_size):
x_size = x_max - x_min
y_size = y_max - y_min
if x_size > max_x_size:
left = recursive_split(x_min, y_min, x_min + (x_size // 2), y_max, max_x_size, max_y_size)
right = recursive_split(x_min + (x_size // 2), y_min, x_max, y_max, max_x_size, max_y_size)
return left + right
elif y_size > max_y_size:
up = recursive_split(x_min, y_min, x_max, y_min + (y_size // 2), max_x_size, max_y_size)
down = recursive_split(x_min, y_min + (y_size // 2), x_max, y_max, max_x_size, max_y_size)
return up + down
else:
return [(x_min, y_min, x_max, y_max)]
def tuple_size(string):
try:
return tuple(map(float, string.split("x")))
except:
raise ValueError("Size must be in the form of numberxnumber eg: 50.0x65.14")
def main():
parser = argparse.ArgumentParser("LAS recursive splitter", description="Splits a las file bounds recursively")
parser.add_argument("input_file")
parser.add_argument("output_dir")
parser.add_argument("size", type=tuple_size, help="eg: 50x64.17")
parser.add_argument("--points-per-iter", default=10**6, type=int)
args = parser.parse_args()
with pylas.open(sys.argv[1]) as file:
sub_bounds = recursive_split(
file.header.x_min,
file.header.y_min,
file.header.x_max,
file.header.y_max,
args.size[0],
args.size[1]
)
writers: List[Optional[pylas.LasWriter]] = [None] * len(sub_bounds)
try:
count = 0
for points in file.chunk_iterator(args.points_per_iter):
print(f"{count / file.header.point_count * 100}%")
# For performance we need to use copy
# so that the underlying arrays are contiguous
x, y = points.x.copy(), points.y.copy()
point_piped = 0
for i, (x_min, y_min, x_max, y_max) in enumerate(sub_bounds):
mask = (x >= x_min) & (x <= x_max) & (y >= y_min) & (y <= y_max)
if np.any(mask):
if writers[i] is None:
output_path = Path(sys.argv[2]) / f"output_{i}.laz"
writers[i] = pylas.open(output_path,
mode='w',
header=file.header)
sub_points = points[mask]
writers[i].write_points(sub_points)
point_piped += np.sum(mask)
if point_piped == len(points):
break
count += len(points)
print(f"{count / file.header.point_count * 100}%")
finally:
for writer in writers:
if writer is not None:
writer.close()
if __name__ == '__main__':
main()
Less Basic Things¶
Extra Dimensions¶
The LAS Specification version 1.4 defines a standard way to add extra dimensions to a LAS file.
In pylas you can add extra dimensions using the LasData.add_extra_dim()
function
The Allowed base types for an extra dimensions are:
pylas name | size (bits) | type |
---|---|---|
u1 or uint8 | 8 | unsigned |
i1 or int8 | 8 | signed |
u2 or uint16 | 16 | unsigned |
i2 or int16 | 16 | signed |
u4 or uint32 | 32 | unsigned |
i4 or int32 | 32 | signed |
u8 or uint64 | 64 | unsigned |
i8 or int64 | 64 | signed |
f4 or float32 | 32 | floating |
f8 or float64 | 64 | floating |
You can prepend the number ‘2’ or ‘3’ to one of the above base type to define an extra dimension that is array of 2 or 3 elements per points. Example: 3u2 -> each points will have an extra dimension that is an array of 3 * 16 bits
Here we are adding a new dimension called “codification” where each value is stored on a 64 bit unsigned integer and an array field of 3 doubles for each points.
import pylas
las = pylas.read("somefile.las")
las.add_extra_dim(pylas.ExtraBytesParams(
name="codification",
type="uint64",
description="More classes available"
))
las.add_extra_dim(pylas.ExtraBytesParams(name="mysterious", type="3f8"))
Note
Although the specification of the ExtraBytesVlr appeared in the 1.4 LAS Spec, pylas allows to add new dimensions to file with version < 1.4
Note
If you are adding multiple extra dimensions use LasData.add_extra_dims()
as it is more efficient (it allows to allocate all the dimensions at once instead
of re-allocating each time a new dimension is added.
Custom VLRs¶
Provided you have a valid user_id and record_id (meaning that they are not taken by a VLR described in the LAS specification) You can add you own VLRs to a file
Fast & Easy way¶
The fastest and easiest way to add your custom VLR to a file is to create a VLR
,
set its record_data (which must be bytes) and add it to the VLR list.
>>> import pylas
>>> vlr = pylas.vlrs.VLR(user_id='A UserId', record_id=1, description='Example VLR')
>>> vlr
<VLR(user_id: 'A UserId', record_id: '1', data len: 0)>
>>> vlr.record_data = b'somebytes'
>>> vlr
<VLR(user_id: 'A UserId', record_id: '1', data len: 9)>
>>> las = pylas.create()
>>> las.vlrs
[]
>>> las.vlrs.append(vlr)
>>> las.vlrs
[<VLR(user_id: 'A UserId', record_id: '1', data len: 9)>]
Complete & Harder way¶
While the way shown above is quick & easy it might not be perfect for complex VLRs. Also when reading a file that has custom VLR, pylas won’t be able to automatically parse its data into a better structure, so you will have to find the VLR in the vlrs list and parse it yourself one pylas is done.
One way to automate this task is to create your own Custom VLR Class that extends
BaseKnownVLR
by implementing the missing methods pylas
will be able to automatically parse the VLR when reading the file & write it when saving the file.
>>> class CustomVLR(pylas.vlrs.BaseKnownVLR):
... def __init__(self):
... super().__init__()
... self.numbers = []
...
... @staticmethod
... def official_user_id():
... return "CustomId"
...
... @staticmethod
... def official_record_ids():
... return 1,
...
... def record_data_bytes(self):
... return bytes(self.numbers)
...
... def parse_record_data(self, record_data):
... self.numbers = [b for b in record_data]
...
... def __repr__(self):
... return "<MyCustomVLR>"
>>> import numpy as np
>>> cvlr = CustomVLR()
>>> cvlr.numbers
[]
>>> cvlr.numbers = [1,2, 3]
>>> las = pylas.create()
>>> las.vlrs.append(cvlr)
>>> las.vlrs
[<MyCustomVLR>]
>>> las.x = np.array([1.0, 2.0])
>>> las = pylas.lib.write_then_read_again(las)
>>> las.vlrs
[<MyCustomVLR>]
>>> las.vlrs[0].numbers
[1, 2, 3]
Migration guides¶
From 0.4.x to 1.0.0¶
Changes in LAZ backend¶
With pylas 1.0.0, the lazperf backend support was dropped, and the laszip backend changed from using the laszip executable to using laszip python bindings.
If you used lazperf or relied on the laszip executable you’ll have to choose between the available backends. (see Installation section).
Changes in bit fields¶
Some fields in LAS are ‘bit fields’.
with pylas 0.4.x, there was a inconsistency between ‘normal’ fields and ‘bit’ fields, when getting a bit field, pylas returned a copy of the values in a new numpy array whereas when getting a normal field, the array you got acted as a ‘view’ on the real array where the values where stored.
That meant that modifying the values of the array you got from a bit field would no propagate to the real array.
import pylas
import numpy as np
las = pylas.read("pylastests/simple.las")
# return number is a bit field
print(las.return_number)
# array([1, 1, 1, ..., 1, 1, 1], dtype=uint8)
ascending_order = np.argsort(las.return_number)[::-1]
print(las.return_number[ascending_order])
# array([4, 4, 4, ..., 1, 1, 1], dtype=uint8)
las.return_number[:] = las.return_number[ascending_order]
print(las.return_number)
# array([1, 1, 1, ..., 1, 1, 1], dtype=uint8) # bif oof
las.return_number[0] = 7
print(las.return_number)
# array([1, 1, 1, ..., 1, 1, 1], dtype=uint8) # again value not updated
# To actually update you have to do
las.return_number = las.return_number[ascending_order]
print(las.return_number)
# array([4, 4, 4, ..., 1, 1, 1], dtype=uint8)
rn = las.return_number[ascending_order]
rn[0] = 7
las.return_number = rn
print(las.return_number)
# array([7, 4, 4, ..., 1, 1, 1], dtype=uint8)
In order to try to solve this inconsistency, pylas >= 0.5.0
introduced the SubFieldView
class that is meant to propagate
modifications to the real array, and tries to act like a real numpy array.
import pylas
import numpy as np
las = pylas.read("pylastests/simple.las")
print(las.return_number)
# <SubFieldView([1 1 1 ... 1 1 1])>
ascending_order = np.argsort(las.return_number)[::-1]
las.return_number[:] = las.return_number[ascending_order]
print(las.return_number)
# <SubFieldView([4 4 4 ... 1 1 1])>
las.return_number[0] = 7
print(las.return_number)
# <SubFieldView([7 4 4 ... 1 1 1])>
It may be possible that some operation on SubFieldView fail, in that case it is easy to copy them to numpy arrays:
import pylas
import numpy as np
las = pylas.read("pylastests/simple.las")
print(las.return_number)
# <SubFieldView([1 1 1 ... 1 1 1])>
array = np.array(las.return_number)
# array([1, 1, 1, ..., 1, 1, 1], dtype=uint8)
The logic is also the same for ‘Scaled dimensions’ such as x, y, z and scaled extra bytes, where a ScaledArrayView class has been introduced
import pylas
import numpy as np
las = pylas.read("pylastests/simple.las")
print(las.x)
# <ScaledArrayView([637012.24 636896.33 636784.74 ... 637501.67 637433.27 637342.85])>>
# ScaledArray view should behave as much as possible as a numpy array
# However if something breaks in your code when upgrading, and / or
# you need a true numpy array you can get one by doing
array = np.array(las.x)
# array([637012.24, 636896.33, 636784.74, ..., 637501.67, 637433.27,
# 637342.85])
Changes in extra bytes creation¶
The API to create extra bytes changed slightly, now the parameters needed
(and the optional ones) are coupled into ExtraBytesParams
Other changes¶
The points attribute of as LasData
used to return a numpy array
it now returns a PackedPointRecord
to get the same array as before,
use the array property of the point record.
# pylas <= 0.4.3
las = pylas.read("somefile.las")
array = las.points
# pylas 1.0.0
las = pylas.read("somefile.las")
array = las.points.array
API Documentation¶
pylas package¶
Re-exported functions¶
-
pylas.
read
(source, closefd=True, laz_backend=())¶ Entry point for reading las data in pylas
Reads the whole file into memory.
>>> las = read_las("pylastests/simple.las") >>> las.classification <SubFieldView([1 1 1 ... 1 1 1])>
Parameters: - source (str or io.BytesIO) – The source to read data from
- laz_backend (Optional, the backend to use when the file is as LAZ file.) – By default pylas will find the backend to use by himself. Use if you wan a specific backend to be used
- closefd (bool) – if True and the source is a stream, the function will close it after it is done reading
Returns: The object you can interact with to get access to the LAS points & VLRs
Return type: pylas.lasdatas.base.LasBase
-
pylas.
open
(source, mode='r', closefd=True, laz_backend=None, header=None, do_compress=None) → Union[pylas.lasreader.LasReader, pylas.laswriter.LasWriter, pylas.lasappender.LasAppender]¶ The pylas.open opens a LAS/LAZ file in one of the 3 supported mode:
- “r” => Reading => a
pylas.LasReader
will be returned - “w” => Writing => a
pylas.LasWriter
will be returned - “a” => Appending => a
pylas.LasAppender
will be returned
When opening a file in ‘w’ mode, a header (
pylas.LasHeader
) is required>>> with open_las('pylastests/simple.las') as f: ... print(f.header.point_format.id) 3
>>> f = open('pylastests/simple.las', mode='rb') >>> with open_las(f,closefd=False) as flas: ... print(flas.header) <LasHeader(1.2, <PointFormat(3, 0 bytes of extra dims)>)> >>> f.closed False >>> f.close() >>> f.closed True
>>> f = open('pylastests/simple.las', mode='rb') >>> with open_las(f) as flas: ... las = flas.read() >>> f.closed True
Parameters: - source (str or bytes or io.BytesIO) – if source is a str it must be a filename
- mode (Optional, the mode to open the file:) –
- “r” for reading (default)
- ”w” for writing
- ”a” for appending
- laz_backend (Optional, the LAZ backend to use to handle decompression/comression) – By default available backends are detected, see LazBackend to see the preference order when multiple backends are available
- header (The header to use when opening in write mode.) –
- do_compress (optional, bool, only meaningful in writing mode:) –
- None (default) guess if compression is needed using the file extension
or if a laz_backend was explicitely provided - True compresses the file - False do not compress the file
- closefd (optional, bool, True by default) – Whether the stream/file object shall be closed, this only work when using open_las in a with statement. An exception is raised if closefd is specified and the source is a filename
- “r” => Reading => a
-
pylas.
create
(*, point_format: Union[pylas.point.format.PointFormat, int, None] = None, file_version: Union[pylas.header.Version, str, None] = None)¶ Function to create a new empty las data object
Note
If you provide both point_format and file_version an exception will be raised if they are not compatible
>>> las = create_las(point_format=6,file_version="1.2") Traceback (most recent call last): ... pylas.errors.PylasError: Point format 6 is not compatible with file version 1.2
If you provide only the point_format the file_version will automatically selected for you.
>>> las = create_las(point_format=0) >>> las.header.version == '1.2' True
>>> las = create_las(point_format=PointFormat(6)) >>> las.header.version == '1.4' True
Parameters: - point_format – The point format you want the created file to have
- file_version – The las version you want the created las to have
Returns: A new las data object
Return type: pylas.lasdatas.base.LasBase
-
pylas.
convert
(source_las, *, point_format_id=None, file_version=None)[source]¶ Converts a Las from one point format to another Automatically upgrades the file version if source file version is not compatible with the new point_format_id
convert to point format 0
>>> las = read_las('pylastests/simple.las') >>> las.header.version Version(major=1, minor=2) >>> las = convert(las, point_format_id=0) >>> las.header.point_format.id 0 >>> str(las.header.version) '1.2'
convert to point format 6, which need version >= 1.4 then convert back to point format 0, version is not downgraded
>>> las = read_las('pylastests/simple.las') >>> str(las.header.version) '1.2' >>> las = convert(las, point_format_id=6) >>> las.header.point_format.id 6 >>> str(las.header.version) '1.4' >>> las = convert(las, point_format_id=0) >>> str(las.header.version) '1.4'
an exception is raised if the requested point format is not compatible with the file version
>>> las = read_las('pylastests/simple.las') >>> convert(las, point_format_id=6, file_version='1.2') Traceback (most recent call last): ... pylas.errors.PylasError: Point format 6 is not compatible with file version 1.2
Parameters: - source_las (pylas.lasdatas.base.LasBase) – The source data to be converted
- point_format_id (int, optional) – The new point format id (the default is None, which won’t change the source format id)
- file_version (str, optional,) – The new file version. None by default which means that the file_version may be upgraded for compatibility with the new point_format. The file version will not be downgraded.
Returns: Return type: pylas.lasdatas.base.LasBase
Re-exported classes¶
Submodules¶
pylas.lib module¶
‘Entry point’ of the library, Contains the various functions meant to be used directly by a user
-
pylas.lib.
open_las
(source, mode='r', closefd=True, laz_backend=None, header=None, do_compress=None) → Union[pylas.lasreader.LasReader, pylas.laswriter.LasWriter, pylas.lasappender.LasAppender][source]¶ The pylas.open opens a LAS/LAZ file in one of the 3 supported mode:
- “r” => Reading => a
pylas.LasReader
will be returned - “w” => Writing => a
pylas.LasWriter
will be returned - “a” => Appending => a
pylas.LasAppender
will be returned
When opening a file in ‘w’ mode, a header (
pylas.LasHeader
) is required>>> with open_las('pylastests/simple.las') as f: ... print(f.header.point_format.id) 3
>>> f = open('pylastests/simple.las', mode='rb') >>> with open_las(f,closefd=False) as flas: ... print(flas.header) <LasHeader(1.2, <PointFormat(3, 0 bytes of extra dims)>)> >>> f.closed False >>> f.close() >>> f.closed True
>>> f = open('pylastests/simple.las', mode='rb') >>> with open_las(f) as flas: ... las = flas.read() >>> f.closed True
Parameters: - source (str or bytes or io.BytesIO) – if source is a str it must be a filename
- mode (Optional, the mode to open the file:) –
- “r” for reading (default)
- ”w” for writing
- ”a” for appending
- laz_backend (Optional, the LAZ backend to use to handle decompression/comression) – By default available backends are detected, see LazBackend to see the preference order when multiple backends are available
- header (The header to use when opening in write mode.) –
- do_compress (optional, bool, only meaningful in writing mode:) –
- None (default) guess if compression is needed using the file extension
or if a laz_backend was explicitely provided - True compresses the file - False do not compress the file
- closefd (optional, bool, True by default) – Whether the stream/file object shall be closed, this only work when using open_las in a with statement. An exception is raised if closefd is specified and the source is a filename
- “r” => Reading => a
-
pylas.lib.
read_las
(source, closefd=True, laz_backend=())[source]¶ Entry point for reading las data in pylas
Reads the whole file into memory.
>>> las = read_las("pylastests/simple.las") >>> las.classification <SubFieldView([1 1 1 ... 1 1 1])>
Parameters: - source (str or io.BytesIO) – The source to read data from
- laz_backend (Optional, the backend to use when the file is as LAZ file.) – By default pylas will find the backend to use by himself. Use if you wan a specific backend to be used
- closefd (bool) – if True and the source is a stream, the function will close it after it is done reading
Returns: The object you can interact with to get access to the LAS points & VLRs
Return type: pylas.lasdatas.base.LasBase
-
pylas.lib.
create_las
(*, point_format: Union[pylas.point.format.PointFormat, int, None] = None, file_version: Union[pylas.header.Version, str, None] = None)[source]¶ Function to create a new empty las data object
Note
If you provide both point_format and file_version an exception will be raised if they are not compatible
>>> las = create_las(point_format=6,file_version="1.2") Traceback (most recent call last): ... pylas.errors.PylasError: Point format 6 is not compatible with file version 1.2
If you provide only the point_format the file_version will automatically selected for you.
>>> las = create_las(point_format=0) >>> las.header.version == '1.2' True
>>> las = create_las(point_format=PointFormat(6)) >>> las.header.version == '1.4' True
Parameters: - point_format – The point format you want the created file to have
- file_version – The las version you want the created las to have
Returns: A new las data object
Return type: pylas.lasdatas.base.LasBase
-
pylas.lib.
convert
(source_las, *, point_format_id=None, file_version=None)[source]¶ Converts a Las from one point format to another Automatically upgrades the file version if source file version is not compatible with the new point_format_id
convert to point format 0
>>> las = read_las('pylastests/simple.las') >>> las.header.version Version(major=1, minor=2) >>> las = convert(las, point_format_id=0) >>> las.header.point_format.id 0 >>> str(las.header.version) '1.2'
convert to point format 6, which need version >= 1.4 then convert back to point format 0, version is not downgraded
>>> las = read_las('pylastests/simple.las') >>> str(las.header.version) '1.2' >>> las = convert(las, point_format_id=6) >>> las.header.point_format.id 6 >>> str(las.header.version) '1.4' >>> las = convert(las, point_format_id=0) >>> str(las.header.version) '1.4'
an exception is raised if the requested point format is not compatible with the file version
>>> las = read_las('pylastests/simple.las') >>> convert(las, point_format_id=6, file_version='1.2') Traceback (most recent call last): ... pylas.errors.PylasError: Point format 6 is not compatible with file version 1.2
Parameters: - source_las (pylas.lasdatas.base.LasBase) – The source data to be converted
- point_format_id (int, optional) – The new point format id (the default is None, which won’t change the source format id)
- file_version (str, optional,) – The new file version. None by default which means that the file_version may be upgraded for compatibility with the new point_format. The file version will not be downgraded.
Returns: Return type: pylas.lasdatas.base.LasBase
pylas.header module¶
LasHeader¶
-
class
pylas.header.
LasHeader
(*, version: Union[pylas.header.Version, str, None] = None, point_format: Union[pylas.point.format.PointFormat, int, None] = None)[source]¶ Bases:
object
Contains the information from the header of as LAS file with ‘implementation’ field left out and ‘users’ field stored in more ergonomic classes.
This header also contains the VLRs
Examples
Creating a default header:
>>> header = LasHeader() >>> header <LasHeader(1.2, <PointFormat(3, 0 bytes of extra dims)>)>
Creating a header with the wanted version and point format:
>>> header = LasHeader(version=Version(1, 4), point_format=PointFormat(6)) >>> header <LasHeader(1.4, <PointFormat(6, 0 bytes of extra dims)>)>
>>> header = LasHeader(version="1.4", point_format=6) >>> header <LasHeader(1.4, <PointFormat(6, 0 bytes of extra dims)>)>
-
DEFAULT_VERSION
= Version(major=1, minor=2)¶ The default version used when None is given to init
-
DEFAULT_POINT_FORMAT
= <PointFormat(3, 0 bytes of extra dims)>¶ The default point format Used when None is given to init
-
file_source_id
= None¶ File source id
-
uuid
= None¶ Project ID Initialized to null UUID
-
system_identifier
= None¶ System identifier Initialized to ‘OTHER’
-
generating_software
= None¶ The software which generated the file Initialized to ‘pylas’
-
creation_date
= None¶ Day the file was created, initialized to date.today()
-
point_count
= None¶ The number of points in the file
-
scales
= None¶ The numbers used to scale the x,y,z coordinates
-
offsets
= None¶ The numbers used to offset the x,y,z coordinates
-
number_of_points_by_return
= None¶ Number of points by return for las <= 1.2 only the first 5 elements matters
-
vlrs
= None¶ The VLRS
-
extra_header_bytes
= None¶ Extra bytes between end of header and first vlrs
-
extra_vlr_bytes
= None¶ Extra bytes between end of vlr end first point
-
start_of_waveform_data_packet_record
= None¶ Las >= 1.3
-
start_of_first_evlr
= None¶ Las >= 1.4 Offset to the first evlr in the file
-
number_of_evlrs
= None¶ The number of evlrs in the file
-
point_format
¶ The point format
-
version
¶ The version
-
x_scale
¶
-
y_scale
¶
-
z_scale
¶
-
x_offset
¶
-
y_offset
¶
-
z_offset
¶
-
x_max
¶
-
y_max
¶
-
z_max
¶
-
x_min
¶
-
y_min
¶
-
z_min
¶
-
pylas.lasreader module¶
LasReader¶
-
class
pylas.lasreader.
LasReader
(source: BinaryIO, closefd: bool = True, laz_backend: Union[pylas.compression.LazBackend, Iterable[pylas.compression.LazBackend], None] = None)[source]¶ Bases:
object
The reader class handles LAS and LAZ via one of the supported backend
-
read_points
(n: int) → Optional[pylas.point.record.ScaleAwarePointRecord][source]¶ Read n points from the file
If there are no points left to read, returns None.
Parameters: n (The number of points to read) – if n is less than 0, this function will read the remaining points
-
pylas.lasdata module¶
LasData¶
-
class
pylas.lasdata.
LasData
(header: pylas.header.LasHeader, points: Optional[pylas.point.record.PackedPointRecord] = None)[source]¶ Bases:
object
Class synchronizing all the moving parts of LAS files.
It connects the point record, header, vlrs together.
To access points dimensions using this class you have two possibilities
las = pylas.read('some_file.las') las.classification # or las['classification']
-
x
¶ Returns the scaled x positions of the points as doubles
-
y
¶ Returns the scaled y positions of the points as doubles
-
z
¶ Returns the scaled z positions of the points as doubles
-
point_format
¶ Shortcut to get the point format
-
points
¶ Returns the point record
-
vlrs
¶
-
add_extra_dim
(params: pylas.point.format.ExtraBytesParams) → None[source]¶ Adds a new extra dimension to the point record
Note
If you plan on adding multiple extra dimensions, prefer
add_extra_dims()
as it will save re-allocations and data copyParameters: params (ExtraBytesParams) – parameters of the new extra dimension to add
-
add_extra_dims
(params: List[pylas.point.format.ExtraBytesParams]) → None[source]¶ Add multiple extra dimensions at once
Parameters: params (list of parameters of the new extra dimensions to add) –
-
update_header
() → None[source]¶ Update the information stored in the header to be in sync with the actual data.
This method is called automatically when you save a file using
pylas.lasdatas.base.LasBase.write()
-
write
(destination, do_compress=None, laz_backend=None)[source]¶ Writes to a stream or file
Note
When destination is a string, it will be interpreted as the path were the file should be written to, and whether the file will be compressed depends on the extension used (case insensitive):
- .laz -> compressed
- .las -> uncompressed
And the do_compress option will be ignored
Parameters:
-
pylas.vlrs.vlrlist module¶
-
class
pylas.vlrs.vlrlist.
VLRList
(*args, **kwargs)[source]¶ Bases:
list
Class responsible for managing the vlrs
-
index
(value, start: int = 0, stop: int = None) → int[source]¶ Return first index of value.
Raises ValueError if the value is not present.
-
get_by_id
(user_id='', record_ids=(None, ))[source]¶ Function to get vlrs by user_id and/or record_ids. Always returns a list even if only one vlr matches the user_id and record_id
>>> import pylas >>> from pylas.vlrs.known import ExtraBytesVlr, WktCoordinateSystemVlr >>> las = pylas.read("pylastests/extrabytes.las") >>> las.vlrs [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get(WktCoordinateSystemVlr.official_user_id()) [] >>> las.vlrs.get(WktCoordinateSystemVlr.official_user_id())[0] Traceback (most recent call last): IndexError: list index out of range >>> las.vlrs.get_by_id(ExtraBytesVlr.official_user_id()) [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get_by_id(ExtraBytesVlr.official_user_id())[0] <ExtraBytesVlr(extra bytes structs: 5)>
Parameters: - user_id (str, optional) – the user id
- record_ids (iterable of int, optional) – THe record ids of the vlr(s) you wish to get
Returns: a list of vlrs matching the user_id and records_ids
Return type:
-
get
(vlr_type: str) → List[pylas.vlrs.known.IKnownVLR][source]¶ Returns the list of vlrs of the requested type Always returns a list even if there is only one VLR of type vlr_type.
>>> import pylas >>> las = pylas.read("pylastests/extrabytes.las") >>> las.vlrs [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get("WktCoordinateSystemVlr") [] >>> las.vlrs.get("WktCoordinateSystemVlr")[0] Traceback (most recent call last): IndexError: list index out of range >>> las.vlrs.get('ExtraBytesVlr') [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get('ExtraBytesVlr')[0] <ExtraBytesVlr(extra bytes structs: 5)>
Parameters: vlr_type (str) – the class name of the vlr Returns: a List of vlrs matching the user_id and records_ids Return type: list
-
extract
(vlr_type: str) → List[pylas.vlrs.known.IKnownVLR][source]¶ Returns the list of vlrs of the requested type The difference with get is that the returned vlrs will be removed from the list
Parameters: vlr_type (str) – the class name of the vlr Returns: a List of vlrs matching the user_id and records_ids Return type: list
-
classmethod
read_from
(data_stream: BinaryIO, num_to_read: int, extended: bool = False) → pylas.vlrs.vlrlist.VLRList[source]¶ Reads vlrs and parse them if possible from the stream
Parameters: - data_stream (io.BytesIO) – stream to read from
- num_to_read (int) – number of vlrs to be read
- extended (bool) – whether the vlrs are regular vlr or extended vlr
Returns: List of vlrs
Return type:
-
pylas.vlrs.known module¶
The definition of the VLR Header, VLR, the KnownVLRs are in this module.
A KnownVLR is a VLR for which we know how to parse its record_data
-
class
pylas.vlrs.known.
IKnownVLR
[source]¶ Bases:
abc.ABC
Interface that any KnownVLR must implement. A KnownVLR is a VLR for which we know how to parse its record_data
Implementing this interfaces allows to automatically call the right parser for the right VLR when reading them.
-
static
official_user_id
() → str[source]¶ Shall return the official user_id as described in the documentation
-
static
official_record_ids
() → Tuple[int, ...][source]¶ Shall return the official record_id for the VLR
Note
Even if the VLR has one record_id, the return type must be a tuple
Returns: The record_ids this VLR type can have Return type: tuple of int
-
static
-
class
pylas.vlrs.known.
BaseKnownVLR
(record_id=None, description='')[source]¶ Bases:
pylas.vlrs.vlr.BaseVLR
,pylas.vlrs.known.IKnownVLR
,abc.ABC
Base Class to factorize common code between the different type of Known VLRs
-
class
pylas.vlrs.known.
ClassificationLookupVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
This vlr maps class numbers to short descriptions / names
>>> lookup = ClassificationLookupVlr() >>> lookup[0] = "never_classified" >>> lookup[2] = "ground" >>> lookup[0] 'never_classified'
-
parse_record_data
(record_data: bytes) → None[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
() → bytes[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
-
class
pylas.vlrs.known.
LasZipVlr
(data: bytes)[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
Contains the information needed by laszip (or any other laz backend) to compress the point records.
-
parse_record_data
(record_data: bytes) → None[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
() → bytes[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
static
official_user_id
() → str[source]¶ Shall return the official user_id as described in the documentation
-
-
class
pylas.vlrs.known.
ExtraBytesStruct
[source]¶ Bases:
_ctypes.Structure
-
NO_DATA_BIT_MASK
= 1¶
-
MIN_BIT_MASK
= 2¶
-
MAX_BIT_MASK
= 4¶
-
SCALE_BIT_MASK
= 8¶
-
OFFSET_BIT_MASK
= 16¶
-
no_data
¶
-
min
¶
-
max
¶
-
offset
¶
-
scale
¶
-
data_type
¶ Structure/Union member
-
description
¶ Structure/Union member
-
name
¶ Structure/Union member
-
options
¶ Structure/Union member
-
reserved
¶ Structure/Union member
-
unused
¶ Structure/Union member
-
-
class
pylas.vlrs.known.
ExtraBytesVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
-
parse_record_data
(data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
-
class
pylas.vlrs.known.
WaveformPacketStruct
[source]¶ Bases:
_ctypes.Structure
-
bits_per_sample
¶ Structure/Union member
-
digitizer_gain
¶ Structure/Union member
-
digitizer_offset
¶ Structure/Union member
-
number_of_samples
¶ Structure/Union member
-
temporal_sample_spacing
¶ Structure/Union member
-
waveform_compression_type
¶ Structure/Union member
-
-
class
pylas.vlrs.known.
WaveformPacketVlr
(record_id, description='')[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
static
official_record_ids
()[source]¶ Shall return the official record_id for the VLR
Note
Even if the VLR has one record_id, the return type must be a tuple
Returns: The record_ids this VLR type can have Return type: tuple of int
-
-
class
pylas.vlrs.known.
GeoKeyEntryStruct
[source]¶ Bases:
_ctypes.Structure
-
count
¶ Structure/Union member
-
id
¶ Structure/Union member
-
tiff_tag_location
¶ Structure/Union member
-
value_offset
¶ Structure/Union member
-
-
class
pylas.vlrs.known.
GeoKeysHeaderStructs
[source]¶ Bases:
_ctypes.Structure
-
key_direction_version
¶ Structure/Union member
-
key_revision
¶ Structure/Union member
-
minor_revision
¶ Structure/Union member
-
number_of_keys
¶ Structure/Union member
-
-
class
pylas.vlrs.known.
GeoKeyDirectoryVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
-
class
pylas.vlrs.known.
GeoDoubleParamsVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
-
class
pylas.vlrs.known.
GeoAsciiParamsVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
-
class
pylas.vlrs.known.
WktMathTransformVlr
[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
- From the Spec:
- Note that the math transform WKT record is added for completeness, and a coordinate system WKT may or may not require a math transform WKT record
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
class
pylas.vlrs.known.
WktCoordinateSystemVlr
(wkt_string='')[source]¶ Bases:
pylas.vlrs.known.BaseKnownVLR
Replaces Coordinates Reference System for new las files (point fmt >= 5) “LAS is not using the “ESRI WKT”
-
parse_record_data
(record_data)[source]¶ Shall parse the given record_data into a user-friendlier structure
Parameters: record_data (bytes) – The record_data bytes read from the file
-
record_data_bytes
()[source]¶ Shall return the bytes corresponding to the record_data part of the VLR as they should be written in the file.
Returns: The bytes of the vlr’s record_data Return type: bytes
-
pylas.vlrs.vlr¶
pylas.point.record module¶
Contains the classes that manages Las PointRecords Las PointRecords are represented using Numpy’s structured arrays, The PointRecord classes provide a few extra things to manage these arrays in the context of Las point data
-
pylas.point.record.
raise_not_enough_bytes_error
(expected_bytes_len, missing_bytes_len, point_data_buffer_len, points_dtype) → NoReturn[source]¶
-
class
pylas.point.record.
PackedPointRecord
(data: numpy.ndarray, point_format: pylas.point.format.PointFormat)[source]¶ Bases:
object
In the PackedPointRecord, fields that are a combinations of many sub-fields (fields stored on less than a byte) are still packed together and are only de-packed and re-packed when accessed.
This uses of less memory than if the sub-fields were unpacked
>>> #return number is a sub-field >>> from pylas import PointFormat >>> packed_point_record = PackedPointRecord.zeros(PointFormat(0), 10) >>> return_number = packed_point_record['return_number'] >>> return_number <SubFieldView([0 0 0 0 0 0 0 0 0 0])> >>> return_number[:] = 1 >>> np.alltrue(packed_point_record['return_number'] == 1) True
-
point_size
¶ Returns the point size in bytes taken by each points of the record
Returns: The point size in byte Return type: int
-
classmethod
zeros
(point_format, point_count)[source]¶ Creates a new point record with all dimensions initialized to zero
Parameters: - point_format (PointFormat) – The point format id the point record should have
- point_count (int) – The number of point the point record should have
Returns: Return type:
-
classmethod
empty
(point_format)[source]¶ Creates an empty point record.
Parameters: point_format (pylas.PointFormat) – The point format id the point record should have Returns: Return type: PackedPointRecord
-
classmethod
from_point_record
(other_point_record: pylas.point.record.PackedPointRecord, new_point_format: pylas.point.format.PointFormat) → pylas.point.record.PackedPointRecord[source]¶ Construct a new PackedPointRecord from an existing one with the ability to change to point format while doing so
-
-
pylas.point.record.
apply_new_scaling
(record, scales: numpy.ndarray, offsets: numpy.ndarray) → None[source]¶
pylas.errors module¶
All the custom exceptions types
-
exception
pylas.errors.
UnknownExtraType
[source]¶ Bases:
pylas.errors.PylasError
-
exception
pylas.errors.
PointFormatNotSupported
[source]¶ Bases:
pylas.errors.PylasError
-
exception
pylas.errors.
FileVersionNotSupported
[source]¶ Bases:
pylas.errors.PylasError
-
exception
pylas.errors.
LazError
[source]¶ Bases:
pylas.errors.PylasError
-
exception
pylas.errors.
IncompatibleDataFormat
[source]¶ Bases:
pylas.errors.PylasError
pylas.compression module¶
pylas.point.format module¶
-
class
pylas.point.format.
ExtraBytesParams
(name: str, type: str, description: str = '', offsets: Optional[numpy.ndarray] = None, scales: Optional[numpy.ndarray] = None)[source]¶ Bases:
object
All parameters needed to create extra bytes
-
name
= None¶ The name of the extra dimension
-
type
= None¶ The type of the extra dimension
-
description
= None¶ A description of the extra dimension
-
offsets
= None¶ The offsets to use if its a ‘scaled dimension’, can be none
-
scales
= None¶ The scales to use if its a ‘scaled dimension’, can be none
-
-
class
pylas.point.format.
PointFormat
(point_format_id: int)[source]¶ Bases:
object
Class that contains the informations about the dimensions that forms a PointFormat.
A PointFormat has ‘standard’ dimensions (dimensions defined in the LAS standard, each point format has its set of dimensions), but it can also have extra (non-standard) dimensions defined by the user)
>>> fmt = PointFormat(3) >>> all(dim.is_standard for dim in fmt.dimensions) True >>> dim = fmt.dimension_by_name("classification") # or fmt["classification"] >>> dim.max 31 >>> dim.min 0 >>> dim.num_bits 5
-
standard_dimensions
¶ Returns an iterable of the standard dimensions
>>> fmt = PointFormat(0) >>> standard_dims = list(fmt.standard_dimensions) >>> len(standard_dims) 15 >>> standard_dims[4].name 'return_number'
-
extra_dimensions
¶
-
dimension_names
¶ Returns the names of the dimensions contained in the point format
-
standard_dimension_names
¶ Returns the names of the extra dimensions in this point format
-
extra_dimension_names
¶ Returns the names of the extra dimensions in this point format
-
size
¶ Returns the number of bytes (standard + extra) a point takes
>>> PointFormat(3).size 34
>>> fmt = PointFormat(3) >>> fmt.add_extra_dimension(ExtraBytesParams("codification", "uint64")) >>> fmt.size 42
-
num_standard_bytes
¶ Returns the number of bytes used by standard dims
>>> fmt = PointFormat(3) >>> fmt.add_extra_dimension(ExtraBytesParams("codification", "uint64")) >>> fmt.num_standard_bytes 34
-
num_extra_bytes
¶ Returns the number of extra bytes
>>> fmt = PointFormat(3) >>> fmt.add_extra_dimension(ExtraBytesParams("codification", "uint64")) >>> fmt.num_extra_bytes 8
-
has_waveform_packet
¶ Returns True if the point format has waveform packet dimensions
-
dimension_by_name
(name: str) → pylas.point.dims.DimensionInfo[source]¶ Returns the dimension info for the dimension by name
ValueError is raised if the dimension does not exist un the point format
>>> info = PointFormat(2).dimension_by_name('number_of_returns') >>> info.name == 'number_of_returns' True >>> info.num_bits == 3 True
>>> info = PointFormat(2).dimension_by_name('gps_time') Traceback (most recent call last): ... ValueError: Dimension 'gps_time' does not exist
-
pylas.lasmmap module¶
-
class
pylas.lasmmap.
LasMMAP
(filename: Union[str, pathlib.Path])[source]¶ Bases:
pylas.lasdata.LasData
Memory map a LAS file. It works like a regular LasData however the data is not actually read in memory,
Access to dimensions are made directly from the file itself, changes made to the points are directly reflected in the mmap file.
Vlrs cannot be modified.
This can be useful if you want to be able to process a big LAS file
Note
A LAZ (compressed LAS) cannot be mmapped
pylas.lasappender¶
LasAppender¶
pylas.laswriter module¶
LasWriter¶
-
class
pylas.laswriter.
LasWriter
(dest: BinaryIO, header: pylas.header.LasHeader, do_compress: Optional[bool] = None, laz_backend: Union[pylas.compression.LazBackend, Iterable[pylas.compression.LazBackend], None] = None, closefd: bool = True)[source]¶ Bases:
object
Allows to write a complete LAS/LAZ file to the destination.