0
# Point Data Handling
1
2
Comprehensive point format management including standard LAS point formats, custom extra dimensions, and efficient point record processing with coordinate scaling. This module provides the foundation for all point data operations in laspy.
3
4
## Capabilities
5
6
### Point Format Management
7
8
Define and manage LAS point formats including standard formats and custom extra dimensions.
9
10
```python { .api }
11
class PointFormat:
12
def __init__(self, point_format_id: int):
13
"""
14
Create point format from standard LAS point format ID.
15
16
Parameters:
17
- point_format_id: int - Standard LAS point format (0-10)
18
"""
19
20
@property
21
def id(self) -> int: ...
22
@property
23
def dimensions(self) -> List[DimensionInfo]: ...
24
@property
25
def standard_dimensions(self) -> Iterable[DimensionInfo]: ...
26
@property
27
def extra_dimensions(self) -> Iterable[DimensionInfo]: ...
28
@property
29
def size(self) -> int: ... # Total point size in bytes
30
@property
31
def num_standard_bytes(self) -> int: ...
32
@property
33
def num_extra_bytes(self) -> int: ...
34
@property
35
def has_waveform_packet(self) -> bool: ...
36
37
def dimension_by_name(self, name: str) -> DimensionInfo:
38
"""Get dimension info by name."""
39
40
def add_extra_dimension(self, params: ExtraBytesParams):
41
"""Add custom extra dimension to point format."""
42
43
def remove_extra_dimension(self, name: str):
44
"""Remove extra dimension by name."""
45
46
def dtype(self) -> np.dtype:
47
"""Get NumPy dtype for this point format."""
48
```
49
50
**Usage Example:**
51
```python
52
import laspy
53
from laspy import PointFormat, ExtraBytesParams
54
55
# Create standard point format
56
fmt = PointFormat(3) # Point format 3 (X,Y,Z,Intensity,Returns,Classification,etc.)
57
print(f"Format {fmt.id} has {len(fmt.dimensions)} dimensions")
58
print(f"Standard size: {fmt.num_standard_bytes} bytes")
59
60
# Add custom extra dimension
61
extra_param = ExtraBytesParams(
62
name="temperature",
63
type="f4", # 32-bit float
64
description="Temperature in Celsius"
65
)
66
fmt.add_extra_dimension(extra_param)
67
print(f"With extra dims: {fmt.size} bytes total")
68
69
# Get dimension info
70
temp_dim = fmt.dimension_by_name("temperature")
71
print(f"Temperature dimension: {temp_dim.kind}, {temp_dim.num_bits} bits")
72
```
73
74
### Extra Dimension Parameters
75
76
Define custom extra dimensions with proper typing, scaling, and metadata.
77
78
```python { .api }
79
class ExtraBytesParams:
80
def __init__(self, name: str, type, description="", offsets=None, scales=None, no_data=None):
81
"""
82
Define parameters for extra bytes dimension.
83
84
Parameters:
85
- name: str - Dimension name
86
- type: str, np.dtype, or type - Data type ('u1', 'f4', np.uint8, etc.)
87
- description: str - Human-readable description
88
- offsets: array-like - Offset values for scaling
89
- scales: array-like - Scale factors
90
- no_data: array-like - No-data sentinel values
91
"""
92
93
@property
94
def name(self) -> str: ...
95
@property
96
def type(self) -> np.dtype: ...
97
@property
98
def description(self) -> str: ...
99
@property
100
def offsets(self) -> Optional[np.ndarray]: ...
101
@property
102
def scales(self) -> Optional[np.ndarray]: ...
103
@property
104
def no_data(self) -> Optional[np.ndarray]: ...
105
```
106
107
**Usage Examples:**
108
```python
109
import numpy as np
110
from laspy import ExtraBytesParams
111
112
# Simple integer field
113
intensity_extra = ExtraBytesParams(
114
name="intensity_corrected",
115
type="u2", # 16-bit unsigned int
116
description="Radiometrically corrected intensity"
117
)
118
119
# Scaled float field
120
temperature = ExtraBytesParams(
121
name="temperature",
122
type="u1", # Store as uint8 to save space
123
description="Temperature in Celsius",
124
scales=np.array([0.1]), # Scale factor: stored_value * 0.1 = actual_value
125
offsets=np.array([-20.0]), # Offset: (stored_value * scale) + offset = actual_value
126
no_data=np.array([255]) # 255 = no data
127
)
128
129
# Multi-element field (RGB)
130
rgb_extra = ExtraBytesParams(
131
name="rgb_corrected",
132
type="3u1", # 3 uint8 values
133
description="Color-corrected RGB values"
134
)
135
```
136
137
### Dimension Information
138
139
Detailed metadata about point dimensions including data types, scaling, and validation.
140
141
```python { .api }
142
class DimensionInfo:
143
name: str
144
kind: DimensionKind
145
num_bits: int
146
num_elements: int
147
is_standard: bool
148
description: str
149
offsets: Optional[np.ndarray]
150
scales: Optional[np.ndarray]
151
no_data: Optional[np.ndarray]
152
153
@property
154
def num_bytes(self) -> int: ...
155
@property
156
def num_bytes_singular_element(self) -> int: ...
157
@property
158
def is_scaled(self) -> bool: ...
159
@property
160
def dtype(self) -> Optional[np.dtype]: ...
161
@property
162
def max(self): ...
163
@property
164
def min(self): ...
165
166
def type_str(self) -> Optional[str]: ...
167
168
@classmethod
169
def from_extra_bytes_param(cls, params: ExtraBytesParams) -> DimensionInfo: ...
170
@classmethod
171
def from_dtype(cls, name: str, dtype: np.dtype, is_standard=True, description="", offsets=None, scales=None) -> DimensionInfo: ...
172
@classmethod
173
def from_bitmask(cls, name: str, bit_mask: int, is_standard=False) -> DimensionInfo: ...
174
175
class DimensionKind(Enum):
176
SignedInteger = 0
177
UnsignedInteger = 1
178
FloatingPoint = 2
179
BitField = 3
180
181
@classmethod
182
def from_letter(cls, letter: str) -> DimensionKind: ...
183
def letter(self) -> Optional[str]: ...
184
```
185
186
### Packed Point Records
187
188
Raw packed point data containers providing direct access to binary point data.
189
190
```python { .api }
191
class PackedPointRecord:
192
def __init__(self, data: np.ndarray, point_format: PointFormat):
193
"""
194
Create packed point record from raw data array.
195
196
Parameters:
197
- data: np.ndarray - Raw point data
198
- point_format: PointFormat - Point format definition
199
"""
200
201
@property
202
def point_size(self) -> int: ...
203
@property
204
def array(self) -> np.ndarray: ...
205
@property
206
def point_format(self) -> PointFormat: ...
207
208
@staticmethod
209
def zeros(point_count, point_format) -> PackedPointRecord:
210
"""Create zero-initialized point record."""
211
212
@staticmethod
213
def empty(point_format) -> PackedPointRecord:
214
"""Create empty point record."""
215
216
@classmethod
217
def from_point_record(cls, other_point_record, new_point_format) -> PackedPointRecord:
218
"""Create from existing point record with format conversion."""
219
220
@classmethod
221
def from_buffer(cls, buffer, point_format, count=-1, offset=0) -> PackedPointRecord:
222
"""Create from memory buffer."""
223
224
def copy_fields_from(self, other_record: PackedPointRecord):
225
"""Copy compatible fields from another record."""
226
227
def copy(self) -> PackedPointRecord:
228
"""Create deep copy."""
229
230
def memoryview(self) -> memoryview:
231
"""Get memory view of underlying data."""
232
233
def resize(self, new_size: int):
234
"""Resize point record."""
235
236
def validate_dimension_name(self, key: str) -> DimensionNameValidity:
237
"""Validate dimension name."""
238
239
def __len__(self) -> int: ...
240
def __getitem__(self, key): ... # Access dimension by name or index
241
def __setitem__(self, key, value): ... # Set dimension values
242
def __getattr__(self, name): ... # Direct attribute access to dimensions
243
def __setattr__(self, name, value): ... # Direct attribute setting
244
```
245
246
**Usage Example:**
247
```python
248
import laspy
249
import numpy as np
250
251
# Create point format and empty record
252
point_format = laspy.PointFormat(3)
253
points = laspy.PackedPointRecord.zeros(1000, point_format)
254
255
# Set point data
256
points.x = np.random.uniform(0, 100, 1000).astype(np.int32)
257
points.y = np.random.uniform(0, 100, 1000).astype(np.int32)
258
points.z = np.random.uniform(0, 10, 1000).astype(np.int32)
259
points.classification = np.full(1000, 2, dtype=np.uint8) # Ground class
260
261
# Access data
262
print(f"Point count: {len(points)}")
263
print(f"X range: {points.x.min()} to {points.x.max()}")
264
265
# Copy and modify
266
subset = points.copy()
267
subset.resize(100) # Keep only first 100 points
268
```
269
270
### Scale-Aware Point Records
271
272
Point records with automatic coordinate scaling and transformation support.
273
274
```python { .api }
275
class ScaleAwarePointRecord(PackedPointRecord):
276
def __init__(self, array, point_format, scales, offsets):
277
"""
278
Create scale-aware point record with coordinate transformation.
279
280
Parameters:
281
- array: np.ndarray - Raw point data
282
- point_format: PointFormat - Point format definition
283
- scales: np.ndarray - Scale factors for X,Y,Z coordinates
284
- offsets: np.ndarray - Offset values for X,Y,Z coordinates
285
"""
286
287
@property
288
def scales(self) -> np.ndarray: ...
289
@property
290
def offsets(self) -> np.ndarray: ...
291
292
@staticmethod
293
def zeros(point_count, *, point_format=None, scales=None, offsets=None, header=None) -> ScaleAwarePointRecord:
294
"""Create zero-initialized scaled point record."""
295
296
@staticmethod
297
def empty(point_format=None, scales=None, offsets=None, header=None) -> ScaleAwarePointRecord:
298
"""Create empty scaled point record."""
299
300
def change_scaling(self, scales=None, offsets=None):
301
"""Update coordinate scaling parameters."""
302
```
303
304
**Usage Example:**
305
```python
306
import laspy
307
import numpy as np
308
309
# Create with coordinate scaling
310
scales = np.array([0.01, 0.01, 0.001]) # 1cm XY, 1mm Z precision
311
offsets = np.array([500000, 4000000, 0]) # UTM-like offsets
312
313
points = laspy.ScaleAwarePointRecord.zeros(
314
1000,
315
point_format=laspy.PointFormat(3),
316
scales=scales,
317
offsets=offsets
318
)
319
320
# Set scaled coordinates (automatically converted to/from raw integers)
321
points.x = np.random.uniform(500000, 501000, 1000) # Real-world coordinates
322
points.y = np.random.uniform(4000000, 4001000, 1000)
323
points.z = np.random.uniform(100, 200, 1000)
324
325
# Access scaled values (real-world coordinates)
326
print(f"Real X range: {points.x.min():.2f} to {points.x.max():.2f}")
327
328
# Change scaling if needed
329
new_scales = np.array([0.001, 0.001, 0.0001]) # Higher precision
330
points.change_scaling(scales=new_scales)
331
```
332
333
### Array Views
334
335
Specialized array views for scaled coordinates and bit field access.
336
337
```python { .api }
338
class ArrayView:
339
"""Abstract base class for specialized array views."""
340
def __array__(self): ...
341
def __getitem__(self, key): ...
342
def __setitem__(self, key, value): ...
343
def copy(self) -> np.ndarray: ...
344
345
@property
346
def dtype(self): ...
347
@property
348
def shape(self): ...
349
@property
350
def ndim(self): ...
351
352
class ScaledArrayView(ArrayView):
353
def __init__(self, array: np.ndarray, scale, offset):
354
"""
355
Array view with automatic scaling/offset transformation.
356
357
Parameters:
358
- array: np.ndarray - Raw integer array
359
- scale: float or np.ndarray - Scale factor(s)
360
- offset: float or np.ndarray - Offset value(s)
361
"""
362
363
def scaled_array(self):
364
"""Get scaled floating-point array."""
365
366
class SubFieldView(ArrayView):
367
def __init__(self, array: np.ndarray, bit_mask):
368
"""
369
Array view for bit field access.
370
371
Parameters:
372
- array: np.ndarray - Raw array containing bit fields
373
- bit_mask: int - Bit mask for field extraction
374
"""
375
376
def masked_array(self):
377
"""Get underlying masked array."""
378
```
379
380
## Point Format Conversion
381
382
Utilities for converting between point formats and checking compatibility.
383
384
```python { .api }
385
def lost_dimensions(point_fmt_in, point_fmt_out) -> List[str]:
386
"""
387
Get list of dimension names that will be lost in format conversion.
388
389
Parameters:
390
- point_fmt_in: PointFormat - Source point format
391
- point_fmt_out: PointFormat - Target point format
392
393
Returns:
394
List[str]: Names of dimensions that will be lost
395
"""
396
```
397
398
**Usage Example:**
399
```python
400
import laspy
401
from laspy.point.format import lost_dimensions
402
403
# Check what gets lost converting from format 6 to format 3
404
fmt_6 = laspy.PointFormat(6) # Has GPS time, RGB
405
fmt_3 = laspy.PointFormat(3) # Basic format
406
407
lost = lost_dimensions(fmt_6, fmt_3)
408
print(f"Converting 6->3 loses: {lost}") # ['gps_time', 'red', 'green', 'blue']
409
410
# Check reverse conversion
411
lost_reverse = lost_dimensions(fmt_3, fmt_6)
412
print(f"Converting 3->6 loses: {lost_reverse}") # [] (no data lost, new fields get defaults)
413
```
414
415
## Validation
416
417
Dimension name validation for safe attribute access.
418
419
```python { .api }
420
class DimensionNameValidity(Enum):
421
Valid = ... # Valid dimension name
422
Unsupported = ... # Valid but not supported in this format
423
Invalid = ... # Invalid dimension name
424
```