ign-pdal-tools 1.15.7__tar.gz → 1.15.8__tar.gz

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.
Files changed (44) hide show
  1. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/PKG-INFO +1 -1
  2. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/ign_pdal_tools.egg-info/PKG-INFO +1 -1
  3. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/_version.py +1 -1
  4. ign_pdal_tools-1.15.8/pdaltools/las_merge.py +144 -0
  5. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_merge.py +21 -1
  6. ign_pdal_tools-1.15.7/pdaltools/las_merge.py +0 -109
  7. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/LICENSE.md +0 -0
  8. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/README.md +0 -0
  9. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/ign_pdal_tools.egg-info/SOURCES.txt +0 -0
  10. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/ign_pdal_tools.egg-info/dependency_links.txt +0 -0
  11. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/ign_pdal_tools.egg-info/top_level.txt +0 -0
  12. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/add_points_in_pointcloud.py +0 -0
  13. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/color.py +0 -0
  14. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/create_random_laz.py +0 -0
  15. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/download_image.py +0 -0
  16. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_add_buffer.py +0 -0
  17. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_clip.py +0 -0
  18. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_comparison.py +0 -0
  19. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_info.py +0 -0
  20. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_remove_dimensions.py +0 -0
  21. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/las_rename_dimension.py +0 -0
  22. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/pcd_info.py +0 -0
  23. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/replace_area_in_pointcloud.py +0 -0
  24. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/replace_attribute_in_las.py +0 -0
  25. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/standardize_format.py +0 -0
  26. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pdaltools/unlock_file.py +0 -0
  27. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/pyproject.toml +0 -0
  28. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/setup.cfg +0 -0
  29. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_add_points_in_pointcloud.py +0 -0
  30. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_color.py +0 -0
  31. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_create_random_laz.py +0 -0
  32. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_download_image.py +0 -0
  33. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_add_buffer.py +0 -0
  34. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_clip.py +0 -0
  35. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_comparison.py +0 -0
  36. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_info.py +0 -0
  37. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_remove_dimensions.py +0 -0
  38. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_las_rename_dimension.py +0 -0
  39. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_pcd_info.py +0 -0
  40. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_pdal.py +0 -0
  41. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_replace_area_in_pointcloud.py +0 -0
  42. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_replace_attribute_in_las.py +0 -0
  43. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_standardize_format.py +0 -0
  44. {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.8}/test/test_unlock.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ign-pdal-tools
3
- Version: 1.15.7
3
+ Version: 1.15.8
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ign-pdal-tools
3
- Version: 1.15.7
3
+ Version: 1.15.8
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
@@ -1,4 +1,4 @@
1
- __version__ = "1.15.7"
1
+ __version__ = "1.15.8"
2
2
 
3
3
 
4
4
  if __name__ == "__main__":
@@ -0,0 +1,144 @@
1
+ import logging
2
+ import os
3
+
4
+ import pdal
5
+
6
+ from pdaltools.las_info import parse_filename
7
+
8
+
9
+ def create_filenames_suffixes(file: str, tile_width: int = 1000, tile_coord_scale: int = 1000):
10
+ """Generate the name of the tiles around the input LIDAR tile
11
+ It supposes that the file names are formatted as {prefix1}_{prefix2}_{coordx}_{coordy}_{suffix}
12
+ with coordx and coordy having at least 4 digits
13
+
14
+ For example Semis_2021_0000_1111_LA93_IGN69.las
15
+
16
+ Generates only the suffix part of the filename, for example, for file like above, it will generate:
17
+ _0000_1112_LA93_IGN69.las
18
+ _0001_1112_LA93_IGN69.las
19
+ ...
20
+
21
+ Args:
22
+ file(str): name of LIDAR file
23
+ tile width (int): width of tiles in meters (usually 1000m)
24
+ tile_coord_scale (int) : scale used in the filename to describe coordinates in meters
25
+ (usually 1000m)
26
+ Returns:
27
+ list_input(list): List of LIDAR's filename suffix.
28
+ """
29
+
30
+ # Create name of LIDAR tiles who cercle the tile
31
+ # # Parameters
32
+ _prefix, coord_x, coord_y, _suffix = parse_filename(file)
33
+ offset = int(tile_width / tile_coord_scale)
34
+ # On left
35
+ _tile_hl = f"_{(coord_x - offset):04d}_{(coord_y + offset):04d}_{_suffix}"
36
+ _tile_ml = f"_{(coord_x - offset):04d}_{coord_y:04d}_{_suffix}"
37
+ _tile_bl = f"_{(coord_x - offset):04d}_{(coord_y - offset):04d}_{_suffix}"
38
+ # On Right
39
+ _tile_hr = f"_{(coord_x + offset):04d}_{(coord_y + offset):04d}_{_suffix}"
40
+ _tile_mr = f"_{(coord_x + offset):04d}_{coord_y:04d}_{_suffix}"
41
+ _tile_br = f"_{(coord_x + offset):04d}_{(coord_y - offset):04d}_{_suffix}"
42
+ # Above
43
+ _tile_a = f"_{coord_x:04d}_{(coord_y + offset):04d}_{_suffix}"
44
+ # Below
45
+ _tile_b = f"_{coord_x:04d}_{(coord_y - offset):04d}_{_suffix}"
46
+ # Return the severals tile's names
47
+ return _tile_hl, _tile_ml, _tile_bl, _tile_a, _tile_b, _tile_hr, _tile_mr, _tile_br
48
+
49
+
50
+ def match_suffix_with_filenames(suffix_list: list, all_files: list, las_dir: str):
51
+ """Match suffix list with real filenames
52
+ Args:
53
+ suffix_list (list): List of suffix patterns to match
54
+ all_files (list): List of all files in las_dir
55
+ las_dir (str): Directory of pointclouds
56
+
57
+ Returns:
58
+ las_list(List): List of matched files
59
+ """
60
+ las_list = []
61
+ for suffix in suffix_list:
62
+ matches = [filename for filename in all_files if filename.endswith(suffix)]
63
+ if len(matches) == 0:
64
+ logging.info(f"NOK : {suffix}")
65
+ else:
66
+ # in case of multiple matches, select the most recent year (ex: Semis_2021_ before Semis_2020_ )
67
+ matches.sort(reverse=True)
68
+ selected = matches[0]
69
+ if len(matches) > 1:
70
+ logging.warning(f"Multiple matches for {suffix} : {matches} ; taking {selected}")
71
+
72
+ # Append full path
73
+ las_list.append(os.path.join(las_dir, selected))
74
+ return las_list
75
+
76
+
77
+ def create_tiles_list(all_files, las_dir, input_file, tile_width=1000, tile_coord_scale=1000):
78
+ """Return the paths of 8 tiles around the tile + the input tile
79
+ Args:
80
+ all_files (list): list of all files in las_dir
81
+ las_dir (str): directory of pointclouds
82
+ input_file (str): path to queried LIDAR tile
83
+ tile_width (int): Width of a tile(in the reference unit: 1m)
84
+ tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
85
+ 1000 * 1m (with 1m being the reference)
86
+
87
+ Returns:
88
+ list_files: list of tiles
89
+ """
90
+
91
+ # Return list 8 tiles around the tile, but only the suffix part of the name.
92
+ suffix_list = create_filenames_suffixes(os.path.basename(input_file), tile_width, tile_coord_scale)
93
+
94
+ # Match suffix patterns with real files
95
+ list_files = match_suffix_with_filenames(suffix_list, all_files, las_dir)
96
+
97
+ # Appending queried tile to list
98
+ list_files.append(input_file)
99
+
100
+ return list_files
101
+
102
+
103
+ def create_list(las_dir, input_file, tile_width=1000, tile_coord_scale=1000):
104
+ """Return the paths of 8 tiles around the tile + the input tile
105
+ Args:
106
+ las_dir (str): directory of pointclouds
107
+ input_file (str): path to queried LIDAR tile
108
+ tile_width (int): Width of a tile(in the reference unit: 1m)
109
+ tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
110
+ 1000 * 1m (with 1m being the reference)
111
+
112
+ Returns:
113
+ list_files: list of tiles
114
+ """
115
+
116
+ # list files on the disk
117
+ all_files = os.listdir(las_dir)
118
+
119
+ # call the function with the list of files
120
+ return create_tiles_list(all_files, las_dir, input_file, tile_width, tile_coord_scale)
121
+
122
+
123
+ def las_merge(las_dir, input_file, merge_file, tile_width=1000, tile_coord_scale=1000):
124
+ """Merge LIDAR tiles around input_file tile
125
+ Args:
126
+ las_dir (str): directory of pointclouds (to look for neigboprs)
127
+ input_file (str): name of query LIDAR file (with extension)
128
+ output_file (str): path to output
129
+ tile_width (int): Width of a tile(in the reference unit: 1m)
130
+ tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
131
+ 1000 * 1m (with 1m being the reference)
132
+ """
133
+ # List files to merge
134
+ files = create_list(las_dir, input_file, tile_width, tile_coord_scale)
135
+ if len(files) > 0:
136
+ # Merge
137
+ pipeline = pdal.Pipeline()
138
+ for f in files:
139
+ pipeline |= pdal.Reader.las(filename=f)
140
+ pipeline |= pdal.Filter.merge()
141
+ pipeline |= pdal.Writer.las(filename=merge_file, forward="all")
142
+ pipeline.execute()
143
+ else:
144
+ raise ValueError("List of valid tiles is empty : stop processing")
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
  import os
3
+ from pathlib import Path
3
4
  from test.utils import assert_header_info_are_similar
4
5
 
5
6
  import laspy
6
7
  import numpy as np
7
8
 
8
- from pdaltools.las_merge import las_merge
9
+ from pdaltools.las_merge import create_tiles_list, las_merge
9
10
 
10
11
  TEST_PATH = os.path.dirname(os.path.abspath(__file__))
11
12
  TMP_PATH = os.path.join(TEST_PATH, "tmp")
@@ -74,6 +75,25 @@ def test_las_merge():
74
75
  assert_header_info_are_similar(output_file, input_file)
75
76
 
76
77
 
78
+ def test_create_tiles_list_with_different_prefix():
79
+ files = [f"Semis_2021_0770_{6200 + i}_LA93_IGN69.las" for i in range(1, 10)]
80
+
81
+ # add another tile with different year in prefix
82
+ files.append("Semis_2018_0770_6200_LA93_IGN69.las")
83
+ files.append("Semis_2020_0770_6200_LA93_IGN69.las")
84
+ files.append("Semis_2019_0770_6200_LA93_IGN69.las")
85
+
86
+ result = create_tiles_list(files, "las_dir", "las_dir/Semis_2021_0770_6201_LA93_IGN69.las")
87
+ result_path = [Path(p) for p in result]
88
+
89
+ expected_path = [
90
+ Path("las_dir") / Path("Semis_2021_0770_6202_LA93_IGN69.las"),
91
+ Path("las_dir") / Path("Semis_2020_0770_6200_LA93_IGN69.las"),
92
+ Path("las_dir") / Path("Semis_2021_0770_6201_LA93_IGN69.las"),
93
+ ]
94
+ assert result_path == expected_path
95
+
96
+
77
97
  if __name__ == "__main__":
78
98
  logging.basicConfig(level=logging.INFO)
79
99
  test_las_merge()
@@ -1,109 +0,0 @@
1
- import logging
2
- import os
3
-
4
- import pdal
5
-
6
- from pdaltools.las_info import parse_filename
7
-
8
-
9
- def create_filenames(file: str, tile_width: int = 1000, tile_coord_scale: int = 1000):
10
- """Generate the name of the tiles around the input LIDAR tile
11
- It supposes that the file names are formatted as {prefix1}_{prefix2}_{coordx}_{coordy}_{suffix}
12
- with coordx and coordy having at least 4 digits
13
-
14
- For example Semis_2021_0000_1111_LA93_IGN69.las
15
-
16
- Args:
17
- file(str): name of LIDAR file
18
- tile width (int): width of tiles in meters (usually 1000m)
19
- tile_coord_scale (int) : scale used in the filename to describe coordinates in meters
20
- (usually 1000m)
21
- Returns:
22
- list_input(list): List of LIDAR's name
23
- """
24
-
25
- # Create name of LIDAR tiles who cercle the tile
26
- # # Parameters
27
- _prefix, coord_x, coord_y, _suffix = parse_filename(file)
28
- offset = int(tile_width / tile_coord_scale)
29
- # On left
30
- _tile_hl = f"{_prefix}_{(coord_x - offset):04d}_{(coord_y + offset):04d}_{_suffix}"
31
- _tile_ml = f"{_prefix}_{(coord_x - offset):04d}_{coord_y:04d}_{_suffix}"
32
- _tile_bl = f"{_prefix}_{(coord_x - offset):04d}_{(coord_y - offset):04d}_{_suffix}"
33
- # On Right
34
- _tile_hr = f"{_prefix}_{(coord_x + offset):04d}_{(coord_y + offset):04d}_{_suffix}"
35
- _tile_mr = f"{_prefix}_{(coord_x + offset):04d}_{coord_y:04d}_{_suffix}"
36
- _tile_br = f"{_prefix}_{(coord_x + offset):04d}_{(coord_y - offset):04d}_{_suffix}"
37
- # Above
38
- _tile_a = f"{_prefix}_{coord_x:04d}_{(coord_y + offset):04d}_{_suffix}"
39
- # Below
40
- _tile_b = f"{_prefix}_{coord_x:04d}_{(coord_y - offset):04d}_{_suffix}"
41
- # Return the severals tile's names
42
- return _tile_hl, _tile_ml, _tile_bl, _tile_a, _tile_b, _tile_hr, _tile_mr, _tile_br
43
-
44
-
45
- def check_tiles_exist(list_las: list):
46
- """Check if pointclouds exist
47
- Args:
48
- list_las (list): Filenames of the tiles around the LIDAR tile
49
-
50
- Returns:
51
- li(List): Pruned list of filenames with only existing files
52
- """
53
- li = []
54
- for i in list_las:
55
- if not os.path.exists(i):
56
- logging.info(f"NOK : {i}")
57
- pass
58
- else:
59
- li.append(i)
60
- return li
61
-
62
-
63
- def create_list(las_dir, input_file, tile_width=1000, tile_coord_scale=1000):
64
- """Return the paths of 8 tiles around the tile + the input tile
65
- Args:
66
- las_dir (str): directory of pointclouds
67
- input_file (str): path to queried LIDAR tile
68
- tile_width (int): Width of a tile(in the reference unit: 1m)
69
- tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
70
- 1000 * 1m (with 1m being the reference)
71
-
72
- Returns:
73
- list_files(li): list of tiles
74
- """
75
-
76
- # Return list 8 tiles around the tile
77
- list_input = create_filenames(os.path.basename(input_file), tile_width, tile_coord_scale)
78
- # List pointclouds
79
- li = [os.path.join(las_dir, e) for e in list_input]
80
- # Keep only existing files
81
- li = check_tiles_exist(li)
82
- # Appending queried tile to list
83
- li.append(input_file)
84
-
85
- return li
86
-
87
-
88
- def las_merge(las_dir, input_file, merge_file, tile_width=1000, tile_coord_scale=1000):
89
- """Merge LIDAR tiles around input_file tile
90
- Args:
91
- las_dir (str): directory of pointclouds (to look for neigboprs)
92
- input_file (str): name of query LIDAR file (with extension)
93
- output_file (str): path to output
94
- tile_width (int): Width of a tile(in the reference unit: 1m)
95
- tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
96
- 1000 * 1m (with 1m being the reference)
97
- """
98
- # List files to merge
99
- files = create_list(las_dir, input_file, tile_width, tile_coord_scale)
100
- if len(files) > 0:
101
- # Merge
102
- pipeline = pdal.Pipeline()
103
- for f in files:
104
- pipeline |= pdal.Reader.las(filename=f)
105
- pipeline |= pdal.Filter.merge()
106
- pipeline |= pdal.Writer.las(filename=merge_file, forward="all")
107
- pipeline.execute()
108
- else:
109
- raise ValueError("List of valid tiles is empty : stop processing")