← Retour à la carte

Traitements effectués

Ce que fait la page arrets-gpx.html, de la lecture du fichier jusqu'à l'affichage : comment le tracé est reconstitué, comment chaque arrêt est rattaché au tracé, et comment sont déterminés le côté (G / D) et les discontinuités.

1. Vue d'ensemble

Le traitement est le même quel que soit le format d'entrée. Seules les deux premières étapes changent.

  1. Lecture : extraction d'un tracé (liste de points) et d'une liste d'arrêts.
  2. Reconstitution (si besoin) : assemblage des tronçons en une ligne continue, détection des discontinuités, tri des arrêts.
  3. Projection des coordonnées en mètres dans un plan local.
  4. Accrochage de chaque arrêt sur le segment du tracé le plus proche, avec respect de l'ordre de parcours.
  5. Côté de l'arrêt par rapport au sens de circulation.
  6. Prolongement du segment arrêt → tracé et pose de l'indicateur G / D.
  7. Affichage sur la carte et dans la liste.

2. Formats d'entrée

Le format est reconnu sur le contenu du fichier, pas sur son extension.

FormatDétectionTracéArrêts
GPXautre cas<trkpt>, sinon <rtept>, dans l'ordre du fichier<wpt>, dans l'ordre du fichier
GeoJSONcommence par {LineString / MultiLineStringPoint / MultiPoint ; nom lu dans name, nom, stop_name, title ou label
XML OSMcontient <osm au débutways de la relation, dans l'ordre des membresmembres platform* ou stop*

Pour un GPX, l'ordre du fichier est supposé être l'ordre de parcours. Pour un GeoJSON qui contient une seule ligne, l'ordre des points est aussi conservé tel quel.

3. Relation OSM (XML)

C'est le format le plus fiable, car il conserve l'ordre et les rôles des membres. Il s'obtient avec https://api.openstreetmap.org/api/0.6/relation/ID/full, soit en déposant le fichier, soit avec le champ « Relation OSM ».

Choix de la relation

S'il y a plusieurs relations dans la réponse (sous-relations), la première avec le tag type=route est retenue, sinon la première relation.

Tracé

Arrêts

Le titre affiché dans le panneau est ref + name (ou from → to si le nom est absent).

Les tags de la relation dont la clé commence par gtfs:shape_id (par exemple gtfs:shape_id:IDFM) sont affichés sous les liens du panneau, avec leur clé complète ; la valeur se sélectionne d'un clic pour être copiée. S'il n'y en a pas, le panneau l'indique.

4. Export Overpass (GeoJSON)

Un export de relation contient un LineString par way, dans un ordre et un sens quelconques. La page les reconstitue.

Assemblage des tronçons

  1. Les extrémités sont comparées avec 7 décimales (environ 1 cm).
  2. Un tronçon qui a une extrémité libre (reliée à aucun autre) sert de début de chaîne ; à défaut, le premier non utilisé.
  3. La chaîne est prolongée en cherchant un tronçon non utilisé qui touche sa fin. Il est retourné si besoin.
  4. Quand plusieurs chaînes séparées subsistent (trous dans la relation), la plus longue sert de base. Les autres sont rattachées une à une, par la tête ou par la queue, selon l'extrémité la plus proche, en les retournant si nécessaire.

Arrêts

Le sens du tracé assemblé est arbitraire, car l'export n'indique pas le sens de la relation. Si G et D sont inversés, cocher « Inverser le sens du tracé ». Le tri des arrêts est alors recalculé.

5. Ronds-points

Un rond-point tracé d'un seul way est un anneau fermé : son premier et son dernier point sont identiques. Le traiter comme une simple ligne créerait deux faux trous, un à l'entrée et un à la sortie.

Quand un way fermé apparaît dans la relation :

  1. Entrée : sommet de l'anneau le plus proche de la fin de la ligne déjà construite.
  2. Sortie : sommet de l'anneau le plus proche de l'une des extrémités du way suivant.
  3. L'anneau est parcouru dans le sens du way (sens de circulation sur OSM) de l'entrée à la sortie, avec bouclage si besoin.

Seule la portion réellement empruntée est ajoutée au tracé. Un rond-point découpé en plusieurs ways n'a pas besoin de ce traitement : les arcs se raccordent par leurs nœuds communs.

Ce traitement s'applique aux relations OSM (XML). L'assemblage des exports Overpass ne le fait pas : un rond-point d'un seul tenant peut y provoquer des trous.

6. Discontinuités

Une discontinuité est repérée à chaque raccord entre deux tronçons dont les extrémités sont à plus de 2 m l'une de l'autre. En dessous, elles sont considérées comme jointes.

Causes fréquentes : un way manquant dans la relation, des ways dans le désordre, un tronçon non connecté dans OSM (nœud non partagé).

Les GPX et les GeoJSON à ligne unique n'ont pas de raccords, donc aucune discontinuité n'est signalée.

7. Projection locale

Les calculs de distance se font en mètres dans un plan, pas en degrés. La page utilise une projection équirectangulaire centrée sur la moyenne des points du tracé :

ky = R · π/180               (mètres par degré de latitude)
kx = ky · cos(latitude_ref)  (mètres par degré de longitude)
x = (lon − lon_ref) · kx
y = (lat − lat_ref) · ky         avec R = 6 371 008,8 m

Sur une ligne de bus, l'erreur est de l'ordre du mètre sur quelques dizaines de kilomètres, ce qui suffit ici.

8. Accrochage au tracé

Pour chaque arrêt P et chaque segment [A, B] du tracé, le point le plus proche est calculé :

t = ((P − A) · (B − A)) / |B − A|²     puis t borné à [0, 1]
C = A + t · (B − A)                     (point d'accroche)
distance = |P − C|

Le borne sur t garde le point sur le segment : si la perpendiculaire tombe hors du segment, le point d'accroche est une extrémité. Le segment retenu est celui qui minimise la distance, sous la contrainte d'ordre ci-dessous.

9. Ordre des arrêts

Chaque arrêt est candidat sur tous les segments. Choisir simplement le plus proche donnerait des erreurs aux terminus, aux boucles et aux endroits où la ligne repasse près d'elle-même.

Par défaut (option « Respecter l'ordre des arrêts »), une programmation dynamique choisit l'ensemble des accrochages qui minimise la somme des distances, avec la contrainte que l'indice de segment ne décroît jamais d'un arrêt au suivant.

coût[j][k] = distance(arrêt j, segment k) + min sur k' ≤ k de coût[j−1][k']

Le meilleur prédécesseur est maintenu par un minimum cumulé, donc la complexité est en O(arrêts × segments). Le chemin optimal est reconstruit à rebours.

Décocher l'option accroche chaque arrêt indépendamment sur son segment le plus proche.

Dans un même segment, l'ordre précis de deux arrêts n'est pas contraint (seuls les indices de segment le sont).

10. Côté : gauche / droite

Le côté se déduit du signe du produit vectoriel entre la direction du segment retenu et le vecteur segment → arrêt :

signé = (dx · (Py − Ay) − dy · (Px − Ax)) / |B − A|     avec (dx, dy) = B − A
ValeurRésultat
signé > 1 mG à gauche du sens de circulation
signé < −1 mD à droite
|signé| ≤ 1 maxe (point gris)

Gauche et droite sont relatifs au sens du tracé. Si le tracé est dans le sens inverse de la ligne, les côtés sont inversés ; l'option « Inverser le sens du tracé » corrige cela.

11. Segment prolongé et indicateur

sens du tracé → G distance prolongement arrêt point d'accroche indicateur

12. Distances aux extrémités

Deux distances renseignent sur la couverture du tracé par les arrêts :

Elles sont mesurées le long du tracé : somme des longueurs des segments jusqu'au point d'accroche de l'arrêt (indice de segment + fraction t), pas en ligne droite. Le « 1er » et le « dernier » arrêt sont ceux de la liste, donc dans l'ordre de parcours (relation OSM) ou trié le long du tracé (export Overpass). Elles tiennent compte de l'option « Inverser le sens du tracé ».

Elles apparaissent dans le texte d'état, et sous forme de deux lignes grisées en tête et en fin de la liste (un clic centre la carte sur l'extrémité du tracé). Elles sont aussi exposées aux scripts : arrets.state.startToFirst et arrets.state.lastToEnd, en mètres.

Une distance importante signale un tracé qui dépasse les arrêts (terminus mal placé, tronçon en trop), ou un arrêt manquant en début ou fin de ligne.

13. Liens JOSM

Ils passent par la télécommande de JOSM (http://127.0.0.1:8111). JOSM doit être lancé, avec la télécommande activée (Préférences → Télécommande, option de chargement des données).

L'appel part en arrière-plan. Si le navigateur le bloque, l'URL s'ouvre dans un onglet à la place.

14. Paramètres d'URL

ParamètreEffet
?relation=21744664 (ou ?rel=, ou #relation=)Charge la relation au démarrage ; l'URL est mise à jour quand on en charge une avec le bouton.
#map=zoom/lat/lonPosition et zoom, mis à jour à chaque fin de déplacement. Au chargement, la vue de l'URL n'est pas écrasée par le recadrage automatique sur le tracé.
?bg=none ou #bg=none&map=13/44.8/-0.5 (alias ?nobasemap)Aucun fond de carte : plan et ortho ne sont ni déclarés ni demandés (aucune tuile chargée), le sélecteur de fond est masqué. ?nobasemap=0 (ou false, no, non) le désactive.
bg=osmfr ou bg=orthoForce le fond au chargement, sans modifier le choix mémorisé dans le navigateur.

Ces paramètres se lisent dans la requête (?…) ou dans le fragment (#…). Le paramètre bg est conservé quand la position de l'URL est mise à jour.

En file://, certains navigateurs refusent de réécrire l'URL : le chargement fonctionne, mais l'adresse ne se met pas à jour.

15. Utilisation par un script

La page peut être pilotée et suivie par un script (Playwright, Selenium, console…). Pour éviter tout chargement de tuiles, ouvrir par exemple arrets-gpx.html?bg=none.

Événements

Ils sont émis sur document (ils remontent jusqu'à window). event.detail contient les champs ci-dessous, plus state : une copie de l'état courant.

ÉvénementMomentDétail
arrets:busyun traitement démarre (passage de libre à occupé)phase, message
arrets:progressavancementphase, value (0 à 1), message
arrets:idleplus aucun traitement en cours—
arrets:readycarte initialisée, prête à charger un tracé—
arrets:errorerreur d'un traitementphase, message

Une série de chargements enchaînés (traités l'un après l'autre) ne produit qu'un seul couple busy / idle. L'erreur est effacée de l'état au démarrage du traitement suivant, et un error est toujours suivi d'un idle.

Phases

PhaseValeurSignification
init, ready—, 1initialisation de la carte, puis carte prête
fetch0,1téléchargement de la relation sur l'API OSM
receive0,05lecture du fichier ou réception d'une shape envoyée par la page GTFS
parse0,2 à 0,3lecture du GPX, du GeoJSON ou du XML OSM
snap0,45tri et accrochage des arrêts sur le tracé
render0,65affichage sur la carte et dans la liste
fit0,8cadrage sur le tracé (absent si la vue vient de l'URL ou au recalcul)
tiles0,9attente de la fin du rendu (au plus 8 s)
done1terminé
josm0,3 à 1appel de la télécommande JOSM depuis un popup ou le panneau

Changer une option (prolongement, ordre, inversion) relance un traitement court : snap, render, tiles, done.

État et API

window.arrets.state est une copie de l'état :

ChampContenu
ready, busycarte prête ; traitement en cours
phase, value, messagedernière progression
titletitre du tracé chargé
points, stopsnombre de points du tracé et d'arrêts
left, right, axisarrêts à gauche, à droite, sur l'axe
gapsnombre de discontinuités
startToFirst, lastToEnddistances (m) début du tracé → 1er arrêt et dernier arrêt → fin du tracé, le long du tracé
basemapnone, osmfr ou ortho
errormessage de la dernière erreur, sinon null

Les attributs html[data-arrets-state="busy|idle"] et html[data-arrets-phase] reflètent le même état, pratique pour waitForSelector.

MéthodeEffet
arrets.whenReady()promesse résolue quand la carte est prête
arrets.whenIdle()promesse résolue (avec l'état) quand plus aucun traitement n'est en cours
arrets.loadText(texte, titre?)charge un GPX, un GeoJSON ou un XML OSM fourni sous forme de texte
arrets.loadRelation(id)charge une relation depuis l'API OSM
arrets.load(msg)comme un message de la page GTFS : { gpx, name, shape_id } (ou directement le texte GPX)

Exemple (Playwright)

await page.goto('file:///…/arrets-gpx.html?bg=none');
await page.waitForSelector('html[data-arrets-state="idle"]');
await page.evaluate(t => arrets.loadText(t), gpxTexte);
const etat = await page.evaluate(() => arrets.whenIdle());
console.log(etat.stops, etat.left, etat.right, etat.error);

16. Limites connues