Skip to content

Commit ac0ce1d

Browse files
authored
Merge pull request #8 from IGNF/fix-integration
Misc Clean up
2 parents e69c73d + 0185551 commit ac0ce1d

19 files changed

Lines changed: 397 additions & 309 deletions

.pre-commit-config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# See https://dev.to/m1yag1/how-to-setup-your-project-with-pre-commit-black-and-flake8-183k
2+
# for pre-commits setup
3+
# See https://pre-commit.com for more information
4+
# See https://pre-commit.com/hooks.html for more hooks
5+
repos:
6+
- repo: https://github.com/pycqa/isort
7+
rev: 6.0.1
8+
hooks:
9+
- id: isort
10+
args: ["--profile=black"]
11+
name: isort (python)
12+
- repo: https://github.com/ambv/black
13+
rev: 25.1.0
14+
hooks:
15+
- id: black
16+
language_version: python3.10
17+
- repo: https://github.com/pre-commit/pre-commit-hooks
18+
rev: v2.0.0
19+
hooks:
20+
- id: flake8

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# CHANGELOG
2+
- lint
3+
- ajout de pre-commit hooks pour appliquer le lint au moment des commits
4+
- patchwork crée lui-même les sous-dossiers dont chaque étape a besoin
5+
- correctif pour la recherche de correspondance des las dans le csv de matching
6+
27
## 1.1.0
38
- modification de chemin pour pouvoir passer dans la gpao
49
- coupure des chemins de fichiers en chemins de répertoires/nom de fichiers pour pouvoir les utiliser sur docker + store

README.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
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.
33

44
## Fonctionnement
5-
Les données en entrée sont:
6-
- un fichier lidar que l'ont souhaite enrichir
7-
- un fichier lidar contenant des points supplémentaires
8-
9-
En sortie il y a :
10-
- Un fichier, copie du premier en entrée, enrichi des points voulu
11-
5+
Les données en entrée sont:
6+
- un fichier lidar que l'ont souhaite enrichir
7+
- un fichier lidar contenant des points supplémentaires
8+
9+
En sortie il y a :
10+
- Un fichier, copie du premier en entrée, enrichi des points voulus
11+
1212
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.
1313

1414
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.
@@ -30,31 +30,31 @@ Le script d'ajout de points peut être lancé via :
3030
```
3131
python main.py filepath.DONOR_FILE=[chemin fichier donneur] filepath.RECIPIENT_FILE=[chemin fichier receveur] filepath.OUTPUT_FILE=[chemin fichier de sortie] [autres options]
3232
```
33-
Les différentes options, modifiables soit dans le fichierconfigs/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, 9]. La liste des classes des points du fichier donneur qui peuvent être ajoutés.
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.
4545
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
4646
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...)
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...)
4848

4949
Le script de sélection/découpe de fichier lidar peut être lancé via :
5050
```
51-
python lidar_filepath.py filepath.DONOR_DIRECTORY=[répertoire_fichiers_donneurs] filepath.RECIPIENT_DIRECTORY=[répertoire_fichiers_receveurs] filepath.SHP_NAME=[nom_shapefile] filepath.SHP_DIRECTORY=[répertoire_shapefile] filepath.CSV_NAME=[nom_fichier_csv] filepath.CSV_DIRECTORY=[répertoire_fichier_csv] filepath.OUTPUT_DIRECTORY=[chemin_de_sortie]
51+
python lidar_selecter.py filepath.DONOR_DIRECTORY=[répertoire_fichiers_donneurs] filepath.RECIPIENT_DIRECTORY=[répertoire_fichiers_receveurs] filepath.SHP_NAME=[nom_shapefile] filepath.SHP_DIRECTORY=[répertoire_shapefile] filepath.CSV_NAME=[nom_fichier_csv] filepath.CSV_DIRECTORY=[répertoire_fichier_csv] filepath.OUTPUT_DIRECTORY=[chemin_de_sortie]
5252
```
5353

54-
filepath.DONOR_DIRECTORY: Le répertoire contenant les fichiers lidar donneurs
55-
filepath.RECIPIENT_DIRECTORY: Le répertoire contenant les fichiers lidar receveurs
56-
filepath.SHP_NAME: Le nom du shapefile contenant l'emprise du chantier qui délimite les fichiers lidar qui nous intéressent
57-
filepath.SHP_DIRECTORY: Le répertoire du fichier shapefile
58-
filepath.CSV_NAME: Le nom du fichier csv qui lie les différents fichiers donneurs et receveurs
59-
filepath.CSV_DIRECTORY: Le répertoire du fichier csv
60-
filepath.OUTPUT_DIRECTORY: le répertoire recevant les fichiers lidar découpés
54+
filepath.DONOR_DIRECTORY: Le répertoire contenant les fichiers lidar donneurs
55+
filepath.RECIPIENT_DIRECTORY: Le répertoire contenant les fichiers lidar receveurs
56+
filepath.SHP_NAME: Le nom du shapefile contenant l'emprise du chantier qui délimite les fichiers lidar qui nous intéressent
57+
filepath.SHP_DIRECTORY: Le répertoire du fichier shapefile
58+
filepath.CSV_NAME: Le nom du fichier csv qui lie les différents fichiers donneurs et receveurs
59+
filepath.CSV_DIRECTORY: Le répertoire du fichier csv
60+
filepath.OUTPUT_DIRECTORY: le répertoire recevant les fichiers lidar découpés

constants.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

environment.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ dependencies:
99
- numpy
1010
- geopandas==0.*
1111
- shapely>=2.0.3
12+
- rasterio
1213
# ------------- logging ------------- #
1314
- loguru
1415
# --------- hydra configs --------- #
1516
- hydra-core
1617
- hydra-colorlog
1718
# ----------- linting --------------- #
18-
- flake8
19-
- rasterio
19+
- pre-commit
20+
- black
21+
- flake8

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import hydra
44
from omegaconf import DictConfig
55

6-
from patchwork import patchwork
6+
from patchwork.patchwork import patchwork
77

88

99
@hydra.main(config_path="configs/", config_name="configs_patchwork.yaml", version_base="1.2")

patchwork/constants.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# INTERNAL CONSTANTS, NOT TO BE CHANGED
2+
CLASSIFICATION_STR = "classification"
3+
PATCH_X_STR = "patch_x"
4+
PATCH_Y_STR = "patch_y"
5+
DONOR_SUBDIRECTORY_NAME = "donor"
6+
RECIPIENT_SUBDIRECTORY_NAME = "recipient"
7+
8+
COORDINATES_KEY = "coordinates"
9+
DONOR_FILE_KEY = "donor_file"
10+
RECIPIENT_FILE_KEY = "recipient_file"

indices_map.py renamed to patchwork/indices_map.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import os
1+
import os
22

33
import numpy as np
4-
from omegaconf import DictConfig
5-
import rasterio as rs
6-
from rasterio.transform import from_origin
74
import pandas as pd
5+
import rasterio as rs
6+
from omegaconf import DictConfig
87
from pandas import DataFrame
8+
from rasterio.transform import from_origin
99

10-
from tools import get_tile_origin_from_pointcloud
11-
from constants import PATCH_X_STR, PATCH_Y_STR
10+
from patchwork.constants import PATCH_X_STR, PATCH_Y_STR
11+
from patchwork.tools import get_tile_origin_from_pointcloud
1212

1313

1414
def create_indices_grid(config: DictConfig, df_points: DataFrame) -> np.ndarray:
15-
""" create a binary grid matching the tile the points of df_points are from, where each patch is equal to:
15+
"""create a binary grid matching the tile the points of df_points are from, where each patch is equal to:
1616
1 if the patch has at least one point of df_points
1717
0 if the patch has no point from df_points
1818
"""
@@ -40,14 +40,23 @@ def create_indices_map(config: DictConfig, df_points: DataFrame):
4040
corner_x, corner_y = get_tile_origin_from_pointcloud(config, df_points)
4141

4242
grid = create_indices_grid(config, df_points)
43-
output_indices_map_path = os.path.join(config.filepath.OUTPUT_INDICES_MAP_DIR, config.filepath.OUTPUT_INDICES_MAP_NAME)
43+
os.makedirs(config.filepath.OUTPUT_INDICES_MAP_DIR, exist_ok=True)
44+
output_indices_map_path = os.path.join(
45+
config.filepath.OUTPUT_INDICES_MAP_DIR, config.filepath.OUTPUT_INDICES_MAP_NAME
46+
)
4447

4548
transform = from_origin(corner_x, corner_y, config.PATCH_SIZE, config.PATCH_SIZE)
46-
indices_map = rs.open(output_indices_map_path, 'w', driver='GTiff',
47-
height=grid.shape[0], width=grid.shape[1],
48-
count=1, dtype=str(grid.dtype),
49-
crs=config.CRS,
50-
transform=transform)
49+
indices_map = rs.open(
50+
output_indices_map_path,
51+
"w",
52+
driver="GTiff",
53+
height=grid.shape[0],
54+
width=grid.shape[1],
55+
count=1,
56+
dtype=str(grid.dtype),
57+
crs=config.CRS,
58+
transform=transform,
59+
)
5160
indices_map.write(grid, 1)
5261
indices_map.close()
5362

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,62 @@
22
import pathlib
33
import timeit
44

5-
import hydra
6-
from omegaconf import DictConfig
75
import geopandas as gpd
6+
import hydra
87
import laspy
9-
from laspy import ScaleAwarePointRecord
10-
from shapely import box
118
import numpy as np
9+
from laspy import ScaleAwarePointRecord
10+
from loguru import logger
11+
from omegaconf import DictConfig
1212
from pandas import DataFrame
13+
from shapely import box
1314
from shapely.geometry import MultiPolygon
1415
from shapely.vectorized import contains
15-
from loguru import logger
1616

17-
import constants as c
18-
from tools import identify_bounds, get_tile_origin_from_pointcloud, crop_tile
17+
import patchwork.constants as c
18+
from patchwork.tools import crop_tile, get_tile_origin_from_pointcloud, identify_bounds
1919

2020

21-
@hydra.main(config_path="configs/", config_name="configs_patchwork.yaml", version_base="1.2")
21+
@hydra.main(config_path="../configs/", config_name="configs_patchwork.yaml", version_base="1.2")
2222
def patchwork_dispatcher(config: DictConfig):
23-
data = {c.COORDINATES_KEY: [],
24-
c.DONOR_FILE_KEY: [],
25-
c.RECIPIENT_FILE_KEY: []
26-
}
23+
data = {c.COORDINATES_KEY: [], c.DONOR_FILE_KEY: [], c.RECIPIENT_FILE_KEY: []}
2724
df_result = DataFrame(data=data)
2825
# preparing donor files:
29-
select_lidar(config,
30-
config.filepath.DONOR_DIRECTORY,
31-
config.filepath.OUTPUT_DIRECTORY,
32-
c.DONOR_SUBDIRECTORY_NAME,
33-
df_result,
34-
c.DONOR_FILE_KEY,
35-
True
36-
)
26+
select_lidar(
27+
config,
28+
config.filepath.DONOR_DIRECTORY,
29+
config.filepath.OUTPUT_DIRECTORY,
30+
c.DONOR_SUBDIRECTORY_NAME,
31+
df_result,
32+
c.DONOR_FILE_KEY,
33+
True,
34+
)
3735
# preparing recipient files:
38-
select_lidar(config,
39-
config.filepath.RECIPIENT_DIRECTORY,
40-
config.filepath.OUTPUT_DIRECTORY,
41-
c.RECIPIENT_SUBDIRECTORY_NAME,
42-
df_result,
43-
c.RECIPIENT_FILE_KEY,
44-
False,
45-
)
46-
df_result.to_csv(os.path.join(config.filepath.CSV_DIRECTORY, config.filepath.CSV_NAME), index=False)
36+
select_lidar(
37+
config,
38+
config.filepath.RECIPIENT_DIRECTORY,
39+
config.filepath.OUTPUT_DIRECTORY,
40+
c.RECIPIENT_SUBDIRECTORY_NAME,
41+
df_result,
42+
c.RECIPIENT_FILE_KEY,
43+
False,
44+
)
45+
46+
pathlib.Path(config.filepath.CSV_DIRECTORY).mkdir(exist_ok=True)
47+
df_result.to_csv(
48+
os.path.join(config.filepath.CSV_DIRECTORY, config.filepath.CSV_NAME), index=False, encoding="utf-8"
49+
)
4750

4851

4952
def cut_lidar(las_points: ScaleAwarePointRecord, shapefile_geometry: MultiPolygon) -> ScaleAwarePointRecord:
50-
shapefile_contains_mask = contains(shapefile_geometry, np.array(las_points['x']), np.array(las_points['y']))
53+
shapefile_contains_mask = contains(shapefile_geometry, np.array(las_points["x"]), np.array(las_points["y"]))
5154
return las_points[shapefile_contains_mask]
5255

5356

5457
def update_df_result(df_result: DataFrame, df_key: str, corner_string: str, file_path: str):
5558
# corner_string not yet in df_result
56-
if not corner_string in list(df_result[c.COORDINATES_KEY]):
57-
new_row = {c.COORDINATES_KEY:corner_string, c.DONOR_FILE_KEY: "", c.RECIPIENT_FILE_KEY:""}
59+
if corner_string not in list(df_result[c.COORDINATES_KEY]):
60+
new_row = {c.COORDINATES_KEY: corner_string, c.DONOR_FILE_KEY: "", c.RECIPIENT_FILE_KEY: ""}
5861
new_row[df_key] = file_path
5962
df_result.loc[len(df_result)] = new_row
6063
return df_result
@@ -64,13 +67,15 @@ def update_df_result(df_result: DataFrame, df_key: str, corner_string: str, file
6467
return df_result
6568

6669

67-
def select_lidar(config: DictConfig,
68-
input_directory:str,
69-
output_directory:str,
70-
subdirectory_name: str,
71-
df_result:DataFrame,
72-
df_key: str,
73-
to_be_cut: bool):
70+
def select_lidar(
71+
config: DictConfig,
72+
input_directory: str,
73+
output_directory: str,
74+
subdirectory_name: str,
75+
df_result: DataFrame,
76+
df_key: str,
77+
to_be_cut: bool,
78+
):
7479
"""
7580
Walk the input directory searching for las files, and pick the ones that intersect with the shapefile.
7681
When a las file is half inside the shapfile, it is cut if "to_be_cut" is true, otherwise it kept whole
@@ -83,6 +88,13 @@ def select_lidar(config: DictConfig,
8388

8489
time_old = timeit.default_timer()
8590
time_start = time_old
91+
92+
directory_path = os.path.join(output_directory, subdirectory_name)
93+
# Create output dir only if asked to cut
94+
# Otherwise the input file is intended to be used directly (no copy to the output directory)
95+
if to_be_cut:
96+
pathlib.Path(directory_path).mkdir(parents=True, exist_ok=True)
97+
8698
for root, _, file_names in os.walk(input_directory):
8799

88100
for file_name in file_names:
@@ -95,7 +107,6 @@ def select_lidar(config: DictConfig,
95107
raw_las_points = las_file.read().points
96108
min_x, max_x, min_y, max_y = identify_bounds(config.TILE_SIZE, raw_las_points)
97109
intersect_area = shapefile_geometry.intersection(box(min_x, min_y, max_x, max_y)).area
98-
99110
# if intersect area == 0, this tile is fully outside the shapefile
100111
if intersect_area == 0:
101112

@@ -105,9 +116,6 @@ def select_lidar(config: DictConfig,
105116
time_old = time_new
106117
continue
107118

108-
directory_path = os.path.join(output_directory, subdirectory_name)
109-
pathlib.Path(directory_path).mkdir(parents=True, exist_ok=True)
110-
111119
las_points = crop_tile(config, raw_las_points)
112120
x_corner, y_corner = get_tile_origin_from_pointcloud(config, las_points)
113121

0 commit comments

Comments
 (0)