ign-pdal-tools 1.7.6__py3-none-any.whl → 1.7.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ign_pdal_tools-1.7.6.dist-info → ign_pdal_tools-1.7.7.dist-info}/METADATA +1 -1
- {ign_pdal_tools-1.7.6.dist-info → ign_pdal_tools-1.7.7.dist-info}/RECORD +8 -8
- pdaltools/_version.py +1 -1
- pdaltools/add_points_in_pointcloud.py +81 -13
- pdaltools/las_info.py +18 -0
- {ign_pdal_tools-1.7.6.dist-info → ign_pdal_tools-1.7.7.dist-info}/LICENSE.md +0 -0
- {ign_pdal_tools-1.7.6.dist-info → ign_pdal_tools-1.7.7.dist-info}/WHEEL +0 -0
- {ign_pdal_tools-1.7.6.dist-info → ign_pdal_tools-1.7.7.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
pdaltools/_version.py,sha256=
|
|
1
|
+
pdaltools/_version.py,sha256=k34KtTIEeE3AZ8JK-MhAdByywWh56CG4yUanH4EBsFM,74
|
|
2
2
|
pdaltools/add_points_in_las.py,sha256=TGbt5JUkszjmbQiA2LCUntsjz6A8DHb7QPIXGDuEgWA,3643
|
|
3
|
-
pdaltools/add_points_in_pointcloud.py,sha256=
|
|
3
|
+
pdaltools/add_points_in_pointcloud.py,sha256=ldHQFsV0zpZiG38lJrxhAfsckTwkK4g0BkmXyzHmzmE,6186
|
|
4
4
|
pdaltools/color.py,sha256=PSdtMMdsapOtgzojdnaKVx6IxbKOaN2xP9mScAbCGm0,8629
|
|
5
5
|
pdaltools/las_add_buffer.py,sha256=sBpTywlfsHHS8KuCUa-eydB2hylshEvjrMQt5TrqXb8,11275
|
|
6
6
|
pdaltools/las_clip.py,sha256=GvEOYu8RXV68e35kU8i42GwSkbo4P9TvmS6rkrdPmFM,1034
|
|
7
|
-
pdaltools/las_info.py,sha256=
|
|
7
|
+
pdaltools/las_info.py,sha256=rHweSNFOV1fD8XKVbfAv4bkUWwhr5SGzX9Kr-qCORlk,8682
|
|
8
8
|
pdaltools/las_merge.py,sha256=tcFVueV9X9nNEaoAl5zCduY5DETlBg63MAgP2SuKiNo,4121
|
|
9
9
|
pdaltools/las_remove_dimensions.py,sha256=u_3VfkabkN_Y3eDLdJwCLVGpondvIx0f0v0RdFDoAFw,1792
|
|
10
10
|
pdaltools/pcd_info.py,sha256=NIAH5KGikVDQLlbCcw9FuaPqe20UZvRfkHsDZd5kmZA,3210
|
|
11
11
|
pdaltools/replace_attribute_in_las.py,sha256=po1F-fi8s7iilqKWaryW4JRbsmdMOUe0yGvG3AEKxtk,4771
|
|
12
12
|
pdaltools/standardize_format.py,sha256=gqm2GJbtDkT4k4oC_NX2LIPh9R2BLh4sMHLKYgfKrMc,3973
|
|
13
13
|
pdaltools/unlock_file.py,sha256=pIThdWMNkTph0xgJVVRaM1o9aUMQhM6804PscScB3JI,1963
|
|
14
|
-
ign_pdal_tools-1.7.
|
|
15
|
-
ign_pdal_tools-1.7.
|
|
16
|
-
ign_pdal_tools-1.7.
|
|
17
|
-
ign_pdal_tools-1.7.
|
|
18
|
-
ign_pdal_tools-1.7.
|
|
14
|
+
ign_pdal_tools-1.7.7.dist-info/LICENSE.md,sha256=iVzCFZTUXeiqP8bP474iuWZiWO_kDCD4SPh1Wiw125Y,1120
|
|
15
|
+
ign_pdal_tools-1.7.7.dist-info/METADATA,sha256=8yQ2NV4g9oakIZfylyAvmBkV8qzNsPL_-vA2CUcG5Cs,5722
|
|
16
|
+
ign_pdal_tools-1.7.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
17
|
+
ign_pdal_tools-1.7.7.dist-info/top_level.txt,sha256=KvGW0ZzqQbhCKzB5_Tp_buWMZyIgiO2M2krWF_ecOZc,10
|
|
18
|
+
ign_pdal_tools-1.7.7.dist-info/RECORD,,
|
pdaltools/_version.py
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
1
3
|
import geopandas as gpd
|
|
2
4
|
import laspy
|
|
3
5
|
import numpy as np
|
|
6
|
+
from pyproj import CRS
|
|
7
|
+
from pyproj.exceptions import CRSError
|
|
4
8
|
from shapely.geometry import box
|
|
5
9
|
|
|
6
|
-
from pdaltools.las_info import get_tile_origin_using_header_info
|
|
10
|
+
from pdaltools.las_info import get_epsg_from_las, get_tile_origin_using_header_info
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_args(argv=None):
|
|
14
|
+
parser = argparse.ArgumentParser("Add points from GeoJSON in LIDAR tile")
|
|
15
|
+
parser.add_argument("--input_geojson", "-ig", type=str, required=True, help="Input GeoJSON file")
|
|
16
|
+
parser.add_argument("--input_las", "-i", type=str, required=True, help="Input las file")
|
|
17
|
+
parser.add_argument("--output_las", "-o", type=str, required=True, default="", help="Output las file")
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--virtual_points_classes",
|
|
20
|
+
"-c",
|
|
21
|
+
type=int,
|
|
22
|
+
default=66,
|
|
23
|
+
help="classification value to assign to the added virtual points",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--spatial_ref",
|
|
27
|
+
type=str,
|
|
28
|
+
required=False,
|
|
29
|
+
help="spatial reference for the writer",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--tile_width",
|
|
33
|
+
type=int,
|
|
34
|
+
default=1000,
|
|
35
|
+
help="width of tiles in meters",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return parser.parse_args(argv)
|
|
7
39
|
|
|
8
40
|
|
|
9
41
|
def get_tile_bbox(input_las, tile_width=1000) -> tuple:
|
|
@@ -23,25 +55,25 @@ def get_tile_bbox(input_las, tile_width=1000) -> tuple:
|
|
|
23
55
|
return bbox
|
|
24
56
|
|
|
25
57
|
|
|
26
|
-
def clip_3d_points_to_tile(input_points: str, input_las: str, crs: str) -> gpd.GeoDataFrame:
|
|
58
|
+
def clip_3d_points_to_tile(input_points: str, input_las: str, crs: str, tile_width: int) -> gpd.GeoDataFrame:
|
|
27
59
|
"""
|
|
28
60
|
Add points from a GeoJSON file in the LIDAR's tile.
|
|
29
61
|
|
|
30
62
|
Args:
|
|
31
63
|
input_points (str): Path to the input GeoJSON file with 3D points.
|
|
32
64
|
input_las (str): Path to the LIDAR `.las/.laz` file.
|
|
33
|
-
crs (str): CRS of the data
|
|
65
|
+
crs (str): CRS of the data.
|
|
66
|
+
tile_width (int): Width of the tile in meters (default: 1000).
|
|
34
67
|
|
|
35
68
|
Return:
|
|
36
69
|
gpd.GeoDataFrame: Points 2d with "Z" value
|
|
37
70
|
"""
|
|
38
71
|
# Compute the bounding box of the LIDAR tile
|
|
39
|
-
tile_bbox = get_tile_bbox(input_las)
|
|
72
|
+
tile_bbox = get_tile_bbox(input_las, tile_width)
|
|
40
73
|
|
|
41
74
|
# Read the input GeoJSON with 3D points
|
|
42
75
|
points_gdf = gpd.read_file(input_points)
|
|
43
76
|
|
|
44
|
-
# Ensure the CRS matches
|
|
45
77
|
if crs:
|
|
46
78
|
points_gdf = points_gdf.to_crs(crs)
|
|
47
79
|
|
|
@@ -55,7 +87,7 @@ def clip_3d_points_to_tile(input_points: str, input_las: str, crs: str) -> gpd.G
|
|
|
55
87
|
|
|
56
88
|
|
|
57
89
|
def add_points_to_las(
|
|
58
|
-
input_points_with_z: gpd.GeoDataFrame, input_las: str, output_las: str, virtual_points_classes=66
|
|
90
|
+
input_points_with_z: gpd.GeoDataFrame, input_las: str, output_las: str, crs: str, virtual_points_classes=66
|
|
59
91
|
):
|
|
60
92
|
"""Add points (3D points in LAZ format) by LIDAR tiles (tiling file)
|
|
61
93
|
|
|
@@ -63,6 +95,7 @@ def add_points_to_las(
|
|
|
63
95
|
input_points_with_z(gpd.GeoDataFrame): geometry columns (2D points) as encoded to WKT.
|
|
64
96
|
input_las (str): Path to the LIDAR tiles (LAZ).
|
|
65
97
|
output_las (str): Path to save the updated LIDAR file (LAS/LAZ format).
|
|
98
|
+
crs (str): CRS of the data.
|
|
66
99
|
virtual_points_classes (int): The classification value to assign to those virtual points (default: 66).
|
|
67
100
|
"""
|
|
68
101
|
# Check if input points are empty
|
|
@@ -75,28 +108,63 @@ def add_points_to_las(
|
|
|
75
108
|
z_coords = input_points_with_z.RecupZ
|
|
76
109
|
classes = virtual_points_classes * np.ones(len(input_points_with_z.index))
|
|
77
110
|
|
|
78
|
-
# Read the existing LIDAR file
|
|
79
111
|
with laspy.open(input_las, mode="r") as las:
|
|
80
112
|
las_data = las.read()
|
|
81
113
|
header = las.header
|
|
82
114
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
if not header:
|
|
116
|
+
header = laspy.LasHeader(point_format=8, version="1.4")
|
|
117
|
+
if crs:
|
|
118
|
+
try:
|
|
119
|
+
crs_obj = CRS.from_user_input(crs) # Convert to a pyproj.CRS object
|
|
120
|
+
except CRSError:
|
|
121
|
+
raise ValueError(f"Invalid CRS: {crs}")
|
|
122
|
+
header.add_crs(crs_obj)
|
|
86
123
|
|
|
87
|
-
# Append
|
|
124
|
+
# Append new points
|
|
88
125
|
new_x = np.concatenate([las_data.x, x_coords])
|
|
89
126
|
new_y = np.concatenate([las_data.y, y_coords])
|
|
90
127
|
new_z = np.concatenate([las_data.z, z_coords])
|
|
91
128
|
new_classes = np.concatenate([las_data.classification, classes])
|
|
92
129
|
|
|
93
|
-
# Create a new LAS file with updated data
|
|
94
130
|
updated_las = laspy.LasData(header)
|
|
95
131
|
updated_las.x = new_x
|
|
96
132
|
updated_las.y = new_y
|
|
97
133
|
updated_las.z = new_z
|
|
98
134
|
updated_las.classification = new_classes
|
|
99
135
|
|
|
100
|
-
# Write the updated LAS file
|
|
101
136
|
with laspy.open(output_las, mode="w", header=header, do_compress=True) as writer:
|
|
102
137
|
writer.write_points(updated_las.points)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def add_points_from_geojson_to_las(
|
|
141
|
+
input_geojson: str, input_las: str, output_las: str, virtual_points_classes: int, spatial_ref: str, tile_width: int
|
|
142
|
+
):
|
|
143
|
+
"""Add points with Z value(GeoJSON format) by LIDAR tiles (tiling file)
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
input_geojson (str): Path to the input GeoJSON file with 3D points.
|
|
147
|
+
input_las (str): Path to the LIDAR `.las/.laz` file.
|
|
148
|
+
output_las (str): Path to save the updated LIDAR file (LAS/LAZ format).
|
|
149
|
+
virtual_points_classes (int): The classification value to assign to those virtual points (default: 66).
|
|
150
|
+
spatial_ref (str): CRS of the data.
|
|
151
|
+
tile_width (int): Width of the tile in meters (default: 1000).
|
|
152
|
+
|
|
153
|
+
Raises:
|
|
154
|
+
RuntimeError: If the input LAS file has no valid EPSG code.
|
|
155
|
+
"""
|
|
156
|
+
if not spatial_ref:
|
|
157
|
+
spatial_ref = get_epsg_from_las(input_las)
|
|
158
|
+
if spatial_ref is None:
|
|
159
|
+
raise RuntimeError(f"LAS file {input_las} does not have a valid EPSG code.")
|
|
160
|
+
|
|
161
|
+
# Clip points from GeoJSON by LIDAR tile
|
|
162
|
+
points_clipped = clip_3d_points_to_tile(input_geojson, input_las, spatial_ref, tile_width)
|
|
163
|
+
|
|
164
|
+
# Add points by LIDAR tile and save the result
|
|
165
|
+
add_points_to_las(points_clipped, input_las, output_las, spatial_ref, virtual_points_classes)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
args = parse_args()
|
|
170
|
+
add_points_from_geojson_to_las(**vars(args))
|
pdaltools/las_info.py
CHANGED
|
@@ -3,6 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
from typing import Dict, Tuple
|
|
5
5
|
|
|
6
|
+
import laspy
|
|
6
7
|
import osgeo.osr as osr
|
|
7
8
|
import pdal
|
|
8
9
|
|
|
@@ -211,3 +212,20 @@ def get_writer_parameters_from_reader_metadata(metadata: Dict, a_srs=None) -> Di
|
|
|
211
212
|
"a_srs": a_srs if a_srs else reader_metadata["comp_spatialreference"],
|
|
212
213
|
}
|
|
213
214
|
return params
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_epsg_from_las(filename: str) -> str:
|
|
218
|
+
"""Extract EPSG code from LAS file metadata and return as 'EPSG:XXXX' format.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
filename (str): full path of file for which to get the bounding box
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
str : CRS's value of the data in 'EPSG:XXXX' format, or None if not found.
|
|
225
|
+
"""
|
|
226
|
+
with laspy.open(filename) as las:
|
|
227
|
+
crs = las.header.parse_crs()
|
|
228
|
+
if crs is None:
|
|
229
|
+
return None # Return None if CRS is not defined
|
|
230
|
+
epsg_code = crs.to_epsg()
|
|
231
|
+
return f"EPSG:{epsg_code}" if epsg_code else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|