0
# COPC Operations
1
2
Cloud Optimized Point Cloud (COPC) support for efficient web-based and spatial querying of large LiDAR datasets. COPC enables selective data access and HTTP streaming, making it ideal for web applications and large-scale data processing.
3
4
## Capabilities
5
6
### COPC Reader
7
8
Specialized reader for COPC files with support for spatial and level-based queries.
9
10
```python { .api }
11
class CopcReader:
12
def __init__(self, stream, close_fd=True, http_num_threads=8, decompression_selection=None):
13
"""
14
Initialize COPC reader.
15
16
Parameters:
17
- stream: BinaryIO - COPC file stream
18
- close_fd: bool - Whether to close file descriptor (default: True)
19
- http_num_threads: int - Number of HTTP worker threads (default: 8)
20
- decompression_selection: DecompressionSelection - Fields to decompress
21
"""
22
23
@classmethod
24
def open(cls, source, http_num_threads=8, decompression_selection=None) -> CopcReader:
25
"""
26
Open COPC file from local path or HTTP URL.
27
28
Parameters:
29
- source: str, Path, or URL - COPC file location
30
- http_num_threads: int - Number of HTTP worker threads for remote files
31
- decompression_selection: DecompressionSelection - Fields to decompress
32
33
Returns:
34
CopcReader: COPC reader instance
35
"""
36
37
@property
38
def header(self) -> LasHeader:
39
"""Get LAS header for the COPC file."""
40
41
def query(self, bounds=None, resolution=None, level=None) -> ScaleAwarePointRecord:
42
"""
43
General query method supporting spatial bounds, resolution, and level constraints.
44
45
Parameters:
46
- bounds: Bounds - Spatial bounds for query (optional)
47
- resolution: float - Target point resolution/density (optional)
48
- level: int or range - Octree level(s) to query (optional)
49
50
Returns:
51
ScaleAwarePointRecord: Points matching query criteria
52
"""
53
54
def spatial_query(self, bounds: Bounds) -> ScaleAwarePointRecord:
55
"""
56
Query points within specified spatial bounds.
57
58
Parameters:
59
- bounds: Bounds - Spatial query bounds
60
61
Returns:
62
ScaleAwarePointRecord: Points within bounds
63
"""
64
65
def level_query(self, level) -> ScaleAwarePointRecord:
66
"""
67
Query points at specific octree level(s).
68
69
Parameters:
70
- level: int or range - Octree level or level range
71
72
Returns:
73
ScaleAwarePointRecord: Points at specified level(s)
74
"""
75
76
def close(self):
77
"""Close COPC reader and free resources."""
78
79
def __enter__(self) -> CopcReader: ...
80
def __exit__(self, exc_type, exc_val, exc_tb): ...
81
```
82
83
**Usage Examples:**
84
85
```python
86
import laspy
87
from laspy import CopcReader, Bounds
88
89
# Open local COPC file
90
with CopcReader.open('data.copc.laz') as reader:
91
print(f"COPC file has {reader.header.point_count} points")
92
93
# Query all points
94
all_points = reader.query()
95
print(f"Retrieved {len(all_points)} points")
96
97
# Open remote COPC file via HTTP
98
url = "https://example.com/data.copc.laz"
99
with CopcReader.open(url, http_num_threads=4) as reader:
100
# Spatial query
101
bounds = Bounds(
102
mins=[100, 200, 0],
103
maxs=[200, 300, 50]
104
)
105
spatial_points = reader.spatial_query(bounds)
106
print(f"Spatial query returned {len(spatial_points)} points")
107
108
# Level-based query for different detail levels
109
with CopcReader.open('data.copc.laz') as reader:
110
# Get coarse overview (low levels)
111
overview = reader.level_query(range(0, 3))
112
113
# Get detailed data (high levels)
114
detailed = reader.level_query(range(8, 12))
115
116
print(f"Overview: {len(overview)} points")
117
print(f"Detailed: {len(detailed)} points")
118
```
119
120
### Spatial Bounds
121
122
Geometric bounds definition for spatial queries and region-of-interest operations.
123
124
```python { .api }
125
class Bounds:
126
def __init__(self, mins, maxs):
127
"""
128
Create spatial bounds.
129
130
Parameters:
131
- mins: array-like - Minimum coordinates [x, y] or [x, y, z]
132
- maxs: array-like - Maximum coordinates [x, y] or [x, y, z]
133
"""
134
135
@property
136
def mins(self) -> np.ndarray:
137
"""Minimum coordinate values."""
138
139
@property
140
def maxs(self) -> np.ndarray:
141
"""Maximum coordinate values."""
142
143
def overlaps(self, other: Bounds) -> bool:
144
"""
145
Check if bounds overlap with another bounds.
146
147
Parameters:
148
- other: Bounds - Other bounds to check against
149
150
Returns:
151
bool: True if bounds overlap
152
"""
153
154
@staticmethod
155
def ensure_3d(mins, maxs) -> Bounds:
156
"""
157
Ensure bounds are 3D by extending 2D bounds.
158
159
Parameters:
160
- mins: array-like - Minimum coordinates
161
- maxs: array-like - Maximum coordinates
162
163
Returns:
164
Bounds: 3D bounds object
165
"""
166
```
167
168
**Usage Examples:**
169
170
```python
171
import numpy as np
172
from laspy import Bounds
173
174
# Create 2D bounds (Z dimension will be unlimited)
175
bounds_2d = Bounds(
176
mins=[1000, 2000],
177
maxs=[1500, 2500]
178
)
179
180
# Create 3D bounds
181
bounds_3d = Bounds(
182
mins=[1000, 2000, 100],
183
maxs=[1500, 2500, 200]
184
)
185
186
# Check bounds properties
187
print(f"2D bounds mins: {bounds_2d.mins}")
188
print(f"2D bounds maxs: {bounds_2d.maxs}")
189
190
# Check overlap
191
other_bounds = Bounds([1400, 2400], [1600, 2600])
192
overlaps = bounds_2d.overlaps(other_bounds)
193
print(f"Bounds overlap: {overlaps}")
194
195
# Convert 2D to 3D bounds
196
bounds_3d_converted = Bounds.ensure_3d([1000, 2000], [1500, 2500])
197
print(f"3D converted: {bounds_3d_converted.mins} to {bounds_3d_converted.maxs}")
198
```
199
200
## Advanced COPC Usage
201
202
### Multi-Region Queries
203
204
```python
205
import laspy
206
from laspy import CopcReader, Bounds
207
208
def query_multiple_regions(copc_file, regions):
209
"""Query multiple spatial regions efficiently."""
210
with CopcReader.open(copc_file) as reader:
211
all_points = []
212
213
for region_name, (mins, maxs) in regions.items():
214
bounds = Bounds(mins, maxs)
215
points = reader.spatial_query(bounds)
216
print(f"Region '{region_name}': {len(points)} points")
217
all_points.append(points)
218
219
return all_points
220
221
# Define regions of interest
222
regions = {
223
"downtown": ([1000, 2000, 0], [1200, 2200, 100]),
224
"park": ([1500, 2500, 0], [1700, 2700, 50]),
225
"industrial": ([2000, 3000, 0], [2300, 3300, 80])
226
}
227
228
results = query_multiple_regions('city.copc.laz', regions)
229
```
230
231
### Progressive Level-of-Detail
232
233
```python
234
import laspy
235
from laspy import CopcReader
236
237
def progressive_query(copc_file, max_points=1000000):
238
"""Query with progressive level-of-detail until point limit reached."""
239
with CopcReader.open(copc_file) as reader:
240
level = 0
241
total_points = 0
242
243
while total_points < max_points:
244
level_points = reader.level_query(level)
245
if len(level_points) == 0:
246
break
247
248
total_points += len(level_points)
249
print(f"Level {level}: {len(level_points)} points (total: {total_points})")
250
251
# Process level points
252
yield level, level_points
253
level += 1
254
255
# Use progressive querying
256
for level, points in progressive_query('large.copc.laz'):
257
# Process points at this level of detail
258
density = len(points) / (points.x.max() - points.x.min()) / (points.y.max() - points.y.min())
259
print(f"Level {level} density: {density:.2f} points/unit²")
260
```
261
262
### HTTP Streaming with Custom Threading
263
264
```python
265
import laspy
266
from laspy import CopcReader, DecompressionSelection
267
268
# Optimize for network access
269
def stream_copc_efficiently(url, regions, num_threads=16):
270
"""Stream COPC data with optimized HTTP settings."""
271
272
# Use selective decompression to reduce bandwidth
273
selection = DecompressionSelection.base() # Only essential fields
274
275
with CopcReader.open(url,
276
http_num_threads=num_threads,
277
decompression_selection=selection) as reader:
278
279
print(f"Streaming from: {url}")
280
print(f"File bounds: {reader.header.mins} to {reader.header.maxs}")
281
282
for region_name, bounds in regions.items():
283
print(f"Fetching region: {region_name}")
284
points = reader.spatial_query(bounds)
285
286
# Process immediately to minimize memory usage
287
ground_points = points[points.classification == 2]
288
yield region_name, ground_points
289
290
# Stream from cloud storage
291
url = "https://cloud-storage.example.com/lidar/city.copc.laz"
292
regions = {
293
"area1": Bounds([1000, 2000], [1100, 2100]),
294
"area2": Bounds([1200, 2200], [1300, 2300])
295
}
296
297
for region_name, ground_points in stream_copc_efficiently(url, regions):
298
print(f"Processing {len(ground_points)} ground points from {region_name}")
299
```
300
301
## Integration with Standard LAS Operations
302
303
COPC data can be seamlessly integrated with standard laspy operations:
304
305
```python
306
import laspy
307
from laspy import CopcReader, Bounds
308
309
# Query COPC data and write to standard LAS
310
with CopcReader.open('input.copc.laz') as reader:
311
# Query region of interest
312
bounds = Bounds([1000, 2000, 0], [1500, 2500, 100])
313
points = reader.spatial_query(bounds)
314
315
# Create LAS data container
316
las = laspy.LasData(reader.header, points)
317
318
# Apply filters
319
las.points = las.points[las.classification.isin([2, 3, 4])] # Ground, low/med vegetation
320
321
# Write filtered data
322
las.write('filtered_region.las')
323
324
# Convert COPC query results to different point format
325
with CopcReader.open('input.copc.laz') as reader:
326
points = reader.query()
327
las = laspy.LasData(reader.header, points)
328
329
# Convert to point format with RGB
330
converted = laspy.convert(las, point_format_id=7)
331
converted.write('converted.las')
332
```
333
334
## Error Handling
335
336
COPC operations can raise specific exceptions:
337
338
```python
339
import laspy
340
from laspy import CopcReader, Bounds
341
from laspy.errors import LaspyException
342
343
try:
344
with CopcReader.open('https://example.com/missing.copc.laz') as reader:
345
points = reader.spatial_query(Bounds([0, 0], [100, 100]))
346
except LaspyException as e:
347
print(f"COPC error: {e}")
348
except Exception as e:
349
print(f"Network or other error: {e}")
350
```