Skip to content

Commit e0a101a

Browse files
authored
Merge pull request #103 from IGNF/add_points_with_pdal and version 1.9.1
Add points with pdal
2 parents 0a60acb + 52b3c59 commit e0a101a

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2+
# 1.9.1
3+
- las_add_points_to_pointcloud: Fix add points to LAS (use PDAL instead of Laspy)
4+
15
# 1.9.0
26
- custom PDAL: in the docker image, compile custom PDAL (waiting for PDAL 2.9)
37

pdaltools/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.9.0"
1+
__version__ = "1.9.1"
22

33

44
if __name__ == "__main__":

pdaltools/add_points_in_pointcloud.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import argparse
22
from shutil import copy2
3+
import tempfile
34

45
import geopandas as gpd
56
import laspy
@@ -10,6 +11,8 @@
1011

1112
from pdaltools.las_info import get_epsg_from_las, get_tile_bbox
1213

14+
import pdal
15+
1316

1417
def parse_args(argv=None):
1518
parser = argparse.ArgumentParser("Add points from GeoJSON in LIDAR tile")
@@ -127,13 +130,12 @@ def add_points_to_las(
127130
crs (str): CRS of the data.
128131
virtual_points_classes (int): The classification value to assign to those virtual points (default: 66).
129132
"""
130-
# Copy data pointcloud
131-
copy2(input_las, output_las)
132133

133134
if input_points_with_z.empty:
134135
print(
135136
"No points to add. All points of the geojson file are outside the tile. Copying the input file to output"
136137
)
138+
copy2(input_las, output_las)
137139
return
138140

139141
# Extract XYZ coordinates and additional attribute (classification)
@@ -148,24 +150,30 @@ def add_points_to_las(
148150
header = las.header
149151
if not header:
150152
header = laspy.LasHeader(point_format=8, version="1.4")
153+
154+
new_points = laspy.ScaleAwarePointRecord.zeros(nb_points, header=header) # use header for input_las
155+
# then fill in the gaps (X, Y, Z an classification)
156+
new_points.x = x_coords.astype(new_points.x.dtype)
157+
new_points.y = y_coords.astype(new_points.y.dtype)
158+
new_points.z = z_coords.astype(new_points.z.dtype)
159+
new_points.classification = classes.astype(new_points.classification.dtype)
160+
161+
with tempfile.NamedTemporaryFile(suffix="_new_points.las") as tmp:
162+
with laspy.open(tmp.name, mode="w", header=header) as las_file:
163+
las_file.write_points(new_points)
164+
151165
if crs:
152-
try:
153-
crs_obj = CRS.from_user_input(crs) # Convert to a pyproj.CRS object
154-
except CRSError:
155-
raise ValueError(f"Invalid CRS: {crs}")
156-
header.add_crs(crs_obj)
157-
158-
# Add the new points with 3D points
159-
with laspy.open(output_las, mode="a", header=header) as output_las: # mode `a` for adding points
160-
# create nb_points points with "0" everywhere
161-
new_points = laspy.ScaleAwarePointRecord.zeros(nb_points, header=header) # use header for input_las
162-
# then fill in the gaps (X, Y, Z an classification)
163-
new_points.x = x_coords.astype(new_points.x.dtype)
164-
new_points.y = y_coords.astype(new_points.y.dtype)
165-
new_points.z = z_coords.astype(new_points.z.dtype)
166-
new_points.classification = classes.astype(new_points.classification.dtype)
167-
168-
output_las.append_points(new_points)
166+
a_srs = crs
167+
else:
168+
a_srs = get_epsg_from_las(input_las)
169+
170+
# Use pdal to merge the new points with the existing points
171+
pipeline = pdal.Pipeline()
172+
pipeline |= pdal.Reader.las(filename=input_las)
173+
pipeline |= pdal.Reader.las(filename=tmp.name)
174+
pipeline |= pdal.Filter.merge()
175+
pipeline |= pdal.Writer.las(filename=output_las, forward="all", a_srs=a_srs)
176+
pipeline.execute()
169177

170178

171179
def line_to_multipoint(line, spacing: float, z_value: float = None):

test/test_add_points_in_pointcloud.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,22 @@ def test_add_points_from_geometry_to_las(input_file, input_points, epsg, expecte
272272
input_points, input_file, OUTPUT_FILE, 68, epsg, 1000, spacing, altitude_column
273273
)
274274
assert Path(OUTPUT_FILE).exists() # check output exists
275+
276+
# Read input and output files to compare headers
277+
input_las = laspy.read(input_file)
278+
output_las = laspy.read(OUTPUT_FILE)
279+
280+
# Compare headers
281+
assert input_las.header.version == output_las.header.version
282+
assert input_las.header.system_identifier == output_las.header.system_identifier
283+
assert input_las.header.extra_header_bytes == output_las.header.extra_header_bytes
284+
assert input_las.header.extra_vlr_bytes == output_las.header.extra_vlr_bytes
285+
assert input_las.header.number_of_evlrs == output_las.header.number_of_evlrs
286+
assert input_las.header.point_format == output_las.header.point_format
287+
assert np.array_equal(input_las.header.scales, output_las.header.scales)
288+
assert np.array_equal(input_las.header.offsets, output_las.header.offsets)
289+
assert input_las.header.vlrs[0].string == output_las.header.vlrs[0].string
290+
275291
point_count = compute_count_one_file(OUTPUT_FILE)["68"]
276292
assert point_count == expected_nb_points # Add all points from geojson
277293

0 commit comments

Comments
 (0)