Comment rendre les passages piétons avec TileMill

Voulant étendre le rendu "OSM" sur un niveau de zoom supplémentaire (19), j'ai voulu y ajouter les passages piétons.

L'objectif est de tracer chaque passage en respectant son orientation par rapport à la route, le tout en partant d'un schéma postgrestql/postgis classique de type osm2pgsql.

Assez rapidement j'identifie plusieurs cas de figure:

  • le nœud du passage piéton se trouve au milieu d'un way voir, à l'intersection de plusieurs
  • le nœud se trouve à l'extrémité de 2 ways (ou plus)
  • une combinaison des deux...

Il va donc falloir jouer avec postgis pour récupérer les directions des tronçons de voirie à l'intersection.

Premièrement, sélectionner les nœuds... relativement facile:

SELECT p.osm_id, h.osm_id FROM planet_osm_point p JOIN planet_osm_line h ON (SI_Intersects(p.way,h.way) AND h.highway IS NOT NULL WHERE (p.highway='crossing' or p.tags->'crossing' in ('traffic_signals','uncontrolled'))

J'obtiens bien mes nœuds de passage piéton et les ways en intersection.

Maintenant, il faut déterminer les orientations des segments autour de ce nœud, pour cela je vais utiliser un petit tampon autour du nœud et récupérer les intersections entre ce tampon et les tronçons à l'aide de:

degrees(ST_Azimuth(
  ST_Line_Interpolate_Point(ST_Intersection(ST_Buffer(p.way,0.1), h.way),0),
  ST_Line_Interpolate_Point(ST_Intersection(ST_Buffer(p.way,0.1), h.way),1)
)) as angle

Explication...

  • p.way est mon nœud
  • je prend un tampon autour de ce nœud (ST_Buffer), pour avoir l'orientation du way près du nœud (tangente) et pas globalement
  • je récupère la partie du way recouverte par ce buffer (ST_Intersection)
  • puis j'extrais les extrémités via deux ST_Line_Interpolate_Point le paramètre 0 puis 1 permettent d'obtenir chacune des extrémités
  • il ne reste plus qu'à utiliser ST_Azimuth pour obtenir l'angle formé par le passage piéton et chacune des extrémités des mini-tronçons

Une autre chose à prendre en compte c'est l'orientation qui peut être oposée selon le sens de tracé des tronçons. Pour cela, un simple module 180 fait l'affaire.

Le problème suivant est de gérer les différents cas de figure évoqué au début, mais aussi les passages où les tronçons partent en T et Y ou en X.

En faisant la moyenne des orientations, cela fonctionne bien pour les Y tant que l'angle n'est pas trop ouvert. Au final, en comparant la moyenne des orientation avec la différence maximale entre celles-ci on peut déterminer si il est possible de tracer un passage piéton orienté où si il faut une icône non orientée pour les passages en T ou X.

La requête complète donne ceci:

(select osm_id, ST_GeometryN(st_union(way),1) as way, max(angle)-min(angle) as angle_diff, avg(angle) as angle from
  (select p.osm_id, p.way as way, cast(90+degrees(ST_Azimuth(st_line_interpolate_point(ST_Intersection(st_buffer(p.way,0.1), h.way),0),st_line_interpolate_point(ST_Intersection(st_buffer(p.way,0.1), h.way),1))) as integer) % 180 as angle
   from planet_osm_point p join planet_osm_line h on (st_intersects(p.way,h.way) and h.highway is not null and h.highway not in ('footway','cycleway','path','pedestrian','steps','service'))
   where (p.highway='crossing' or p.tags->'crossing' in ('traffic_signals','uncontrolled')) and p.way && !bbox!) as crossing group by osm_id )
as highway_crossings

Le !bbox! sera remplacé dynamiquement par Mapnik par la bbox de la zone sur laquelle est faite le rendu. Il est nécessaire de le préciser dans la sous requête SQL pour que seuls les passages piétons dans cette zone soient cherchés par postgresql.

Il a fallut ajouter un test sur le type de highway=* pour ne orienter les tracés des passages sans tenir compte des voies piétonnes, pistes cyclables, chemins et escaliers.

Du côté style, c'est relativement simple, on récupère l'angle moyen et la différence maxi entre les angles. Si cette différence dépasse par exemple 30°, on dessine une icône nonorientée, sinon, on utiliser angle pour faire une rotation de notre symbole de passage piéton (de petites bandes gris très clair).

#highway_crossings {
  [zoom=19][angle_diff<30]
  {
    point-ignore-placement: true;
    point-file: url('symbols/fr/crossing2.png');
    point-transform: 'rotate([angle])';
  }
  [zoom=19][angle_diff>=30]
  {
    point-file: url('symbols/fr/crossing.png');
    point-transform: 'scale(0.75)';
  }
}

Et voilà ce que cela donne: