ign-pdal-tools 1.15.7__tar.gz → 1.15.9__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.
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/PKG-INFO +1 -1
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/ign_pdal_tools.egg-info/PKG-INFO +1 -1
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/_version.py +1 -1
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/color.py +1 -1
- ign_pdal_tools-1.15.9/pdaltools/las_merge.py +144 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_merge.py +21 -1
- ign_pdal_tools-1.15.7/pdaltools/las_merge.py +0 -109
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/LICENSE.md +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/README.md +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/ign_pdal_tools.egg-info/SOURCES.txt +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/ign_pdal_tools.egg-info/dependency_links.txt +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/ign_pdal_tools.egg-info/top_level.txt +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/add_points_in_pointcloud.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/create_random_laz.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/download_image.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_add_buffer.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_clip.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_comparison.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_info.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_remove_dimensions.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/las_rename_dimension.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/pcd_info.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/replace_area_in_pointcloud.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/replace_attribute_in_las.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/standardize_format.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pdaltools/unlock_file.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/pyproject.toml +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/setup.cfg +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_add_points_in_pointcloud.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_color.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_create_random_laz.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_download_image.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_add_buffer.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_clip.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_comparison.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_info.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_remove_dimensions.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_las_rename_dimension.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_pcd_info.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_pdal.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_replace_area_in_pointcloud.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_replace_attribute_in_las.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_standardize_format.py +0 -0
- {ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/test/test_unlock.py +0 -0
|
@@ -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")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ign_pdal_tools-1.15.7 → ign_pdal_tools-1.15.9}/ign_pdal_tools.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|