Points de connexion (arrêts de bus), qui ne se trouvent pas sur les lignes (LINESTRING), au réseau?

9

J'ai besoin de connecter des arrêts de bus (points) à une couche réseau (données OSM). Ces arrêts de bus ne se trouvent pas directement sur les lignes (voir capture d'écran) et leur emplacement ne doit pas être déplacé. J'utilise PostGIS, pgrouting et QGIS et le réseau est déjà routable avec des colonnes source et cible, etc.

entrez la description de l'image ici

Je veux surtout faire deux choses après:

  1. Obtenir les distances entre les arrêts de bus en utilisant l'analyse du chemin le plus court.
  2. Création d'isochrones à distance de marche de l'arrêt de bus en utilisant le réseau OSM.

Pour obtenir des valeurs exactes, il est nécessaire que le routage «démarre» et «s'arrête» le plus près des arrêts de bus. Dans de nombreux cas, le nœud existant le plus proche sera trop éloigné pour obtenir des valeurs exactes. Mais il ne devrait pas y avoir de routage vers l'emplacement réel du point de l'arrêt de bus. Dans mon exemple sur l'image, vous pouvez voir à quoi devrait ressembler le routage entre les arrêts.

Y a-t-il une possibilité d'insérer automatiquement de nouveaux nœuds dans le réseau (LINESTRING) qui sont les plus proches des arrêts de bus ou est-il possible de commencer le routage sur une sorte de `` point factice '' qui est défini uniquement pour la requête (similaire à ce que la route plugin graphique dans QGIS fait)?

Setraworks
la source

Réponses:

5

La première partie de la solution est la suivante:

SELECT a.id, ST_Closestpoint(ST_Collect(b.geom_way), a.geom) AS geom 
FROM point_table a, line_table b
GROUP BY a.id, a.geom;

Cela arrête les arrêts de bus sur les lignes du réseau routier comme vous pouvez le voir sur l'image et fonctionne assez facilement.

entrez la description de l'image ici

Ensuite, je vais essayer de diviser les lignes aux emplacements des points. Après avoir divisé les lignes, je veux utiliser à nouveau pgr_createTopology. Après cela, il devrait être possible de créer une requête pour trouver les nœuds les plus proches des arrêts de bus, qui seront alors mes nœuds nouvellement générés aux «points de partage».

Je serais reconnaissant à quelqu'un d'avoir une idée de la façon de diviser la chaîne de caractères avec des entités ponctuelles dans Postgis, car après avoir examiné des questions similaires, il ne semble pas être une solution facile pour le moment.

Setraworks
la source
ST_Split (quelque chose à couper, lame)
simplexio
1
ajouter un commentaire parce que je n'ai pas testé cela du tout, la syntaxe est probablement fausse ... ... sélectionnez *, st_split (a.lg, a.pg) à partir de (sélectionnez *, lines.g comme lg, points.geom comme pg à partir des points joindre des lignes sur ST_intersect (p.geom, l.geom)) en tant que mais collection de retour fractionnée de sorte que vous devez toujours en retirer toutes les lignes ...
simplexio
2

Ceci est ma solution complète. Cela implique une sorte de piratage pour effectuer le fractionnement: j'obtiens les points sur les lignes (façons d'utiliser la terminologie OSM) en les utilisant ST_ClosestPoint, puis je les tamponne sur une très petite distance pour que le fractionnement fonctionne réellement. Sinon, des erreurs d'imprécision / d'arrondi empêchaient le fractionnement.

Cela a le problème de générer deux divisions sur chaque ligne par point (en raison de la mise en mémoire tampon). Pour mon utilisation, cela allait bien, car plus tard, j'ai routé entre les points de division les plus proches des points d'origine, qui étaient en dehors de la ligne, et cela pourrait être l'un des deux points de division de l'intersection ligne-tampon.

J'ai commencé par télécharger les données OSM et les intégrer dans Postgres:

CITY="MY_CITY"
BBOX="-46.6003,-23.7362,-46.4806,-23.5965"
wget --progress=dot:mega -O "$CITY.osm" "http://www.overpass-api.de/api/xapi?*[bbox=${BBOX}][@meta]"

# create database
createdb my_database
# add extensions
psql -d my_database -c "CREATE EXTENSION postgis;"
psql -d my_database -c "CREATE EXTENSION pgrouting;"

# import osm data to postgres database
osm2pgrouting \
    -f MY_CITY.osm \
    -d my_database \
    -U user

# load points into db
shp2pgsql -I -s 4326 points_to_split_ways.shp public.points_to_split_ways | psql -d my_database

Diviser les voies à l'aide d'un tampon:

WITH pts_ways AS (
  -- get nearest way for each point we want to split the ways by
  SELECT s.gid AS pt_id, ws.gid AS way_gid, s.geom AS pt_geom, ws.the_geom AS way_geom FROM points_to_split_ways s
  CROSS JOIN LATERAL
  (
    SELECT w.gid, w.the_geom
    FROM ways w
    ORDER BY s.geom <-> w.the_geom LIMIT 1
  ) AS ws
), pts_on_ways AS (
  -- "move" these points to be on top of the ways
  SELECT pt_id, way_gid, ST_ClosestPoint(way_geom, pt_geom) as geom
  FROM pts_ways
), ways_without_pts AS (
  -- get the ways that don't have any points on them
  SELECT the_geom as the_geom, gid as way_gid FROM ways
  WHERE gid NOT IN (SELECT way_gid FROM pts_ways)
)
SELECT
  way_gid as old_id,
  -- we need to build a new unique ID, because split ways will share the old ID
  row_number() over(order by way_gid) as gid,
  -- this is the split way geometry
  the_geom
FROM (
  SELECT 
    way_gid,
    -- split the ways and dump into indiviudal segments
    (ST_Dump(ST_Split(line_geom, pt_geom))).geom AS the_geom
  FROM (
    (SELECT the_geom as line_geom, gid FROM ways) AS lines
    LEFT JOIN
    -- HACK: use a buffer to fix imprecisions / rounding errors
    -- this will generate one extra splitting per point (each buffer will intersect each way twice)
    -- but it's ok for our purposes
    -- also, collect them grouped by the way to handle cases where there are multiple points on the same way
    (SELECT ST_Collect(ST_Buffer(geom, 0.000001)) as pt_geom, way_gid FROM pts_on_ways GROUP BY way_gid) AS pts
    ON lines.gid = pts.way_gid
  ) AS tmp1
  -- union the ways without points, otherwise you'd get only the ones that were split
  UNION ALL
  SELECT way_gid, the_geom FROM ways_without_pts
) AS tmp2;

Créez la topologie nécessaire pour le routage avec pgrouting:

SELECT UpdateGeometrySRID('ways_split','the_geom', 4326);
SELECT find_srid('public','ways_split','the_geom');
ALTER TABLE ways_split ADD COLUMN "source" integer;
ALTER TABLE ways_split ADD COLUMN "target" integer;
ALTER TABLE ways_split ADD PRIMARY KEY (gid);
ALTER TABLE ways_split ADD CONSTRAINT ways_source_fkey FOREIGN KEY (source) REFERENCES ways_split_vertices_pgr (id) MATCH FULL;
ALTER TABLE ways_split ADD CONSTRAINT ways_target_fkey FOREIGN KEY (target) REFERENCES ways_split_vertices_pgr (id) MATCH FULL;
SELECT pgr_createTopology('ways_split', 0.00001, 'the_geom', 'gid', clean := TRUE);
SELECT pgr_analyzeGraph('ways_split', 0.000001, the_geom := 'the_geom', id := 'gid');
bplmp
la source
Ma première pensée a également été le tampon. Mais si vous pouvez obtenir une `` distance au plus proche '', mettre en mémoire tampon ce montant, créer un point à cette intersection ... alors vous pouvez créer une ligne avec les extrémités comprenant votre point d'origine et le point `` le plus proche ''.
Mox
1

Comme je travaille sur une tâche similaire, je voulais juste parler de l'approche que j'utilise actuellement. Cela utilise GRASS GIS, mais en ce qui concerne mes expériences avec PostGIS, il est assez compliqué d'ajouter plusieurs nouveaux points aux lignes de chaîne existantes en divisant ces lignes de chaîne aux emplacements respectifs - bien que je suis sûr qu'il existe une solution pratique.

J'ai maintenant utilisé la v.netfonction GRASS GIS en utilisant l'option connect. Choisissez simplement input vector line layeret points layer. Il y a la possibilité soit d'aligner les points sur le point le plus proche sur les lignes, soit de créer de nouvelles connexions entre le point le plus proche sur les lignes et le nouveau point.

Voici une image avant et après. Sur le côté droit, pour chaque point de la couche de points, un nœud sur le réseau routier a été ajouté: entrez la description de l'image ici

Ensuite, dans PostGIS, après avoir créé votre ..._vertices_pgrtable à partir du réseau routier, affectez simplement vos points au sommet le plus proche afin de pouvoir les utiliser dans vos demandes de routage. Pour cette tâche, vous pouvez utiliser la ST_ClosestPointfonction telle que effectuée par @Setraworks dans sa réponse.

Les inconvénients de cette approche sont:

  • la connexion des points avec les lignes doit être effectuée dans GRASS GIS
  • les itinéraires calculés peuvent être constitués de nombreux composants (selon la quantité de points nouvellement ajoutés)
  • ajout dynamique de nouveaux points impossible

Cette approche fonctionne bien si vous avez un nombre défini de points à ajouter au réseau routier (comme dans l'exemple de la question avec les arrêts de bus).

Si quelqu'un peut fournir un exemple de travail en utilisant PostGIS, j'aimerais en savoir plus à ce sujet!

SchoGeo
la source
0

Il existe un message qui traite d'un problème similaire, vous pouvez le voir à l'emplacement suivant: http://osdir.com/ml/qgis-user-gis/2011-11/msg00220.html

Ryan Garnett
la source
Ceci n'est qu'une partie d'une solution possible, car après avoir accroché des points aux lignes, les points se trouvent directement sur les lignes, mais ils ne font toujours pas partie du réseau.
Setraworks
Si vous vous attendez à obtenir une réponse qui vous fournisse toutes vos exigences, vous pourriez être déçu. Cela peut vous amener à mi-chemin, alors vous pouvez vous concentrer sur l'autre partie qui vous manque.
Ryan Garnett
Je pense que tu as raison Ryan. J'ai déjà réussi à aligner les points sur les lignes, donc la prochaine étape sera de savoir comment diviser les chaînes de lignes avec des points dans les postgis. Merci pour votre aide jusqu'à maintenant!
Setraworks
Heureux d'avoir pu aider. Il existe des outils qui sépareront une ligne avec un point, mais je continuerai à chercher une option dans PostGIS. Bonne chance
Ryan Garnett
@Setraworks, vous souhaiterez peut-être consulter l'option PostGIS suivante (ST_Split) postgis.net/docs/ST_Split.html . Vous pouvez diviser une ligne avec un point, voici l'explication de PostGIS: La fonction prend en charge la division d'une ligne par point, d'une ligne par ligne, d'un polygone par ligne. La géométrie renvoyée est toujours une collection.
Ryan Garnett