"""Corrections faites à la main (interface, onglet « Carte ») et appliquées à la génération des pages ; les fichiers d'origine ne sont jamais modifiés. voyage.toml [positions] média déplacé ou placé à la main "IMG_4831.jpeg" = [48.85, 2.35] [corrections] masques = [[48.85, 2.35, 300]] zones (centre, rayon en m) retirées des traces site.toml [confidentialite] zones = [[48.80, 2.10, 500]] zones retirées des traces de tous les voyages (autour de chez soi, par exemple) """ import csv import math import os import gpxpy.gpx def masques(cfg): """[(lat, lon, rayon_m)] : zones du voyage et zones privées du site.""" res = [] for liste in (cfg.get("corrections", {}).get("masques", []), cfg.get("confidentialite", {}).get("zones", [])): for m in liste: try: res.append((float(m[0]), float(m[1]), float(m[2]))) except (TypeError, ValueError, IndexError): pass return res def positions(cfg): """{nom de fichier: (lat, lon)} des médias placés à la main.""" res = {} for nom, pos in cfg.get("positions", {}).items(): try: res[nom] = (float(pos[0]), float(pos[1])) except (TypeError, ValueError, IndexError): pass return res def distance_m(lat1, lon1, lat2, lon2): x = math.radians(lon2 - lon1) * math.cos(math.radians((lat1 + lat2) / 2)) y = math.radians(lat2 - lat1) return 6371000 * math.hypot(x, y) def dans_un_masque(lat, lon, zones): return any(distance_m(lat, lon, m[0], m[1]) <= m[2] for m in zones) def filtrer_piste(piste, zones): """Retire d'une piste GPX les points situés dans les zones ; un segment traversé par une zone est coupé en deux. Renvoie le nombre de points retirés.""" if not zones: return 0 retires = 0 nouveaux = [] for seg in piste.segments: courant = gpxpy.gpx.GPXTrackSegment() for p in seg.points: if dans_un_masque(p.latitude, p.longitude, zones): retires += 1 if courant.points: nouveaux.append(courant) courant = gpxpy.gpx.GPXTrackSegment() else: courant.points.append(p) if courant.points: nouveaux.append(courant) piste.segments = [s for s in nouveaux if len(s.points) > 1] return retires def lire_medias(voyage, cfg): """Photos et vidéos des exports CSV, positions corrigées comprises. Renvoie une liste de dicts {fichier, type, date, lat, lon, deplace} ; lat et lon valent None pour un média sans position.""" corr = positions(cfg) res = [] for nom_csv, type_ in (("export_photos.csv", "photo"), ("export_videos.csv", "video")): chemin = os.path.join(voyage, nom_csv) if not os.path.exists(chemin): continue vus = set() with open(chemin, encoding="utf-8", errors="replace") as f: for ligne in csv.reader(f): if len(ligne) < 4 or ligne[0] in vus: continue vus.add(ligne[0]) try: lat, lon = float(ligne[2]), float(ligne[3]) if lat == 0 and lon == 0: lat = lon = None except ValueError: lat = lon = None m = {"fichier": ligne[0], "type": type_, "date": ligne[1], "lat": lat, "lon": lon, "deplace": ligne[0] in corr} if m["deplace"]: m["lat"], m["lon"] = corr[ligne[0]] res.append(m) return res