Skip to content

Commit 39e5a85

Browse files
committed
Convert paths from shapefile using mount points
1 parent 165749e commit 39e5a85

17 files changed

+410
-36
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# CHANGELOG
2-
2+
- Possibilité d'utiliser des points de montage pour rediriger les chemins donnés dans le shapefile vers un autre dossier
3+
- [Breaking change] Utilisation d'un shaepfile pour définir les fichiers donneurs à utiliser pour chaque zone
34
- génération de la carte d'indice même quand il n'y a pas de points à ajouter
45

56
## 1.1.1

README.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
# patchwork
2-
Patchwork est un outil permettant d'enrichir un fichier lidar à haute densité avec des points d'un fichier à basse densité dans les secteurs où le premier fichier n'a pas de point mais où le second en possède.
2+
Patchwork est un outil permettant d'enrichir un fichier lidar à haute densité avec des points d'un ou plusieurs fichiers à basse densité dans les secteurs où le premier fichier n'a pas de point mais où le second en possède.
33

44
## Fonctionnement
55
Les données en entrée sont:
66
- un fichier lidar que l'ont souhaite enrichir
7-
- un fichier lidar contenant des points supplémentaires
7+
- un fichier shapefile, décrivant les fichiers qui serviront à enrichir le fichier lidar et les zones d'application potentielles (détails dans [Définition du fichier shapefile](#définition-du-fichier-shapefile))
88

99
En sortie il y a :
10-
- Un fichier, copie du premier en entrée, enrichi des points voulus
10+
- Un fichier, copie du premier en entrée, enrichi des points des fichiers basse densité dans les zones identifiées.
1111

12-
Les deux fichiers d'entrée sont découpés en tuiles carrées, généralement d'1m². Si une tuile du fichier à enrichir ne contient aucun point ayant le classement qui nous intéresse, on prend les points de la tuile de même emplacement du fichier de points supplémentaire.
12+
Les deux fichiers d'entrée sont découpés en mailles carrées, part défaut d'1m². Si une tuile du fichier à enrichir ne contient aucun point ayant le classement qui nous intéresse, on prend les points de la tuile de même emplacement du fichier de points supplémentaire.
1313

14-
L'appartenance à une tuile est décidée par un arrondi par défaut, c'est-à-dire que tous les éléments de [n, n+1[ (ouvert en n+1) font parti de la même tuile.
14+
L'appartenance à une tuile est décidée par un arrondi par défaut, c'est-à-dire que tous les éléments de [n, n+1[ (ouvert en n+1) font partie de la même tuile.
1515

1616
## Installation
1717
pré-requis: installer anaconda
@@ -24,24 +24,23 @@ git clone https://github.com/IGNF/patchwork.git
2424
conda env create -f environment.yml
2525
conda activate patchwork
2626
```
27-
## utilisation
27+
## Utilisation
2828

2929
Le script d'ajout de points peut être lancé via :
30+
```bash
31+
python main.py \
32+
filepath.RECIPIENT_DIRECTORY=[dossier parent du fichier receveur] \
33+
filepath.RECIPIENT_NAME=[nom du fichier receveur] \
34+
filepath.SHP_DIRECTORY=[dossier parent du shapefile] \
35+
filepath.SHP_NAME=[nom du fichier shapefile] \
36+
filepath.OUTPUT_DIR=[dossier de sortie] \
37+
filepath.OUTPUT_NAME=[nom du fichier de sortie] \
38+
[autres options]
3039
```
31-
python main.py filepath.DONOR_FILE=[chemin fichier donneur] filepath.RECIPIENT_FILE=[chemin fichier receveur] filepath.OUTPUT_FILE=[chemin fichier de sortie] [autres options]
32-
```
33-
Les différentes options, modifiables soit dans le fichier `configs/configs_patchwork.yaml`, soit en ligne de commande comme indiqué juste au-dessus :
34-
35-
filepath.DONOR_DIRECTORY : Le répertoire du fichier qui peut donner des points à ajouter
36-
filepath.DONOR_NAME : Le nom du fichier qui peut donner des points à ajouter
37-
filepath.RECIPIENT_DIRECTORY : Le répertoire du fichier qui va obtenir des points en plus
38-
filepath.RECIPIENT_NAME : Le nom du fichier qui va obtenir des points en plus
39-
filepath.OUTPUT_DIR : Le répertoire du fichier en sortie
40-
filepath.OUTPUT_NAME : Le nom du fichier en sortie
41-
filepath.OUTPUT_INDICES_MAP_DIR : Le répertoire de sortie du fichier d'indice
42-
filepath.OUTPUT_INDICES_MAP_NAME : Le nom de sortie du fichier d'indice
43-
44-
DONOR_CLASS_LIST : Défaut [2, 22]. La liste des classes des points du fichier donneur qui peuvent être ajoutés.
45-
RECIPIENT_CLASS_LIST : Défaut [2, 3, 9, 17]. La liste des classes des points du fichier receveur qui, s'ils sont absents dans une cellule, justifirons de prendre les points du fichier donneur de la même cellule
46-
TILE_SIZE : Défaut 1000. Taille du côté de l'emprise carrée représentée par les fichiers lidar d'entrée
47-
PATCH_SIZE : Défaut 1. taille en mètre du côté d'une cellule (doit être un diviseur de TILE_SIZE, soit pour 1000 : 0.25, 0.5, 2, 4, 5, 10, 25...)
40+
Les différentes options sont modifiables soit dans le fichier `configs/configs_patchwork.yaml`, soit en ligne de commande comme indiqué juste au-dessus.
41+
Voir le fichier [config_patchwork.yaml](configs/configs_patchwork.yaml) pour le détail des options
42+
43+
44+
## Définition du fichier shapefile
45+
46+
TODO

configs/configs_patchwork.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ filepath:
4040
# path to this subdirectory can be configured using "DONOR_SUBDIRECTORY"
4141
DONOR_SUBDIRECTORY: "data"
4242

43+
mount_points:
44+
- ORIGINAL_PATH: \\store\my-store # WARNING: do NOT use quotes around the path if it contains \\
45+
MOUNTED_PATH: /my_mounted_store/
46+
ORIGINAL_PLATFORM_IS_WINDOWS: true
47+
4348
CRS: 2154
4449

4550
DONOR_CLASS_LIST: [2, 22]

patchwork/patchwork.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ def patchwork(config: DictConfig):
211211

212212
shapefile_path = os.path.join(config.filepath.SHP_DIRECTORY, config.filepath.SHP_NAME)
213213
donor_info_df = get_donor_info_from_shapefile(
214-
shapefile_path,
215-
x_shapefile,
216-
y_shapefile,
217-
config.filepath.DONOR_SUBDIRECTORY,
214+
shapefile_path, x_shapefile, y_shapefile, config.filepath.DONOR_SUBDIRECTORY, config.mount_points
218215
)
219216

220217
complementary_bd_points = get_complementary_points(

patchwork/path_manipulation.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from pathlib import Path, PurePosixPath, PureWindowsPath
2+
from typing import Dict, List
3+
4+
5+
def get_mounted_path_from_raw_path(raw_path: str, mount_points: List[Dict]):
6+
"""Get mounted path from a raw path and a list of mount points.
7+
In case the raw path does not correspond to any mount point, the input raw_path is returned.
8+
9+
Each mount point is described in a dictionary with keys:
10+
- ORIGINAL_PATH (str): Original path of the mounted directory (root of the raw path to replace)
11+
- MOUNTED_PATH (str): Mounted path of the directory (root path by which to replace the root of the raw path
12+
in order to access to the directory on the current computer)
13+
- ORIGINAL_PLATFORM_IS_WINDOWS (bool): true if the raw path should be interpreted as a windows path
14+
when using this mount point
15+
16+
Args:
17+
raw_path (str): Original path to convert to a mounted path
18+
mount_points (List[Dict]): List of mount points (as described above)
19+
"""
20+
mounted_path = None
21+
for mount_point in mount_points:
22+
mounted_path = get_mounted_path_from_mount_point(raw_path, mount_point)
23+
if mounted_path is not None:
24+
break
25+
if mounted_path is None:
26+
mounted_path = Path(raw_path)
27+
28+
return mounted_path
29+
30+
31+
def get_mounted_path_from_mount_point(raw_path, mount_point):
32+
out_path = None
33+
PureInputPath = PureWindowsPath if mount_point["ORIGINAL_PLATFORM_IS_WINDOWS"] else PurePosixPath
34+
if PureInputPath(raw_path).is_relative_to(PureInputPath(mount_point["ORIGINAL_PATH"])):
35+
relative_path = PureInputPath(raw_path).relative_to(PureInputPath(mount_point["ORIGINAL_PATH"]))
36+
out_path = mount_point["MOUNTED_PATH"] / Path(relative_path)
37+
38+
return out_path

patchwork/shapefile_data_extraction.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import fnmatch
22
import os
3+
from typing import Dict, List
34

45
import geopandas as gpd
6+
from omegaconf import DictConfig
57

8+
from patchwork.path_manipulation import get_mounted_path_from_raw_path
69

7-
def get_donor_info_from_shapefile(input_shapefile: str, x: int, y: int, tile_subdirectory: str) -> gpd.GeoDataFrame:
10+
11+
def get_donor_info_from_shapefile(
12+
input_shapefile: str, x: int, y: int, tile_subdirectory: str, mount_points: List[Dict] | DictConfig
13+
) -> gpd.GeoDataFrame:
814
"""Retrieve paths to all the donor files associated with a given tile (with origin x, y) from a shapefile.
915
1016
The shapefile should contain one geometry per donor file, with attributes:
@@ -20,13 +26,23 @@ def get_donor_info_from_shapefile(input_shapefile: str, x: int, y: int, tile_sub
2026
2127
It is stored in the "full_path" column of the output geodataframe
2228
29+
The mount_point dictionaries should contains these keys:
30+
- ORIGINAL_PATH (str): Original path of the mounted directory (root of the raw path to replace)
31+
- MOUNTED_PATH (str): Mounted path of the directory (root path by which to replace the root of the raw path
32+
in order to access to the directory on the current computer)
33+
- ORIGINAL_PLATFORM_IS_WINDOWS (bool): true if the raw path should be interpreted as a windows path
34+
when using this mount point
35+
2336
Args:
2437
input_shapefile (str): Shapefile describing donor files
2538
x (int): x coordinate of the tile for which to get the donors
2639
(in the same unit as in the shapefile, usually km)
2740
y (int): y coordinate of the tile for which to get the donors
2841
(in the same unit as in the shapefile, usually km)
2942
tile_subdirectory (str): subdirectory of "nuage_mixa" in which the donor files are stored
43+
mount_points (List[Dict]): dictionaries describing the mount points to use to interpret paths from "nuage_mixa"
44+
in case the path is related to a distant folder that can be mounted in different ways *(cf. dictionary
45+
structure above)
3046
3147
Raises:
3248
NotImplementedError: if nom_coord is false (case not handled)
@@ -51,8 +67,9 @@ def get_donor_info_from_shapefile(input_shapefile: str, x: int, y: int, tile_sub
5167

5268
if len(gdf.index):
5369

54-
def find_las_path_from_geometry_attributes(x: int, y: int, path_root: str):
55-
tile_directory = os.path.join(path_root, tile_subdirectory)
70+
def find_las_path_from_geometry_attributes(x: int, y: int, path_root: str, mount_points: List[Dict]):
71+
mounted_path_root = get_mounted_path_from_raw_path(path_root, mount_points)
72+
tile_directory = os.path.join(mounted_path_root, tile_subdirectory)
5673
if not os.path.isdir(tile_directory):
5774
raise FileNotFoundError(f"Directory {tile_directory} not found")
5875
potential_filenames = fnmatch.filter(os.listdir(tile_directory), f"*{x}_{y}*.la[sz]")
@@ -68,10 +85,9 @@ def find_las_path_from_geometry_attributes(x: int, y: int, path_root: str):
6885
return os.path.join(tile_directory, potential_filenames[0])
6986

7087
gdf["full_path"] = gdf.apply(
71-
lambda row: find_las_path_from_geometry_attributes(row["x"], row["y"], row["nuage_mixa"]),
88+
lambda row: find_las_path_from_geometry_attributes(row["x"], row["y"], row["nuage_mixa"], mount_points),
7289
axis="columns",
7390
)
74-
7591
else:
7692
gdf = gpd.GeoDataFrame(columns=["x", "y", "full_path", "geometry"])
7793

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# @package _global_
2+
3+
# path to original working directory
4+
# hydra hijacks working directory by changing it to the current log directory,
5+
# so it's useful to have this path as a special variable
6+
# learn more here: https://hydra.cc/docs/next/tutorials/basic/running_your_app/working_directory
7+
work_dir: ${hydra:runtime.cwd}
8+
9+
# disable ouput directory from being created
10+
hydra:
11+
output_subdir: null
12+
run:
13+
dir: .
14+
15+
# disable main.log from being created
16+
defaults:
17+
- override hydra/hydra_logging: disabled
18+
- override hydra/job_logging: disabled
19+
- _self_
20+
21+
filepath:
22+
SHP_NAME: null # name of the shapefile used to match tiles to patch
23+
SHP_DIRECTORY: null # path to the directory containing the shapefile
24+
25+
OUTPUT_DIR: null # directory of the file with added points, from patchwork.
26+
OUTPUT_NAME: null # name of the file with added points, from patchwork.
27+
28+
INPUT_INDICES_MAP_DIR: null
29+
INPUT_INDICES_MAP_NAME: null
30+
31+
OUTPUT_INDICES_MAP_DIR: null # path to the directory for the indices map reflecting the changes to the recipient, from patchwork
32+
OUTPUT_INDICES_MAP_NAME: null # name of the indices map reflecting the changes to the recipient, from patchwork
33+
34+
RECIPIENT_DIRECTORY: null # directory containing the recipient file for patchwork
35+
RECIPIENT_NAME: null # name of the recipient file for patchwork
36+
37+
# The input shapefile should contain a "nuage_mixa" attrubute for each geometry
38+
# "nuage_mixa" contains the path to the folder containing the files related to a specific donor source.
39+
# Laz/las files from this source are usually contained in a subdirectory of "nuage_mixa"
40+
# path to this subdirectory can be configured using "DONOR_SUBDIRECTORY"
41+
DONOR_SUBDIRECTORY: "data"
42+
43+
mount_points:
44+
- ORIGINAL_PATH: \\store\my-store # WARNING: do NOT use quotes around the path if it contains \\
45+
MOUNTED_PATH: .
46+
ORIGINAL_PLATFORM_IS_WINDOWS: true
47+
- ORIGINAL_PATH: /store/my-store # WARNING: do NOT use quotes around the path if it contains \\
48+
MOUNTED_PATH: .
49+
ORIGINAL_PLATFORM_IS_WINDOWS: false
50+
51+
CRS: 2154
52+
53+
DONOR_CLASS_LIST: [2, 22]
54+
RECIPIENT_CLASS_LIST: [2, 6, 9, 17]
55+
56+
TILE_SIZE: 1000
57+
SHP_X_Y_TO_METER_FACTOR: 1000 # multiplication factor to convert shapefile x, y attributes values to meters
58+
PATCH_SIZE: 1 # size of a patch of the grid. Must be a divisor of TILE_SIZE, so for 1000: 0.25, 0.5, 2, 4, 5, 10, 25...
59+
NEW_COLUMN: null # If not null, contains the name of the new column
60+
NEW_COLUMN_SIZE: 8 # must be 8, 16, 32 or 64
61+
VALUE_ADDED_POINTS: 1 # in case of a new column, value of the new point (the other are set to 0)
62+
VIRTUAL_CLASS_TRANSLATION: {2: 69, 22: 70} # if there is no new column, translate the class of DONOR_CLASS_LIST into those values
63+
# each value of DONOR_CLASS_LIST must be a key in VIRTUAL_CLASS_TRANSLATION. Not used if NEW_COLUMN is not None (or "")
2.88 KB
Binary file not shown.
11.2 KB
Binary file not shown.
132 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)