ign-pdal-tools 1.8.1__py3-none-any.whl → 1.10.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ign-pdal-tools
3
- Version: 1.8.1
3
+ Version: 1.10.0
4
4
  Summary: Library for common LAS files manipulation with PDAL
5
5
  Author-email: Guillaume Liegard <guillaume.liegard@ign.fr>
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,18 @@
1
+ ign_pdal_tools-1.10.0.dist-info/licenses/LICENSE.md,sha256=iVzCFZTUXeiqP8bP474iuWZiWO_kDCD4SPh1Wiw125Y,1120
2
+ pdaltools/_version.py,sha256=P7Y6oBanMwNtN5HNsPQJszr9apg6shwS8QyOphkF_c0,75
3
+ pdaltools/add_points_in_pointcloud.py,sha256=13xl8tnoaW6FsRHBPa77-c6Olw8uUw2KBUGoz8JcbBg,12675
4
+ pdaltools/color.py,sha256=vJgpb8dOvT5rnq5NdVOaMdGc_pKL3damLy4HwGvigJQ,14472
5
+ pdaltools/las_add_buffer.py,sha256=rnFExAfi0KqlQpL4hDMh2aC08AcYdSHSB6WPG5RyFIc,11274
6
+ pdaltools/las_clip.py,sha256=GvEOYu8RXV68e35kU8i42GwSkbo4P9TvmS6rkrdPmFM,1034
7
+ pdaltools/las_info.py,sha256=lMKxKzsViptDENI1wOlANG4qOvdc19ixyasYKD-N1ck,9512
8
+ pdaltools/las_merge.py,sha256=tcFVueV9X9nNEaoAl5zCduY5DETlBg63MAgP2SuKiNo,4121
9
+ pdaltools/las_remove_dimensions.py,sha256=f8imGhN6LNTuQ1GMJQRzIIV3Wab_oRPOyEnKi1CgfiM,2318
10
+ pdaltools/las_rename_dimension.py,sha256=YSfxLspBPXvDklVztdPxQIK65FeDl7gEgQDWMWpXxm8,2560
11
+ pdaltools/pcd_info.py,sha256=NIAH5KGikVDQLlbCcw9FuaPqe20UZvRfkHsDZd5kmZA,3210
12
+ pdaltools/replace_attribute_in_las.py,sha256=po1F-fi8s7iilqKWaryW4JRbsmdMOUe0yGvG3AEKxtk,4771
13
+ pdaltools/standardize_format.py,sha256=gqm2GJbtDkT4k4oC_NX2LIPh9R2BLh4sMHLKYgfKrMc,3973
14
+ pdaltools/unlock_file.py,sha256=G2odk0cpp_X9r49Y90oK88v3qlihaMfg6acwmWqblik,1958
15
+ ign_pdal_tools-1.10.0.dist-info/METADATA,sha256=nHBXaDPf2WckwbwvyXJZUo0oCz9fgePfI3sfExF2tXw,5778
16
+ ign_pdal_tools-1.10.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
17
+ ign_pdal_tools-1.10.0.dist-info/top_level.txt,sha256=KvGW0ZzqQbhCKzB5_Tp_buWMZyIgiO2M2krWF_ecOZc,10
18
+ ign_pdal_tools-1.10.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pdaltools/_version.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.8.1"
1
+ __version__ = "1.10.0"
2
2
 
3
3
 
4
4
  if __name__ == "__main__":
@@ -1,5 +1,6 @@
1
1
  import argparse
2
2
  from shutil import copy2
3
+ import tempfile
3
4
 
4
5
  import geopandas as gpd
5
6
  import laspy
@@ -10,6 +11,8 @@ from shapely.geometry import MultiPoint, Point, box
10
11
 
11
12
  from pdaltools.las_info import get_epsg_from_las, get_tile_bbox
12
13
 
14
+ import pdal
15
+
13
16
 
14
17
  def parse_args(argv=None):
15
18
  parser = argparse.ArgumentParser("Add points from GeoJSON in LIDAR tile")
@@ -127,13 +130,12 @@ def add_points_to_las(
127
130
  crs (str): CRS of the data.
128
131
  virtual_points_classes (int): The classification value to assign to those virtual points (default: 66).
129
132
  """
130
- # Copy data pointcloud
131
- copy2(input_las, output_las)
132
133
 
133
134
  if input_points_with_z.empty:
134
135
  print(
135
136
  "No points to add. All points of the geojson file are outside the tile. Copying the input file to output"
136
137
  )
138
+ copy2(input_las, output_las)
137
139
  return
138
140
 
139
141
  # Extract XYZ coordinates and additional attribute (classification)
@@ -148,24 +150,30 @@ def add_points_to_las(
148
150
  header = las.header
149
151
  if not header:
150
152
  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
+
151
165
  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()
169
177
 
170
178
 
171
179
  def line_to_multipoint(line, spacing: float, z_value: float = None):
@@ -158,7 +158,6 @@ def remove_points_from_buffer(input_file: str, output_file: str):
158
158
  pipeline |= pdal.Filter.range(limits=f"{ORIGINAL_TILE_TAG}[1:1]")
159
159
  pipeline |= pdal.Writer.las(filename=tmp_las.name, forward="all", extra_dims="all")
160
160
  pipeline.execute()
161
-
162
161
  remove_dimensions_from_las(tmp_las.name, dimensions=[ORIGINAL_TILE_TAG], output_las=output_file)
163
162
 
164
163
 
@@ -5,22 +5,36 @@ import pdal
5
5
  from pdaltools.las_info import get_writer_parameters_from_reader_metadata
6
6
 
7
7
 
8
- def remove_dimensions_from_las(input_las: str, dimensions: [str], output_las: str):
8
+ def remove_dimensions_from_points(points, metadata, dimensions: [str], output_las: str):
9
9
  """
10
10
  export new las without some dimensions
11
11
  """
12
- pipeline = pdal.Pipeline() | pdal.Reader.las(input_las)
13
- pipeline.execute()
14
- points = pipeline.arrays[0]
12
+
13
+ mandatory_dimensions = ["X", "Y", "Z", "x", "y", "z"]
14
+ output_dimensions_test = [dim for dim in dimensions if dim not in mandatory_dimensions]
15
+ assert len(output_dimensions_test) == len(
16
+ dimensions
17
+ ), "All dimensions to remove must not be mandatory dimensions (X,Y,Z,x,y,z)"
18
+
15
19
  input_dimensions = list(points.dtype.fields.keys())
16
20
  output_dimensions = [dim for dim in input_dimensions if dim not in dimensions]
17
21
  points_pruned = points[output_dimensions]
18
- params = get_writer_parameters_from_reader_metadata(pipeline.metadata)
22
+ params = get_writer_parameters_from_reader_metadata(metadata)
19
23
  pipeline_end = pdal.Pipeline(arrays=[points_pruned])
20
24
  pipeline_end |= pdal.Writer.las(output_las, forward="all", **params)
21
25
  pipeline_end.execute()
22
26
 
23
27
 
28
+ def remove_dimensions_from_las(input_las: str, dimensions: [str], output_las: str):
29
+ """
30
+ export new las without some dimensions
31
+ """
32
+ pipeline = pdal.Pipeline() | pdal.Reader.las(input_las)
33
+ pipeline.execute()
34
+ points = pipeline.arrays[0]
35
+ remove_dimensions_from_points(points, pipeline.metadata, dimensions, output_las)
36
+
37
+
24
38
  def parse_args():
25
39
  parser = argparse.ArgumentParser("Remove dimensions from las")
26
40
  parser.add_argument(
@@ -0,0 +1,79 @@
1
+ """
2
+ Rename dimensions in a LAS file using PDAL's Python API.
3
+
4
+ This script allows renaming dimensions in a LAS file while preserving all other data.
5
+ """
6
+
7
+ import argparse
8
+ import pdal
9
+ import sys
10
+ from pathlib import Path
11
+ from pdaltools.las_remove_dimensions import remove_dimensions_from_points
12
+
13
+
14
+ def rename_dimension(input_file: str, output_file: str, old_dims: list[str], new_dims: list[str]):
15
+ """
16
+ Rename one or multiple dimensions in a LAS file using PDAL.
17
+
18
+ Args:
19
+ input_file: Path to the input LAS file
20
+ output_file: Path to save the output LAS file
21
+ old_dims: List of names of dimensions to rename
22
+ new_dims: List of new names for the dimensions
23
+ """
24
+
25
+ # Validate dimensions
26
+ if len(old_dims) != len(new_dims):
27
+ raise ValueError("Number of old dimensions must match number of new dimensions")
28
+
29
+ mandatory_dimensions = ['X', 'Y', 'Z', 'x', 'y', 'z']
30
+ for dim in new_dims:
31
+ if dim in mandatory_dimensions:
32
+ raise ValueError(f"New dimension {dim} cannot be a mandatory dimension (X,Y,Z,x,y,z)")
33
+
34
+ pipeline = pdal.Pipeline() | pdal.Reader.las(input_file)
35
+ for old, new in zip(old_dims, new_dims):
36
+ pipeline |= pdal.Filter.ferry(dimensions=f"{old} => {new}")
37
+ pipeline |= pdal.Writer.las(output_file)
38
+ pipeline.execute()
39
+ points = pipeline.arrays[0]
40
+
41
+ # Remove old dimensions
42
+ remove_dimensions_from_points(points, pipeline.metadata, old_dims, output_file)
43
+
44
+
45
+ def main():
46
+ parser = argparse.ArgumentParser(description="Rename dimensions in a LAS file")
47
+ parser.add_argument("input_file", help="Input LAS file")
48
+ parser.add_argument("output_file", help="Output LAS file")
49
+ parser.add_argument(
50
+ "--old-dims",
51
+ nargs="+",
52
+ required=True,
53
+ help="Names of dimensions to rename (can specify multiple)",
54
+ )
55
+ parser.add_argument(
56
+ "--new-dims",
57
+ nargs="+",
58
+ required=True,
59
+ help="New names for the dimensions (must match --old-dims count)",
60
+ )
61
+
62
+ args = parser.parse_args()
63
+
64
+ # Validate input file
65
+ input_path = Path(args.input_file)
66
+ if not input_path.exists():
67
+ print(f"Error: Input file {args.input_file} does not exist", file=sys.stderr)
68
+ sys.exit(1)
69
+
70
+ # Validate output file
71
+ output_path = Path(args.output_file)
72
+ if output_path.exists():
73
+ print(f"Warning: Output file {args.output_file} already exists. It will be overwritten.")
74
+
75
+ rename_dimension(args.input_file, args.output_file, args.old_dims, args.new_dims)
76
+
77
+
78
+ if __name__ == "__main__":
79
+ main()
pdaltools/unlock_file.py CHANGED
@@ -1,5 +1,4 @@
1
- """Tools to handle malformed las/laz files
2
- """
1
+ """Tools to handle malformed las/laz files"""
3
2
 
4
3
  # https://gis.stackexchange.com/questions/413191/python-pdal-error-reading-format-1-4-las-file-readers-las-error-global-enco
5
4
 
@@ -1,17 +0,0 @@
1
- ign_pdal_tools-1.8.1.dist-info/licenses/LICENSE.md,sha256=iVzCFZTUXeiqP8bP474iuWZiWO_kDCD4SPh1Wiw125Y,1120
2
- pdaltools/_version.py,sha256=h3YpE0kofxFLptgIZDqRkDU33Y3pyOvd2HkmX5qItO8,74
3
- pdaltools/add_points_in_pointcloud.py,sha256=-xFdqrBr5KC-5ab7g-poaG2rR8dcbyKP9bLFbfd2V2c,12513
4
- pdaltools/color.py,sha256=vJgpb8dOvT5rnq5NdVOaMdGc_pKL3damLy4HwGvigJQ,14472
5
- pdaltools/las_add_buffer.py,sha256=sBpTywlfsHHS8KuCUa-eydB2hylshEvjrMQt5TrqXb8,11275
6
- pdaltools/las_clip.py,sha256=GvEOYu8RXV68e35kU8i42GwSkbo4P9TvmS6rkrdPmFM,1034
7
- pdaltools/las_info.py,sha256=lMKxKzsViptDENI1wOlANG4qOvdc19ixyasYKD-N1ck,9512
8
- pdaltools/las_merge.py,sha256=tcFVueV9X9nNEaoAl5zCduY5DETlBg63MAgP2SuKiNo,4121
9
- pdaltools/las_remove_dimensions.py,sha256=VX6jQqjF298bsPm6MJQlBH-7ue9fG7XlwhDzdkACZ84,1793
10
- pdaltools/pcd_info.py,sha256=NIAH5KGikVDQLlbCcw9FuaPqe20UZvRfkHsDZd5kmZA,3210
11
- pdaltools/replace_attribute_in_las.py,sha256=po1F-fi8s7iilqKWaryW4JRbsmdMOUe0yGvG3AEKxtk,4771
12
- pdaltools/standardize_format.py,sha256=gqm2GJbtDkT4k4oC_NX2LIPh9R2BLh4sMHLKYgfKrMc,3973
13
- pdaltools/unlock_file.py,sha256=pIThdWMNkTph0xgJVVRaM1o9aUMQhM6804PscScB3JI,1963
14
- ign_pdal_tools-1.8.1.dist-info/METADATA,sha256=BNBK2WhJ6rfeHGcRdERPiKzAbIH_bsi7avbmYpH2Df4,5777
15
- ign_pdal_tools-1.8.1.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
16
- ign_pdal_tools-1.8.1.dist-info/top_level.txt,sha256=KvGW0ZzqQbhCKzB5_Tp_buWMZyIgiO2M2krWF_ecOZc,10
17
- ign_pdal_tools-1.8.1.dist-info/RECORD,,