ign-pdal-tools 1.7.11__py3-none-any.whl → 1.8.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.7.11
3
+ Version: 1.8.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
@@ -88,10 +88,15 @@ By default, `xcoord` and `ycoord` are given in kilometers and the shape of the t
88
88
  `readers.las: Global encoding WKT flag not set for point format 6 - 10.` which is due to TerraSolid
89
89
  malformed LAS output for LAS1.4 files with point format 6 to 10.
90
90
 
91
- ## Add points in Las
92
-
93
- [add_points_in_las.py](pdaltools/add_points_in_las.py): add points from some vector files (ex: shp, geojson, ...) inside Las. New points will have X,Y and Z coordinates. Other attributes values given by the initial las file are null (ex: classification at 0). These others attributes could be forced by using the '--dimensions/-d' option in the command line (ex : 'add_points_in_las.py -i myLas.las -g myPoints.json -d classification=64' - points will have their classification set to 64). The dimension should be present in the initial las ; this is not allowed to add new dimension.
91
+ ## Add points in pointcloud
94
92
 
93
+ [add_points_in_pointcloud.py](pdaltools/add_points_in_pointcloud.py): add points from some vector files (ex: shp, geojson, ...) inside Las/Laz:
94
+ - 2 kinds of geometries are handled:
95
+ - if the geometries in the vector file are points, they are added directly to the las file
96
+ - if the geometries are lines, points are added along this line using a `spacing` parameter
97
+ - In case the points are 2D only, Z can be provided as a feature property (parametrized via `altitude_column`)
98
+ - The Classification attribute for these points is parametrized via `virtual_points_classes`
99
+ - All the other attributes are set to 0.
95
100
 
96
101
  # Dev / Build
97
102
 
@@ -0,0 +1,17 @@
1
+ ign_pdal_tools-1.8.0.dist-info/licenses/LICENSE.md,sha256=iVzCFZTUXeiqP8bP474iuWZiWO_kDCD4SPh1Wiw125Y,1120
2
+ pdaltools/_version.py,sha256=pEplHESmf3mY4hIMzjat2Shw3-14Tac3CBkIZS49uF8,74
3
+ pdaltools/add_points_in_pointcloud.py,sha256=UHdC8tFagsehGggkLJCcmBCi8gknJOL4s99f_2TJjsA,12538
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.0.dist-info/METADATA,sha256=r8S5uK_1JposFNefYk7ieT8OR4atRWkGERd1XGIcHYg,5777
15
+ ign_pdal_tools-1.8.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
16
+ ign_pdal_tools-1.8.0.dist-info/top_level.txt,sha256=KvGW0ZzqQbhCKzB5_Tp_buWMZyIgiO2M2krWF_ecOZc,10
17
+ ign_pdal_tools-1.8.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.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.7.11"
1
+ __version__ = "1.8.0"
2
2
 
3
3
 
4
4
  if __name__ == "__main__":
pdaltools/color.py CHANGED
@@ -1,12 +1,14 @@
1
1
  import argparse
2
2
  import tempfile
3
3
  import time
4
- from math import ceil
4
+ from math import ceil, floor
5
+ from pathlib import Path
6
+ from typing import Tuple
5
7
 
6
8
  import numpy as np
7
9
  import pdal
8
10
  import requests
9
- from osgeo import gdal_array
11
+ from osgeo import gdal, gdal_array
10
12
 
11
13
  import pdaltools.las_info as las_info
12
14
  from pdaltools.unlock_file import copy_and_hack_decorator
@@ -28,7 +30,7 @@ def pretty_time_delta(seconds):
28
30
  return "%s%ds" % (sign_string, seconds)
29
31
 
30
32
 
31
- def retry(times, delay, factor=2, debug=False):
33
+ def retry(times, delay, factor, debug=False):
32
34
  def decorator(func):
33
35
  def newfn(*args, **kwargs):
34
36
  attempt = 1
@@ -61,25 +63,28 @@ def is_image_white(filename: str):
61
63
 
62
64
 
63
65
  def download_image_from_geoplateforme(
64
- proj, layer, minx, miny, maxx, maxy, pixel_per_meter, outfile, timeout, check_images
66
+ proj, layer, minx, miny, maxx, maxy, width_pixels, height_pixels, outfile, timeout, check_images
65
67
  ):
66
- # Force a 1-pixel margin in the east and south borders
67
- # to make sure that no point of the pointcloud is on the limit of the last pixel
68
- # to prevent interpolation issues
69
- maxx = maxx + 1 / pixel_per_meter
70
- miny = miny - 1 / pixel_per_meter
68
+ """
69
+ Download image using a wms request to geoplateforme.
70
+
71
+ Args:
72
+ proj (int): epsg code for the projection of the downloaded image.
73
+ layer: which kind of image is downloaded (ORTHOIMAGERY.ORTHOPHOTOS, ORTHOIMAGERY.ORTHOPHOTOS.IRC, ...).
74
+ minx, miny, maxx, maxy: box of the downloaded image.
75
+ width_pixels: width in pixels of the downloaded image.
76
+ height_pixels: height in pixels of the downloaded image.
77
+ outfile: file name of the downloaded file
78
+ timeout: delay after which the request is canceled (in seconds)
79
+ check_images (bool): enable checking if the output image is not a white image
80
+ """
71
81
 
72
82
  # for layer in layers:
73
83
  URL_GPP = "https://data.geopf.fr/wms-r/wms?"
74
84
  URL_FORMAT = "&EXCEPTIONS=text/xml&FORMAT=image/geotiff&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&STYLES="
75
85
  URL_EPSG = "&CRS=EPSG:" + str(proj)
76
86
  URL_BBOX = "&BBOX=" + str(minx) + "," + str(miny) + "," + str(maxx) + "," + str(maxy)
77
- URL_SIZE = (
78
- "&WIDTH="
79
- + str(ceil((maxx - minx) * pixel_per_meter))
80
- + "&HEIGHT="
81
- + str(ceil((maxy - miny) * pixel_per_meter))
82
- )
87
+ URL_SIZE = "&WIDTH=" + str(width_pixels) + "&HEIGHT=" + str(height_pixels)
83
88
 
84
89
  URL = URL_GPP + "LAYERS=" + layer + URL_FORMAT + URL_EPSG + URL_BBOX + URL_SIZE
85
90
 
@@ -96,7 +101,120 @@ def download_image_from_geoplateforme(
96
101
  raise ValueError(f"Downloaded image is white, with stream: {layer}")
97
102
 
98
103
 
104
+ def compute_cells_size(mind: float, maxd: float, pixel_per_meter: float, size_max_gpf: int) -> Tuple[int, int, int]:
105
+ """Compute cell size to have cells of almost equal size, but phased the same way as
106
+ if there had been no paving by forcing cell_size (in pixels) to be an integer
107
+
108
+ Args:
109
+ mind (float): minimum value along the dimension, in meters
110
+ maxd (float): maximum value along the dimension, in meters
111
+ pixel_per_meter (float): resolution (in number of pixels per meter)
112
+ size_max_gpf (int): maximum image size in pixels
113
+
114
+ Returns:
115
+ Tuple[int, int, int]: number of pixels in total, number of cells along the dimension, cell size in pixels
116
+ """
117
+ nb_pixels = ceil((maxd - mind) * pixel_per_meter)
118
+ nb_cells = ceil(nb_pixels / size_max_gpf)
119
+ cell_size_pixels = ceil(nb_pixels / nb_cells) # Force cell size to be an integer
120
+
121
+ return nb_pixels, nb_cells, cell_size_pixels
122
+
123
+
99
124
  @copy_and_hack_decorator
125
+ def download_image(proj, layer, minx, miny, maxx, maxy, pixel_per_meter, outfile, timeout, check_images, size_max_gpf):
126
+ """
127
+ Download image using a wms request to geoplateforme with call of download_image_from_geoplateforme() :
128
+ image are downloaded in blocks then merged, in order to limit the size of geoplateforme requests.
129
+
130
+ Args:
131
+ proj: projection of the downloaded image.
132
+ layer: which kind of image is downloaed (ORTHOIMAGERY.ORTHOPHOTOS, ORTHOIMAGERY.ORTHOPHOTOS.IRC, ...).
133
+ minx, miny, maxx, maxy: box of the downloaded image.
134
+ pixel_per_meter: resolution of the downloaded image.
135
+ outfile: file name of the downloaed file
136
+ timeout: time after the request is canceled
137
+ check_images: check if images is not a white image
138
+ size_max_gpf: block size of downloaded images. (in pixels)
139
+
140
+ return the number of effective requests
141
+ """
142
+
143
+ download_image_from_geoplateforme_retrying = retry(times=9, delay=5, factor=2)(download_image_from_geoplateforme)
144
+
145
+ size_x_p, nb_cells_x, cell_size_x = compute_cells_size(minx, maxx, pixel_per_meter, size_max_gpf)
146
+ size_y_p, nb_cells_y, cell_size_y = compute_cells_size(minx, maxx, pixel_per_meter, size_max_gpf)
147
+
148
+ # the image size is under SIZE_MAX_IMAGE_GPF
149
+ if (size_x_p <= size_max_gpf) and (size_y_p <= size_max_gpf):
150
+ download_image_from_geoplateforme_retrying(
151
+ proj, layer, minx, miny, maxx, maxy, cell_size_x, cell_size_y, outfile, timeout, check_images
152
+ )
153
+ return 1
154
+
155
+ # the image is bigger than the SIZE_MAX_IMAGE_GPF
156
+ # it's preferable to compute it by paving
157
+ with tempfile.TemporaryDirectory() as tmp_dir:
158
+ tmp_gpg_ortho = []
159
+ for line in range(0, nb_cells_y):
160
+ for col in range(0, nb_cells_x):
161
+ # Cope for last line/col that can be slightly smaller than other cells
162
+ remaining_pixels_x = size_x_p - col * cell_size_x
163
+ remaining_pixels_y = size_y_p - line * cell_size_y
164
+ cell_size_x_local = min(cell_size_x, remaining_pixels_x)
165
+ cell_size_y_local = min(cell_size_y, remaining_pixels_y)
166
+
167
+ minx_cell = minx + col * cell_size_x / pixel_per_meter
168
+ maxx_cell = minx_cell + cell_size_x_local / pixel_per_meter
169
+ miny_cell = miny + line * cell_size_y / pixel_per_meter
170
+ maxy_cell = miny_cell + cell_size_y_local / pixel_per_meter
171
+
172
+ cells_ortho_paths = str(Path(tmp_dir)) + f"cell_{col}_{line}.tif"
173
+ download_image_from_geoplateforme_retrying(
174
+ proj,
175
+ layer,
176
+ minx_cell,
177
+ miny_cell,
178
+ maxx_cell,
179
+ maxy_cell,
180
+ cell_size_x_local,
181
+ cell_size_y_local,
182
+ cells_ortho_paths,
183
+ timeout,
184
+ check_images,
185
+ )
186
+ tmp_gpg_ortho.append(cells_ortho_paths)
187
+
188
+ # merge the cells
189
+ with tempfile.NamedTemporaryFile(suffix="_gpf.vrt") as tmp_vrt:
190
+ gdal.BuildVRT(tmp_vrt.name, tmp_gpg_ortho)
191
+ gdal.Translate(outfile, tmp_vrt.name)
192
+
193
+ return nb_cells_x * nb_cells_y
194
+
195
+
196
+ def match_min_max_with_pixel_size(min_d: float, max_d: float, pixel_per_meter: float) -> Tuple[float, float]:
197
+ """Round min/max values along one dimension to the closest multiple of 1 / pixel_per_meter
198
+ It should prevent having to interpolate during a request to the geoplateforme
199
+ in case we use a native resolution.
200
+
201
+ Args:
202
+ min_d (float): minimum value along the dimension, in meters
203
+ max_d (float): maximum value along the dimension, in meters
204
+ pixel_per_meter (float): resolution (in number of pixels per meter)
205
+
206
+ Returns:
207
+ Tuple[float, float]: adapted min / max value
208
+ """
209
+ # Use ceil - 1 instead of ceil to make sure that
210
+ # no point of the pointcloud is on the limit of the first pixel
211
+ min_d = (ceil(min_d * pixel_per_meter) - 1) / pixel_per_meter
212
+ # Use floor + 1 instead of ceil to make sure that no point of the pointcloud is on the limit of the last pixel
213
+ max_d = (floor(max_d * pixel_per_meter) + 1) / pixel_per_meter
214
+
215
+ return min_d, max_d
216
+
217
+
100
218
  def color(
101
219
  input_file: str,
102
220
  output_file: str,
@@ -109,10 +227,14 @@ def color(
109
227
  check_images=False,
110
228
  stream_RGB="ORTHOIMAGERY.ORTHOPHOTOS",
111
229
  stream_IRC="ORTHOIMAGERY.ORTHOPHOTOS.IRC",
230
+ size_max_gpf=5000,
112
231
  ):
113
232
  metadata = las_info.las_info_metadata(input_file)
114
233
  minx, maxx, miny, maxy = las_info.get_bounds_from_header_info(metadata)
115
234
 
235
+ minx, maxx = match_min_max_with_pixel_size(minx, maxx, pixel_per_meter)
236
+ miny, maxy = match_min_max_with_pixel_size(miny, maxy, pixel_per_meter)
237
+
116
238
  if proj == "":
117
239
  proj = las_info.get_epsg_from_header_info(metadata)
118
240
 
@@ -120,8 +242,6 @@ def color(
120
242
 
121
243
  writer_extra_dims = "all"
122
244
 
123
- download_image_from_geoplateforme_retrying = retry(times=9, delay=5, factor=2)(download_image_from_geoplateforme)
124
-
125
245
  if veget_index_file and veget_index_file != "":
126
246
  print(f"Remplissage du champ Deviation à partir du fichier {veget_index_file}")
127
247
  pipeline |= pdal.Filter.colorization(raster=veget_index_file, dimensions="Deviation:1:256.0")
@@ -130,8 +250,18 @@ def color(
130
250
  tmp_ortho = None
131
251
  if color_rvb_enabled:
132
252
  tmp_ortho = tempfile.NamedTemporaryFile(suffix="_rvb.tif")
133
- download_image_from_geoplateforme_retrying(
134
- proj, stream_RGB, minx, miny, maxx, maxy, pixel_per_meter, tmp_ortho.name, timeout_second, check_images
253
+ download_image(
254
+ proj,
255
+ stream_RGB,
256
+ minx,
257
+ miny,
258
+ maxx,
259
+ maxy,
260
+ pixel_per_meter,
261
+ tmp_ortho.name,
262
+ timeout_second,
263
+ check_images,
264
+ size_max_gpf,
135
265
  )
136
266
  # Warning: the initial color is multiplied by 256 despite its initial 8-bits encoding
137
267
  # which turns it to a 0 to 255*256 range.
@@ -143,8 +273,18 @@ def color(
143
273
  tmp_ortho_irc = None
144
274
  if color_ir_enabled:
145
275
  tmp_ortho_irc = tempfile.NamedTemporaryFile(suffix="_irc.tif")
146
- download_image_from_geoplateforme_retrying(
147
- proj, stream_IRC, minx, miny, maxx, maxy, pixel_per_meter, tmp_ortho_irc.name, timeout_second, check_images
276
+ download_image(
277
+ proj,
278
+ stream_IRC,
279
+ minx,
280
+ miny,
281
+ maxx,
282
+ maxy,
283
+ pixel_per_meter,
284
+ tmp_ortho_irc.name,
285
+ timeout_second,
286
+ check_images,
287
+ size_max_gpf,
148
288
  )
149
289
  # Warning: the initial color is multiplied by 256 despite its initial 8-bits encoding
150
290
  # which turns it to a 0 to 255*256 range.
@@ -195,6 +335,13 @@ for 50 cm resolution rasters, use ORTHOIMAGERY.ORTHOPHOTOS.BDORTHO""",
195
335
  help="""WMS raster stream for IRC colorization. Default to ORTHOIMAGERY.ORTHOPHOTOS.IRC
196
336
  Documentation about possible stream : https://geoservices.ign.fr/services-web-experts-ortho""",
197
337
  )
338
+ parser.add_argument(
339
+ "--size-max-GPF",
340
+ type=int,
341
+ default=5000,
342
+ help="Maximum edge size (in pixels) of downloaded images."
343
+ " If input file needs more, several images are downloaded and merged.",
344
+ )
198
345
 
199
346
  return parser.parse_args()
200
347
 
@@ -213,4 +360,5 @@ if __name__ == "__main__":
213
360
  check_images=args.check_images,
214
361
  stream_RGB=args.stream_RGB,
215
362
  stream_IRC=args.stream_IRC,
363
+ size_max_gpf=args.size_max_GPF,
216
364
  )
@@ -1,6 +1,7 @@
1
1
  import argparse
2
2
 
3
3
  import pdal
4
+
4
5
  from pdaltools.las_info import get_writer_parameters_from_reader_metadata
5
6
 
6
7
 
@@ -1,18 +0,0 @@
1
- ign_pdal_tools-1.7.11.dist-info/licenses/LICENSE.md,sha256=iVzCFZTUXeiqP8bP474iuWZiWO_kDCD4SPh1Wiw125Y,1120
2
- pdaltools/_version.py,sha256=lXuB5yCoqB_XdA0YVaGjTGc2J_sFF0RE_IJePTMnyjI,75
3
- pdaltools/add_points_in_las.py,sha256=TGbt5JUkszjmbQiA2LCUntsjz6A8DHb7QPIXGDuEgWA,3643
4
- pdaltools/add_points_in_pointcloud.py,sha256=UHdC8tFagsehGggkLJCcmBCi8gknJOL4s99f_2TJjsA,12538
5
- pdaltools/color.py,sha256=-a0ramyLMkZX4-M3siPq1zIio5u-a0p8UzvgcfoU6zU,8313
6
- pdaltools/las_add_buffer.py,sha256=sBpTywlfsHHS8KuCUa-eydB2hylshEvjrMQt5TrqXb8,11275
7
- pdaltools/las_clip.py,sha256=GvEOYu8RXV68e35kU8i42GwSkbo4P9TvmS6rkrdPmFM,1034
8
- pdaltools/las_info.py,sha256=lMKxKzsViptDENI1wOlANG4qOvdc19ixyasYKD-N1ck,9512
9
- pdaltools/las_merge.py,sha256=tcFVueV9X9nNEaoAl5zCduY5DETlBg63MAgP2SuKiNo,4121
10
- pdaltools/las_remove_dimensions.py,sha256=u_3VfkabkN_Y3eDLdJwCLVGpondvIx0f0v0RdFDoAFw,1792
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=pIThdWMNkTph0xgJVVRaM1o9aUMQhM6804PscScB3JI,1963
15
- ign_pdal_tools-1.7.11.dist-info/METADATA,sha256=Bh4cFjnGLLt4DpiPw8GQuknhDTrLQqx3H5EjkFzV1n4,5745
16
- ign_pdal_tools-1.7.11.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
17
- ign_pdal_tools-1.7.11.dist-info/top_level.txt,sha256=KvGW0ZzqQbhCKzB5_Tp_buWMZyIgiO2M2krWF_ecOZc,10
18
- ign_pdal_tools-1.7.11.dist-info/RECORD,,
@@ -1,104 +0,0 @@
1
- import argparse
2
-
3
- import geopandas
4
- import numpy as np
5
- import pdal
6
-
7
- from pdaltools.las_info import get_writer_parameters_from_reader_metadata, las_info_metadata, get_bounds_from_header_info
8
-
9
-
10
- def extract_points_from_geo(input_geo: str):
11
- file = open(input_geo)
12
- df = geopandas.read_file(file)
13
- return df.get_coordinates(ignore_index=True, include_z=True)
14
-
15
- def point_in_bound(bound_minx, bound_maxx, bound_miny, bound_maxy, pt_x, pt_y):
16
- return pt_x >= bound_minx and pt_x <= bound_maxx and pt_y >= bound_miny and pt_y <= bound_maxy
17
-
18
- def add_points_in_las(input_las: str, input_geo: str, output_las: str, inside_las: bool, values_dimensions: {}):
19
- points_geo = extract_points_from_geo(input_geo)
20
- pipeline = pdal.Pipeline() | pdal.Reader.las(input_las)
21
- pipeline.execute()
22
- points_las = pipeline.arrays[0]
23
- dimensions = list(points_las.dtype.fields.keys())
24
-
25
- if inside_las:
26
- mtd = las_info_metadata(input_las)
27
- bound_minx, bound_maxx, bound_miny, bound_maxy = get_bounds_from_header_info(mtd)
28
-
29
- for i in points_geo.index:
30
- if inside_las :
31
- if not point_in_bound(bound_minx, bound_maxx, bound_miny, bound_maxy, points_geo["x"][i], points_geo["y"][i]):
32
- continue
33
- pt_las = np.empty(1, dtype=points_las.dtype)
34
- pt_las[0][dimensions.index("X")] = points_geo["x"][i]
35
- pt_las[0][dimensions.index("Y")] = points_geo["y"][i]
36
- pt_las[0][dimensions.index("Z")] = points_geo["z"][i]
37
- for val in values_dimensions:
38
- pt_las[0][dimensions.index(val)] = values_dimensions[val]
39
- points_las = np.append(points_las, pt_las, axis=0)
40
-
41
- params = get_writer_parameters_from_reader_metadata(pipeline.metadata)
42
- pipeline_end = pdal.Pipeline(arrays=[points_las])
43
- pipeline_end |= pdal.Writer.las(output_las, forward="all", **params)
44
- pipeline_end.execute()
45
-
46
-
47
- def parse_args():
48
- parser = argparse.ArgumentParser("Add points from geometry file in a las/laz file.")
49
- parser.add_argument("--input_file", "-i", type=str, help="Las/Laz input file")
50
- parser.add_argument("--output_file", "-o", type=str, help="Las/Laz output file.")
51
- parser.add_argument("--input_geo_file", "-g", type=str, help="Geometry input file.")
52
- parser.add_argument("--inside_las", "-l", type=str, help="Keep points only inside the las boundary.")
53
- parser.add_argument(
54
- "--dimensions",
55
- "-d",
56
- metavar="KEY=VALUE",
57
- nargs="+",
58
- help="Set a number of key-value pairs corresponding to value "
59
- "needed in points added in the output las; key should be included in the input las.",
60
- )
61
- return parser.parse_args()
62
-
63
-
64
- def is_nature(value, nature):
65
- if value is None:
66
- return False
67
- try:
68
- nature(value)
69
- return True
70
- except:
71
- return False
72
-
73
-
74
- def parse_var(s):
75
- items = s.split("=")
76
- key = items[0].strip()
77
- if len(items) > 1:
78
- value = "=".join(items[1:])
79
- if is_nature(value, int):
80
- value = int(value)
81
- elif is_nature(value, float):
82
- value = float(value)
83
- return (key, value)
84
-
85
-
86
- def parse_vars(items):
87
- d = {}
88
- if items:
89
- for item in items:
90
- key, value = parse_var(item)
91
- d[key] = value
92
- return d
93
-
94
-
95
- if __name__ == "__main__":
96
- args = parse_args()
97
- added_dimensions = parse_vars(args.dimensions)
98
- add_points_in_las(
99
- input_las=args.input_file,
100
- input_geo=args.input_geo_file,
101
- output_las=args.input_file if args.output_file is None else args.output_file,
102
- inside_las=args.inside_las,
103
- values_dimensions=added_dimensions,
104
- )