diff --git a/lidar_pipeline/cli.py b/lidar_pipeline/cli.py index cbe0e72..c58457f 100644 --- a/lidar_pipeline/cli.py +++ b/lidar_pipeline/cli.py @@ -130,8 +130,8 @@ def main(): default=None, metavar="LISTE", help="Sélectionner le(s) GPU(s) à utiliser : un index (ex: -g 0), " - "une liste (ex: -g 0,2), ou 'all' pour tous (ex: -g). " - "Sans -g : GPU auto (le premier si disponible)." + "une liste (ex: -g 0,2), ou 'all' pour tous. " + "Sans -g : tous les GPU disponibles, un par worker (round-robin)." ) parser.add_argument( "-f", "--force", diff --git a/lidar_pipeline/index.py b/lidar_pipeline/index.py index a142d43..7f2f3c6 100644 --- a/lidar_pipeline/index.py +++ b/lidar_pipeline/index.py @@ -366,7 +366,7 @@ def scan_tiles(vis_dir): Liste de dictionnaires: {basename, col, row, resolution, dir_path, viz: {viz_key: {filename, ext}}, dir_name} - Triée par (resolution, row, col). + Triée par (résolution, row décroissant, col). """ vis_dir = Path(vis_dir) if not vis_dir.is_dir(): @@ -488,17 +488,13 @@ def compute_zones(tiles, proximity_threshold=15): zone_members[root] = [] zone_members[root].append(tiles[i]) - zones = [] - for i, zone_tiles in enumerate(zone_members.values(), 1): - zone_bbox = compute_bbox(zone_tiles) - zones.append({ - 'label': f'Zone {i} ({len(zone_tiles)} tuiles)', - 'tiles': zone_tiles, - 'bbox': zone_bbox, - }) - - # Trier par taille décroissante + zones = [{'tiles': zone_tiles, 'bbox': compute_bbox(zone_tiles)} + for zone_tiles in zone_members.values()] + # Trier AVANT de numéroter : les libellés « Zone N » suivent l'ordre + # d'affichage (taille décroissante) zones.sort(key=lambda z: len(z['tiles']), reverse=True) + for i, z in enumerate(zones, 1): + z['label'] = f'Zone {i} ({len(z["tiles"])} tuiles)' return zones diff --git a/lidar_pipeline/rendering.py b/lidar_pipeline/rendering.py index dd7423b..ae3d410 100644 --- a/lidar_pipeline/rendering.py +++ b/lidar_pipeline/rendering.py @@ -200,7 +200,8 @@ def _apply_colormap(data, tif_file, resolution=None): info = RGB_LEGENDS[key] return data, None, info['title'], info['legend'], info['description'], True, None, None - # Find matching colormap — sort by key length descending so 'mslrm' matches before 'lrm' + # Find matching colormap — tri par longueur décroissante : en cas de + # chevauchement de mots-clés dans un nom de fichier, le plus long prime for key in sorted(COLORMAPS.keys(), key=len, reverse=True): info = COLORMAPS[key] if key in name: @@ -369,8 +370,8 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None, resolution: Grid resolution in m/px. keep_tif: If True, keep the source TIFF after conversion. source_info: Dict with method/date/basename for metadata. - quality: Image quality (1-100). Use 100 for lossless. Default 95. - output_format: Output format ('webp' or 'avif'). Default 'webp'. + quality: Image quality (1-100). Use 100 for lossless. Default 98. + output_format: Output format ('webp' or 'avif'). Default 'avif'. Returns: Path to output image file, or None on failure. @@ -915,7 +916,8 @@ def generate_pdf_report(basename, vis_dir, pdf_dir, resolution): # Sort analysis files by archaeological priority order = ['mslrm', 'svf', 'negative_openness', 'positive_openness', 'sailore', 'hillshade_multi', - 'flow_acc', 'solar', 'slope', 'roughness', 'wavelet'] + 'flow_acc', 'solar', 'slope', 'roughness', 'wavelet', + 'aspect', 'anomaly'] def sort_key(f): name = f.stem.lower() diff --git a/lidar_pipeline/webapp.py b/lidar_pipeline/webapp.py index e4cd428..bc1eed6 100644 --- a/lidar_pipeline/webapp.py +++ b/lidar_pipeline/webapp.py @@ -1129,10 +1129,20 @@ def rebuild_status(): def _tail_log(n_lines): + """Dernières lignes du journal — lecture depuis la fin uniquement. + + Le journal peut atteindre plusieurs Mo et /api/status est sondé toutes + les ~2 s par l'interface : relire tout le fichier à chaque sondage + coûte cher, surtout sur Raspberry Pi. + """ try: - lines = LOG_FILE.read_text(encoding="utf-8", errors="replace").splitlines() - return lines[-n_lines:] - except Exception: + with open(LOG_FILE, "rb") as fh: + fh.seek(0, os.SEEK_END) + size = fh.tell() + fh.seek(max(0, size - 64 * 1024)) + chunk = fh.read().decode("utf-8", errors="replace") + return chunk.splitlines()[-n_lines:] + except OSError: return []