MTES-MCT / dialog

Intégration de la réglementation de circulation dans les solutions numériques
https://dialog.beta.gouv.fr
GNU Affero General Public License v3.0
9 stars 1 forks source link

Section entre deux numéros d'une voie nommée #623

Closed florimondmanca closed 7 months ago

florimondmanca commented 9 months ago

Contexte

Suite à #536, DiaLog permet de créer des restrictions de circulation localisées sur la totalité d'une voie nommée (par exemple : "La rue des écoliers à L'Abergement-Clémenciat"). Pour rappel, une "voie nommée" est un type de voie dans la BD TOPO, présente en milieu urbain (rues, avenues, boulevard...) et portant une adresse qu'on peut, en principe, retrouver dans le référentiel de la base adresse nationale (BAN).

En milieu urbain, un nombre significatif de restrictions sont cependant localisées sur une section (ou "tronçon") de voie nommée, c'est à dire pas sur la totalité mais sur une partie de celle-ci (par exemple "Du 9 au 15 de l'avenue de la République à Paris").

L'objectif de ce ticket est de gérer un seul type de section : section entre deux numéros d'une voie nommée.

Pour les autres types de sections, voir le paragraphe "Sections" de l'epic #518, qui centralise tous les travaux liés aux localisations sur du linéaire.

Implémentation

Explo à terminer avant implé.

Backend

Une section entre deux numéros d'une voie nommée est une ligne : une Linestring en GeoJSON. Pour générer cette géométrie, on doit concevoir un système qui permet de :

Idée d'implé à approfondir :

dans le code qui calcule la géométrie au moment d'enregistrer une localisation, si des numéros de maison sont définis, on va prendre le linéaire de voie, et géocoder les adresses de maison, puis faire un calcul géométrique avec PostGIS (à concevoir) pour "tronquer" le linéaire.

Questions auxquelles l'explo doit répondre pour concevoir un système robuste :

UI

Voir aussi :

Voir maquettes

UX

A définir (voir #571 pour exemple)

Critères d'acceptation

Hors périmètre (tickets à créer, voir #518) : section entre 2 intersections #419, etc.

johanricher commented 9 months ago

J'ai mis à jour le ticket pour restreindre son périmètre à un seul problème et une seule implé pour l'instant (la plus prioritaire pour les JOP) : une section entre deux numéros d'une voie nommée.

Pour commencer l'exploration, reprendre discussion commencée ici : https://github.com/MTES-MCT/dialog/issues/518#issuecomment-1852052168

J'ai ajouté des questions dans le corps du ticket, je pense que c'est important d'y réfléchir et d'en ajouter d'autres pour périmétrer encore davantage le travail d'implé.

L'explo devrait être priorisée de telle façon à ce qu'on la mène en parallèle des autres implés en cours (notamment #571) et que le ticket soit "prêt à développer" à la suite de celles-ci.

Pourrait venir ensuite (feuille de route sujette à évolution) :

florimondmanca commented 8 months ago

@johanricher Je réponds ici aux

Questions auxquelles l'explo doit répondre pour concevoir un système robuste :

  • Comment garantir qu'un point de début/fin (géocodé grâce à la BAN/API Adresse) appartient bien à la voie nommée (géocodé grâce à la BD TOPO) ?

Je ne suis pas sûr d'avoir compris la question, en particulier ce que veut dire "appartenir à"

Actuellement si je demande "9 Avenue de la République, Paris" à l'API Adresse j'obtiens un point qui ne tombe pas "sur" le linéaire de voie

Pour le voir copier-coller le GeoJSON suivant (obtenu en combinant le Point de l'API Adresse et la MultiLineString de l'IGN) dans https://geojson.io

{"type":"FeatureCollection","features":[{"type":"Feature","id":"voie_nommee.1172044","geometry":{"type":"MultiLineString","coordinates":[[[2.38720084,48.86287689],[2.3872814,48.86286471]],[[2.38559574,48.86317084],[2.38701646,48.86290742],[2.38720084,48.86287689]],[[2.38494136,48.86329429],[2.38559574,48.86317084]],[[2.38229374,48.86379418],[2.38222679,48.86380732],[2.3814767,48.86395364],[2.38129088,48.86399045],[2.38128542,48.86399132],[2.38119936,48.86400706]],[[2.38024167,48.86418647],[2.38100535,48.86404473],[2.38119936,48.86400706]],[[2.38494136,48.86329429],[2.38338261,48.86358575]],[[2.38229374,48.86379418],[2.38338261,48.86358575]],[[2.3745253,48.86527372],[2.37465633,48.86526001],[2.37477514,48.86524175]],[[2.37258659,48.8656386],[2.37275738,48.86560532],[2.37411958,48.86534534],[2.3745253,48.86527372]],[[2.37477514,48.86524175],[2.37520694,48.86515497]],[[2.37803789,48.86461032],[2.378303,48.86455504],[2.38024167,48.86418647]],[[2.37520694,48.86515497],[2.37803789,48.86461032]],[[2.37151819,48.86583804],[2.3711889,48.86590106],[2.37085277,48.86596675]],[[2.37085277,48.86596675],[2.3706615,48.86600171]],[[2.3706615,48.86600171],[2.37060274,48.866014],[2.37051666,48.86603063],[2.37017508,48.86609539]],[[2.36896174,48.86633002],[2.36914619,48.86629502],[2.36954381,48.86621797]],[[2.36885106,48.86635102],[2.36896174,48.86633002]],[[2.36679615,48.86673677],[2.36641493,48.8668094],[2.3660228,48.86688287],[2.36567433,48.86695297],[2.36550363,48.86697725]],[[2.36731266,48.86663608],[2.36800811,48.86650485]],[[2.36679615,48.86673677],[2.36731266,48.86663608]],[[2.36885106,48.86635102],[2.36813655,48.86648035]],[[2.36813655,48.86648035],[2.36800811,48.86650485]],[[2.37017508,48.86609539],[2.36964899,48.86619964]],[[2.36954381,48.86621797],[2.36964899,48.86619964]],[[2.3723256,48.86569029],[2.37258659,48.8656386]],[[2.3723256,48.86569029],[2.37208794,48.86572951],[2.37151819,48.86583804]]]},"geometry_name":"geometrie","properties":{},"bbox":[2.36550363,48.86286471,2.3872814,48.86697725]},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.367957,48.866665]},"properties":{"label":"9AvenuedelaRépublique75011Paris","score":0.980391818181818,"housenumber":"9","id":"75111_8158_00009","name":"9AvenuedelaRépublique","postcode":"75011","citycode":"75111","x":653634.09,"y":6863145.04,"city":"Paris","district":"Paris11eArrondissement","context":"75,Paris,Île-de-Frœance","type":"housenumber","importance":0.78431,"street":"AvenuedelaRépublique"}}],"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}}}

L'hypothèse qu'on doit faire (je pense) est qu'un résultat renvoyé par l'API Adresse correspond effectivement bien à une adresse existante

Par exemple si je demande le 9 Avenue de la République, Paris, et que l'API Adresse renvoie un résultat, on doit supposer que ce résultat est bien pour cette adresse-là

C'est basique mais si on fait cette hypothèse alors je ne pense pas qu'on ait besoin d'autre "garantie"

Par contre il faut "recaler" le point renvoyé par l'API Adresse SUR le linéaire de voie, ce qui est l'objet de ce ticket

Comme signalé dans #533 Eudonet ne fournit pas de sections déjà calculées

Pour BAC-IDF en revanche on ne fait aucun géocodage de notre côté car Bac-IDF fournit directement les géométries, donc effectivement pour BAc-IDF ce ticket n'a pas d'intérêt particulier

  • Pour le cas d'usage "création d'arrêté" (UI DiaLog) : Est-ce que le calcul d'une section à la volée, au moment de la définition de la localisation, est possible sans engendrer des problèmes d'UX ? (délai long, etc.)calcul en tâche de fond ?

Selon moi il n'y aura pas d'impact UX si mon hypothèse se confirme qu'il s'agit d'un calcul purement géométrique à faire faire par PostGIS, donc que ça sera très rapide car PostGIS est spécialisé là-dedans.

Voilà un dessin du calcul qui doit être fait à mon avis

section

Après une rapide recherche on dirait qu'on peut utiliser ST_ClosestPoint

Returns the 2-dimensional point on geom1 that is closest to geom2. This is the first point of the shortest line between the geometries (as computed by ST_ShortestLine).

florimondmanca commented 8 months ago

@MathieuFV Comme je disais ce matin, les voies nommées à plusieurs "côtés" comme des avenues avec voies séparées par un terre-plein central posent quelques questions supplémentaires

Si je demande "Avenue des Dentellières, Valenciennes" à l'IGN, il me sort cette géométrie : deux lignes continues, une le côté pair, l'autre pour le côté impair (au milieu c'est une double voie de tram)

Screenshot from 2024-03-06 10-57-20

Prenons l'exemple d'un section "du 2 au 4", qui correspond à la portion entre le Zeeman et le Tribunal de Grande Instance sur la photo

Je me dis qu'il faudrait :

Actuellement avec #669 j'ai une erreur technique de Postgis, car au moment de "recoller" les points je lui file les deux lignes alors qu'il exige de n'en recevoir qu'une seule, il faut donc choisir laquelle

Réflexion en cours, peut-être qu'il faut procéder par étapes, on ne va pas pouvoir traiter tous les cas "particuliers" tout de suite...

florimondmanca commented 8 months ago

Autre cas "rigolo" : les rues qui s'étendent à travers plusieurs intersections et/ou rond-points

La Rue du Faubourg de Paris à Valenciennes est ainsi en "3 morceaux" qui se rejoignent en un rond-point

Screenshot from 2024-03-06 11-10-47

Est-ce que ça peut exister un arrêté qui dit : "Rue du Faubourg de Paris, du 12 (qui est sur le 'morceau' à l'ouest) au 46 (qui est sur le 'morceau' au nord)" ? @MathieuFV

MathieuFV commented 8 months ago
Je me dis qu'il faudrait :

Trouver à quelle ligne appartient le 2
À quelle ligne appartient le 4

Est-ce que ce ne serait pas là que le produit "BAN plus" de l'IGN peut servir ? J'avais retenu dans la doc qu'il y avait :

- Le lien Adresse-Support permet de rattacher une adresse à son « support » BDTOPO® (tronçon de route, zone d’activité ou d’intérêt, zone d’habitation, lieu-dit non habité, point du réseau, équipement de transport, etc…). Une adresse peut avoir plusieurs « supports ».
- Les adresses BAN - les ponctuels adresses issus de la BAN (extraction complète à la date du calcul des 4 liens).

Après @johanricher disait qu'on ne pouvait pas appeler ce produit par une API je crois (à confirmer)

MathieuFV commented 8 months ago

Autre cas "rigolo" : les rues qui s'étendent à travers plusieurs intersections et/ou rond-points

Là tu me pose une colle ! Je ne sais pas, je suppose que ça peut arriver oui... Après dans ce cas on serait obligés de demander à l'utilisateur de diviser en deux sa localisation pour ne fournir qu'une ligne au service PostGis à chaque fois (du numéro 12 au numéro 20 (au rond point, admettons) puis du numéro 22 au numéro 46").

Et dans ce cas je pencherais pour qu'un message d'erreur ("La localisation saisie ne peut contenir d'intersection" ou quelque chose du genre) pour que l'utilisateur soit au courant du blocage... A moins encore une fois que BAN plus ne permette des choses !

johanricher commented 8 months ago

J'ai exploré BD TOPO et BAN PLUS (voir notes qui couvrent le scope sur l'ensemble des cas de linéaire : https://github.com/MTES-MCT/dialog/issues/518#issuecomment-1982070020) et j'ai l'impression que les réponses aux questions ci-dessus sont dans l'usage des tronçons de route à la place des voies nommées et routes de la BD TOPO.

Dans la BD TOPO, les tronçons de route, comme les routes, ont souvent 2 "objets", 2 voies qui correspondent par exemple à "chaussées séparées" comme dans les exemples remontés par Florimond.

Contrairement aux routes, les tronçons de route peuvent porter des attributs "Identifiant voie BAN gauche" ou "Identifiant voie BAN droite". Il est donc possible, en théorie, de relier via un identifiant (donc avec certitude) une adresse BAN et un tronçon de route BD TOPO. Alors que jusqu'à présent on relie une adresse BAN et une voie nommée ou une route BD TOPO via leseul libellé, donc qui peut différer dans les deux bases (assez évident que ce n'est pas un système très robuste et il me semble qu'on commence déjà à se rendre compte des problèmes que ça engendre !). C'était le sens de ma première question.

Si je comprends bien la doc de BAN PLUS, le lien avec la BD TOPO se fait avec les "tronçons de route", qui sont un objet différent des voies (cf. page 318 de la doc BD TOPO).

Dans BAN PLUS, un "lien sémantique" est créé pour relier "l'adresse BAN avec le point du tronçon le plus proche de l’adresse".

image Illustration avec QGIS sur l'exemple utilisé par Florimond ci-dessus

Mon interprétation est donc que BAN PLUS ne permettrait pas de géocoder une seule ligne à partir d'une adresse (au lieu de la géométrie avec les 2 lignes), seulement de trouver sur la géométrie le point le plus proche de l'adresse.

Pour les cas "simples" de tronçons avec 2 voies, 2 géométries parallèles, on pourrait calculer une géométrie moyenne au milieu des 2 lignes ? et utiliser ça comme résultat du géocodage de la section entre 2 points d'une voie ?

Pour ton deuxième exemple, les voies en "plusieurs morceaux", l'usage des tronçons nous aidera aussi peut-être ?

Dans tous les cas je suis d'accord avec cette conclusion :slightly_smiling_face: :

peut-être qu'il faut procéder par étapes, on ne va pas pouvoir traiter tous les cas "particuliers" tout de suite...

florimondmanca commented 8 months ago

Pour les cas "simples" de tronçons avec 2 voies, 2 géométries parallèles, on pourrait calculer une géométrie moyenne au milieu des 2 lignes ? et utiliser ça comme résultat du géocodage de la section entre 2 points d'une voie ?

Au-delà de la faisabilité technique (le calcul est potentiellement facile si les points des voies sont bien "zippés", c'est-à-dire qu'on peut faire une moyenne "point à point" des line strings, sinon c'est un peu plus compliqué), je me demande juste si ça peut créer des problèmes plus tard pour les GPS ? Lesquels semblent être assez tâtillons sur le "calage" des points sur les voies et/ou tronçons que eux connaissent.

Pour ton deuxième exemple, les voies en "plusieurs morceaux", l'usage des tronçons nous aidera aussi peut-être ?

Pas forcément, en tout cas je ne pense pas qu'il faudra requêter particulièrement la classe tronçon

Car tout ce qui importe techniquement c'est de faire les calculs de sectionnage sur un même "morceau".

Or je peux calculer ces "morceaux" dans PostGis : il y a une fonction ST_LineMerge() qui permet de demander "rassemble les points de la géométrie en lignes continues ('morceaux')".

Ensuite il faut juste ensuite décider du "morceau" sur lequel on va faire le calcul. Et je pense qu'un calcul de distance avec ST_Distance() peut suffire : pour chaque numéro demandé on prend le "morceau" qui dont la distance au numéro est la plus faible. Et si les morceaux ne correspondent pas, c'est une erreur (je pense ?).

johanricher commented 8 months ago

je me demande juste si ça peut créer des problèmes plus tard pour les GPS

Je pense que tu as raison, calculer une ligne moyenne, centrale, qui ne correspond à aucune des 2 voies, risque de ne pas être validé. Par Waze notamment, où l'on voit que leur modélisation est équivalente à celle de la BD TOPO :

image

Autre idée/question pour ce genre de cas :

Dans la BD TOPO pour cette voie il y a une géométrie qui est donnée mais 2 lignes bien distinctes. Est-ce qu'il est possible des les isoler et de récupérer seulement une des 2 lignes ? c'est à dire dire celle qui serait la proche du point d'adresse (proximité déterminée par un calcul).

florimondmanca commented 8 months ago

Dans la BD TOPO pour cette voie il y a une géométrie qui est donnée mais 2 lignes bien distinctes. Est-ce qu'il est possible des les isoler et de récupérer seulement une des 2 lignes ? c'est à dire dire celle qui serait la proche du point d'adresse (proximité déterminée par un calcul).

Oui ce serait précisément l'idée que j'ai essayé de décrire ci-dessus, je pense

Isoler la ligne la plus proche du point d'adresse

florimondmanca commented 7 months ago

Il y a des suites à donner à #669...

=> Nécessite de résoudre le problème de l'orientation des voies : est-ce que le métier utilise l'orientation par numéro d'adresse ? Par sens de circulation ?

La BD TOPO a un ordre interne de numérisation (ordre des points dans le linéaire), qui n'est pas forcément ni le sens de numérotation de maisons (quand il existe), ni le sens de circulation.

On en discutait ci-dessus.

Par exemple

Screenshot from 2024-03-26 14-52-07

Screenshot from 2024-03-26 14-50-30

La limitation est certes d'ordre technique. Le calcul PostGIS plante si on lui passe un linéaire de voie qui est en plusieurs morceaux : il faut "choisir" sur quel morceau on essaie d'interpoler le début/fin de section.

An exception occurred while executing a query: SQLSTATE[XX000]: Internal error: 7 ERROR: line_locate_point: 1st arg isn't a line

(Actuellement ça se manifestera par "La voie n'est pas valide, veuillez vérifier le nom ou les numéros" dans l'UI.)

Mais elle est aussi logique : si une rue (définie comme un ensemble de morceaux) a des noeuds d'ordre 3 ou plus (c'est-à-dire que 3 morceaux ou plus portant le nom de la rue s'y rejoignent), comme la Rue du Faubourg de Paris à Valenciennes au niveau du rond-point, quelle approche de validation prend-on ? Est-ce que le début et la fin doivent être sur le même segment ?

Screenshot from 2024-03-26 14-47-12

Screenshot from 2024-03-26 14-49-13

MathieuFV commented 7 months ago

Premier élément de réponse : on a vu lorsqu'on a rencontré la métropole de Rennes qu'ils utilisaient l'orientation par points cardinaux pour définir le sens impacté par une réglementation. Ex : le boulevard de Rochechouart dans le sens Ouest -> Est.

Ça ne nous aide pas beaucoup malheureusement car encore une fois, il s'agit d'une définition qui fonctionne lorsque c'est lu par un humain. Pour une machine c'est une autre histoire...

Second point sur les noeuds : que se passe t il pour l'ordre de numérisation des points de la BD TOPO au passage d'un noeud ?

Sur un noeud d'ordre 2 est ce qu'il y a une réinitialisation de l'ordre de numérisation ?

Sur un noeud d'ordre 3 ou plus s'il n'y a pas de réinitialisation au passage du noeud comment est choisie la branche sur laquelle on continue de numéroter les points ?

Je suppose en effet que le sens de circulation permettrait de démêler ce problème. La proposition d'Aurelie en utilisant les points A et B me semble bonne.

On prend les points du linéaire les plus proches de A et B, on compare les ordres de numérisation des deux points.

A partir de là on a plusieurs possibilités :

1/ on est sur un linéaire continu et sur une route bidirectionnelle. A ce moment là pas de sujet on coupe le linéaire aux points les plus proches de A et de B

2/ on est sur un linéaire continu avec des chaussées séparées. On peut exploiter le sens renseigné par l'utilisateur (A->B ou B->A) pour choisir celui des deux linéaires qui correspond. Sachant qu'il faudrait garder la possibilité de ne pas spécifier de sens, auquel cas on prendrai les deux côtés de la rue en même temps.

3/ on est sur un linéaire discontinu avec un noeud d'ordre 2 et sur une route bidirectionnelle.

A ce moment là, soit la numérotation BD TOPO se réinitialise au droit du noeud, soit elle est continue au travers du noeud.

Le second cas est assez trivial je crois car il revient à la situation 1/ en arrêtant le calcul au droit du noeud : je prends le point de début et je vais dans la direction du point de fin jusqu'au noeud, puis je reprends après le noeud et je finis jusqu'au point de fin.

Le premier cas par contre c'est autre chose car on ne peut pas savoir dans quelle direction doit aller sur le linéaire pour relier le point A et le point B.

On pourrait imaginer utiliser des propriétés géométriques du linéaire, en regardant dans quelle direction on se "rapproche" du point de fin en partant du point de début, mais on peut dans ce cas imaginer un contre exemple d'une voie circulaire où les points de début et de fin sont proches. A ce moment là on voit qu'au début du linéaire il faut s'éloigner du point de fin pour suivre le linéaire, qui finit bien par le rejoindre.

Dans ce cas là je ne vois donc pas comment on peut mettre une méthode en place pour garantir qu'on choisit le bon linéaire à chaque fois.

Il y a des cas plus complexes encore avec les routes à chaussées séparées qui ont des noeuds, d'ordres 2 ou 3 et plus.

Je ne sais pas si toute cette réflexion est utile mais c'est la manière dont je comprends le problème, je crois que ça peut au moins servir à nous synchroniser si j'ai loupé quelque chose.

florimondmanca commented 7 months ago

Second point sur les noeuds : que se passe t il pour l'ordre de numérisation des points de la BD TOPO au passage d'un noeud ? Sur un noeud d'ordre 2 est ce qu'il y a une réinitialisation de l'ordre de numérisation ? Sur un noeud d'ordre 3 ou plus s'il n'y a pas de réinitialisation au passage du noeud comment est choisie la branche sur laquelle on continue de numéroter les points ?

Je ne sais pas ce que serait une "réinitialisation", mais il n'y a pas de garantie ou de standard sur l'ordre de numérisation sur un tronçon il me semble

De notre côté on passe la géométrie à la moulinette de la fonction ST_LineMerge de PostGIS qui fait de son mieux pour regrouper les lignes en ignorant leur orientation. Il y a quelques dessins dans la doc : https://postgis.net/docs/ST_LineMerge.html

Grâce à #700 on devrait déjà pouvoir gérer pas mal de nouvelles situations, pourvu que les points demandés tombent sur le même "morceau".

MathieuFV commented 7 months ago

Quand tu parles d'ordre de numérisation je comprends que chaque point d'une ligne a quelque part un numéro, et que ce numéro s'incrémente dans un sens de la ligne, mais je me trompe peut être ? A ce moment là je me demandais ce qu'il advenait du numéro en question au passage d'un nœud, est-ce qu'il continue comme si de rien était ? Est-ce qu'il revient à 0 ou 1 ? Que se passe t il quand on a un noeud d'ordre 3 ou plus pour l'ordre de numérisation ?

florimondmanca commented 7 months ago

Je t'avoue Mathieu que je ne comprends pas forcément l'enjeu de cette réflexion mais je vais essayer de te répondre :sweat_smile:

Quand tu parles d'ordre de numérisation je comprends que chaque point d'une ligne a quelque part un numéro, et que ce numéro s'incrémente dans un sens de la ligne, mais je me trompe peut être ?

Non pas vraiment, je veux parler de l'ordre dans lequel les points apparaissent dans la donnée (= l'ordre dans lequel la BD TOPO nous fournit les points des lignes)

{
  "type": "MultiLineString",
  "coordinates": [
    [0.3, 2.4], // 1er point
    [0.4, 2.3], // 2e point
    // ... etc
  ]
}

A ce moment là je me demandais ce qu'il advenait du numéro en question au passage d'un nœud, est-ce qu'il continue comme si de rien était ?

Tout dépend de comment le "noeud" (= recoupement de lignes ?) est représenté "physiquement" dans la BD TOPO

Par exemple, un T (noeud de degré 3) peut être représenté aussi bien par 2 LINESTRINGS dont l'une vient toucher l'autre, que par 3 LINESTRINGs dont l'une des extrémités est commune.

D'un point de vue technique comme je disais au dessus ça importe peu car PostGIS nous permet de faire des calculs en "normalisant" cette représentation

On ne fait pas de calculs en tenant compte de l'ordre de numérisation à ce stade. Pour la gestion des sens de circulation ça va nous intéresser mais peut-être qu'on devrait d'abord poser les cas d'usage, et ensuite on verra techniquement ce qui peut être fait...