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.
- {ign_pdal_tools-1.7.11.dist-info → ign_pdal_tools-1.8.0.dist-info}/METADATA +9 -4
- ign_pdal_tools-1.8.0.dist-info/RECORD +17 -0
- {ign_pdal_tools-1.7.11.dist-info → ign_pdal_tools-1.8.0.dist-info}/WHEEL +1 -1
- pdaltools/_version.py +1 -1
- pdaltools/color.py +169 -21
- pdaltools/las_remove_dimensions.py +1 -0
- ign_pdal_tools-1.7.11.dist-info/RECORD +0 -18
- pdaltools/add_points_in_las.py +0 -104
- {ign_pdal_tools-1.7.11.dist-info → ign_pdal_tools-1.8.0.dist-info}/licenses/LICENSE.md +0 -0
- {ign_pdal_tools-1.7.11.dist-info → ign_pdal_tools-1.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ign-pdal-tools
|
|
3
|
-
Version: 1.
|
|
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
|
|
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,,
|
pdaltools/_version.py
CHANGED
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
|
|
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,
|
|
66
|
+
proj, layer, minx, miny, maxx, maxy, width_pixels, height_pixels, outfile, timeout, check_images
|
|
65
67
|
):
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
134
|
-
proj,
|
|
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
|
-
|
|
147
|
-
proj,
|
|
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,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,,
|
pdaltools/add_points_in_las.py
DELETED
|
@@ -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
|
-
)
|
|
File without changes
|
|
File without changes
|