Skip to content

Commit 67dc30b

Browse files
committed
fix: las_merge: handle filenames with different prefix
1 parent 1900640 commit 67dc30b

File tree

3 files changed

+86
-32
lines changed

3 files changed

+86
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# 1.15.7
22
- [update] Update las_comparison with tolerance
33
- [update] Colorisation of Las with stream or files
4+
- [fix] las_merge: handle filenames with different prefix
45

56
# 1.15.6
67
- [fix] fix use of tempory file in windows

pdaltools/las_merge.py

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,83 +6,118 @@
66
from pdaltools.las_info import parse_filename
77

88

9-
def create_filenames(file: str, tile_width: int = 1000, tile_coord_scale: int = 1000):
9+
def create_filenames_suffixes(file: str, tile_width: int = 1000, tile_coord_scale: int = 1000):
1010
"""Generate the name of the tiles around the input LIDAR tile
1111
It supposes that the file names are formatted as {prefix1}_{prefix2}_{coordx}_{coordy}_{suffix}
1212
with coordx and coordy having at least 4 digits
1313
1414
For example Semis_2021_0000_1111_LA93_IGN69.las
1515
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+
1621
Args:
1722
file(str): name of LIDAR file
1823
tile width (int): width of tiles in meters (usually 1000m)
1924
tile_coord_scale (int) : scale used in the filename to describe coordinates in meters
2025
(usually 1000m)
2126
Returns:
22-
list_input(list): List of LIDAR's name
27+
list_input(list): List of LIDAR's filename suffix.
2328
"""
2429

2530
# Create name of LIDAR tiles who cercle the tile
2631
# # Parameters
2732
_prefix, coord_x, coord_y, _suffix = parse_filename(file)
2833
offset = int(tile_width / tile_coord_scale)
2934
# 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}"
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}"
3338
# 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}"
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}"
3742
# Above
38-
_tile_a = f"{_prefix}_{coord_x:04d}_{(coord_y + offset):04d}_{_suffix}"
43+
_tile_a = f"_{coord_x:04d}_{(coord_y + offset):04d}_{_suffix}"
3944
# Below
40-
_tile_b = f"{_prefix}_{coord_x:04d}_{(coord_y - offset):04d}_{_suffix}"
45+
_tile_b = f"_{coord_x:04d}_{(coord_y - offset):04d}_{_suffix}"
4146
# Return the severals tile's names
4247
return _tile_hl, _tile_ml, _tile_bl, _tile_a, _tile_b, _tile_hr, _tile_mr, _tile_br
4348

4449

45-
def check_tiles_exist(list_las: list):
46-
"""Check if pointclouds exist
50+
def match_suffix_with_filenames(suffix_list: list, all_files: list, las_dir: str):
51+
"""Match suffix list with real filenames
4752
Args:
48-
list_las (list): Filenames of the tiles around the LIDAR tile
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
4956
5057
Returns:
51-
li(List): Pruned list of filenames with only existing files
58+
las_list(List): List of matched files
5259
"""
53-
li = []
54-
for i in list_las:
55-
if not os.path.exists(i):
56-
logging.info(f"NOK : {i}")
57-
pass
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}")
5865
else:
59-
li.append(i)
60-
return li
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}")
6171

72+
# Append full path
73+
las_list.append(os.path.join(las_dir, selected))
74+
return las_list
6275

63-
def create_list(las_dir, input_file, tile_width=1000, tile_coord_scale=1000):
76+
77+
def create_list_from_list(all_files, las_dir, input_file, tile_width=1000, tile_coord_scale=1000):
6478
"""Return the paths of 8 tiles around the tile + the input tile
6579
Args:
80+
all_files (list): list of all files in las_dir
6681
las_dir (str): directory of pointclouds
6782
input_file (str): path to queried LIDAR tile
6883
tile_width (int): Width of a tile(in the reference unit: 1m)
6984
tile_coord_scale (int): Scale used in filename to describe coordinates (usually kilometers)
7085
1000 * 1m (with 1m being the reference)
7186
7287
Returns:
73-
list_files(li): list of tiles
88+
list_files: list of tiles
7489
"""
7590

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)
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+
8297
# Appending queried tile to list
83-
li.append(input_file)
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)
84118

85-
return li
119+
# call the function with the list of files
120+
return create_list_from_list(all_files, las_dir, input_file, tile_width, tile_coord_scale)
86121

87122

88123
def las_merge(las_dir, input_file, merge_file, tile_width=1000, tile_coord_scale=1000):

test/test_las_merge.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import laspy
66
import numpy as np
77

8-
from pdaltools.las_merge import las_merge
8+
from pdaltools.las_merge import create_list_from_list, las_merge
99

1010
TEST_PATH = os.path.dirname(os.path.abspath(__file__))
1111
TMP_PATH = os.path.join(TEST_PATH, "tmp")
@@ -74,6 +74,24 @@ def test_las_merge():
7474
assert_header_info_are_similar(output_file, input_file)
7575

7676

77+
def test_create_list_with_different_prefix():
78+
files = [f"Semis_2021_0770_{6200 + i}_LA93_IGN69.las" for i in range(1, 10)]
79+
80+
# add another tile with different year in prefix
81+
files.append("Semis_2018_0770_6200_LA93_IGN69.las")
82+
files.append("Semis_2020_0770_6200_LA93_IGN69.las")
83+
files.append("Semis_2019_0770_6200_LA93_IGN69.las")
84+
85+
result = create_list_from_list(files, "las_dir", "las_dir/Semis_2021_0770_6201_LA93_IGN69.las")
86+
87+
expected = [
88+
"las_dir/Semis_2021_0770_6202_LA93_IGN69.las",
89+
"las_dir/Semis_2020_0770_6200_LA93_IGN69.las",
90+
"las_dir/Semis_2021_0770_6201_LA93_IGN69.las",
91+
]
92+
assert result == expected
93+
94+
7795
if __name__ == "__main__":
7896
logging.basicConfig(level=logging.INFO)
7997
test_las_merge()

0 commit comments

Comments
 (0)