Construire un polygone sur une zone accessible

10

Je travaille actuellement dans le domaine des isochrones et des algorithmes sous-jacents. Ce qui pose problème maintenant, ce n'est plus le calcul de l'isochrone elle-même, mais la visualisation des résultats.
Le résultat de mon algorithme isochrone sont des points et des bords. En fait, j'ai une solution qui fonctionne, mais pour 3873 bords et pour 1529 nœuds, les choses semblent prendre une éternité (environ 2,0 secondes sur mon ordinateur portable Lenovo T440s qui contient un processeur Core i7 2015 et un SSD assez rapide). Au lieu de quelques secondes, je veux quelque chose de plus comme msec :-).

Peut-être que quelqu'un peut m'aider à réduire le temps de calcul nécessaire pour construire les polygones qui visualisent les zones accessibles.

Mais attendez ... tout d'abord!
Voici une visualisation des bords que je suis le résultat du calcul de mon isochrone: Résultat du calcul d'isochrone (squelette existant de chaînes de lignes) Ces bords sont stockés dans une table de base de données PostGIS et sont de simples chaînes de lignes.

Ce que je veux montrer à l'utilisateur ressemble à ceci: entrez la description de l'image ici Notez les zones déconnectées à l'extrême sud et à l'est de l'image. Ceux-ci doivent être dessinés comme des zones distinctes (donc aucune fusion n'est autorisée ici :-))

Actuellement, j'utilise cette requête:

SELECT ST_AsGeoJson(St_Transform(ST_Multi(ST_Collect(polygons)), 4326)) AS coverage FROM (
    SELECT ST_MakePolygon(ST_ExteriorRing(ST_GeometryN(segments, generate_series(1, ST_NumGeometries(segments))))) AS polygons FROM (
        SELECT ST_Union(ST_Buffer("GEOMETRY", 20, 'quad_segs=2')) AS segments FROM my_edges AS a
    ) AS b
) AS c

J'ai déjà fait des expériences et j'ai également lu beaucoup de documentation, mais je ne peux tout simplement pas trouver une meilleure solution.
À mes yeux, le gros problème est l'utilisation de ST_Union (comme indiqué dans la documentation, cette fonction peut être lente). La chose très intéressante est que le remplacer par ST_Collect semble ralentir le calcul de ST_Buffer afin que la requête suivante prenne encore plus de temps, bien qu'elle ne remplisse pas les zones entre les bords (elle crée seulement un tampon autour des lignes ):

SELECT ST_AsGeoJson(St_Transform(ST_Multi(ST_Collect(polygons)), 4326)) AS coverage FROM (
    SELECT ST_Buffer(ST_Collect(ST_LineMerge("GEOMETRY")), 20, 'quad_segs=2') AS polygons FROM my_edges AS a
) AS b

Cela prend environ 3,8 secondes sur mon système (soit près de deux fois le temps). Ma première conclusion à partir de ce petit benchmark est que ST_Buffer devient incroyablement lent en ce qui concerne MultiLineStrings (encore plus lent que lors de la création de tampons pour chaque ligne et de la fusion des tampons - ce qui à mes yeux est juste bizarre)

J'ai également essayé d'utiliser des formes alpha (en utilisant l'implémentation de pgRouting), mais comme il n'y a pas de valeur alpha à définir (et en fait, je ne voudrais pas vraiment maintenant à quelle valeur définir une telle valeur), j'obtiens juste un grand polygone ( je perdrais donc les régions de l'extrême sud et de l'est en tant que régions distinctes, ce qui n'est pas ce que je veux).
Aussi ST_Polygonize (qui était la première chose qui m'est venue à l'esprit) n'a produit aucun résultat utilisable, mais peut-être que j'ai raté quelque chose ici ...

Existe-t-il un meilleur moyen de créer la zone affichée dans PostGIS? Peut-être aussi en utilisant du code java (jts) ou du code javascript côté client (jsts)? En fait, je pourrais vivre en perdant certains détails tant que les zones affichées dans mon résultat restent séparées et que le calcul devient (beaucoup) plus rapide.

Nikolaus Krismer
la source
Pouvez-vous non seulement utiliser ST_Exteriorring (ST_Dump (ST_Union (ST_Buffer (geom, ....)))). Vous devrez peut-être tester les Linestrings qui résultent parfois de ST_Union après un tampon, mais c'est facile avec ST_GeometryType (geom). En ce qui concerne Java ou jsts, vous pouvez, mais il est peu probable que ce soit plus rapide, étant donné qu'un une grande partie des fonctions Postgis (GEOS) sont en premier lieu des ports C / C ++ de JTS
John Powell
Vous avez raison, cela fonctionne, mais en fait ce n'est pas plus rapide (prend environ 3,1 secondes, alors que l'utilisation de GeometryN prend 2 secondes). Voici ce que j'ai utilisé: SELECT ST_AsGeoJson (ST_Transform (ST_Exteriorring ((ST_Dump (ST_Union (ST_Buffer ("GEOMETRY", 20)))).. Geom), 4326)) FROM my_edges;
Nikolaus Krismer
@ john-barça: Oh .. je brume la partie quad_segs = 2 dans le ST_Buffer lorsque vous essayez votre approche ... avec ce changement, les requêtes sont paires (les deux à environ 2 secondes). Cependant, c'est encore très lent (à mes yeux), y a-t-il une autre façon de l'essayer?
Nikolaus Krismer
Problème intéressant .... voulez-vous partager des données de test?
dbaston
Si cela aide, je suis heureux de partager certaines données. Toutes les choses que je fais ici sont open-source, donc cela ne devrait pas être un gros problème. Première chose à noter: une application Web pour les tests se trouve sur dbis-isochrone.uibk.ac.at:8080/testing . Plus d'informations sur les choses sur lesquelles je travaille sont disponibles sur dbis-isochrone.uibk.ac.at . Dans la section "liens" du site Web, il y a d'autres références (y compris des données de test)
Nikolaus Krismer

Réponses:

5

Mis à part la sérialisation GeoJSON, ce qui suit prend environ 6,3 secondes sur mon ordinateur portable:

SELECT
  ST_MakePolygon(
    ST_ExteriorRing(
      (ST_Dump(
        ST_Union(
          ST_Buffer(geom, 20, 2)))).geom))
FROM bz_edges

En regardant les données dans OpenJUMP, j'ai remarqué pas mal de détails dans les segments de rue, par rapport au niveau de détail souhaité dans la sortie. Il semble que même une simplification à la volée de ces lignes puisse produire une grande accélération dans PostGIS:

SELECT
  ST_MakePolygon(
    ST_ExteriorRing(
      (ST_Dump(
        ST_Union(
          ST_Buffer(ST_Simplify(geom, 10), 20, 2)))).geom))
FROM bz_edges

ce qui ramène les choses à 2,3 secondes. Je pensais que je pourrais peut-être faire mieux en stockant la géométrie généralisée dans une colonne séparée, au lieu de la calculer à la volée, mais cela n'a en fait fourni aucun avantage supplémentaire.

En fonction de la quantité de code que vous êtes prêt à écrire, vous pouvez certainement faire mieux en Java, si rien d'autre, car vous pouvez profiter de plusieurs cœurs. (Pour ce que ça vaut, JTS effectue l'opération ci-dessus en 2,8 secondes). Une approche pourrait être d'étendre CascadedPolygonUnionpour que certaines opérations syndicales se déroulent en parallèle. (mise à jour - voici un ParallelCascadedPolygonUnion )

J'ai remarqué dans les exemples de données que les bords sont stockés avec des références à leurs nœuds de début et de fin, c'est-à-dire que vous avez un graphique pré-construit. Je soupçonne que vous pouvez générer ces polygones beaucoup plus rapidement si vous travaillez à partir du graphique au lieu d'utiliser des opérations de géométrie génériques. Par exemple, je pense que vous pourriez donc quelque chose comme ça:

  1. identifier les composants connectés du graphique
  2. pour chaque composant connecté, trouvez le nœud avec la coordonnée X minimale (garanti d'être à l'extérieur du composant)
  3. parcourez les bords du composant, en tournant toujours à gauche (ou à droite) lorsque cela est possible. Cela vous donnera l'anneau extérieur de chaque composant.
  4. polygoniser l'anneau extérieur et le tampon de manière appropriée.
dbaston
la source
Merci ... la simplification est une grande et même une amélioration "simple". Cela a pris le temps nécessaire sur mon ordinateur portable à 1,5 s. Ce n'est pas où je veux être, mais un peu mieux.
Nikolaus Krismer
Concernant votre solution suggérée (points 1-4). Cela semble également très simple et mérite un essai. J'ai pensé à quelque chose de similaire, mais je suis bloqué au point1 (donc très tôt :-)). Comment identifier les composants connectés (la seule chose à laquelle je peux penser est une requête récursive qui pourrait également être très lente).
Nikolaus Krismer
@NikolausKrismer J'utilise à la fois JGraphT et loom pour des tâches comme celle-ci. Si vous écrivez vos propres méthodes graphiques à la place (ce n'est pas une mauvaise idée pour de meilleures performances), une recherche en profondeur vous trouvera les composants. (Vous pouvez les trouver dans le prochain PostGIS 2.2 avec ST_ClusterIntersectingmais je pense que vous voudrez que tout type de traitement de graphique se produise en dehors de la base de données, donc ce n'est probablement pas utile).
dbaston
ce sont de bons conseils. J'ai regardé JGraphT et cela pourrait certainement aider à résoudre mon problème. Cependant, j'ai également examiné Postgis 2.2 et la fonction ST_ClusterIntersecting -> il faut environ 200 à 250 ms pour identifier les différents clusters dans le cas ci-dessus. C'est OK pour moi (JGraphT pourrait certainement faire mieux). Maintenant, je dois gérer la création de exteriorRing (ST_ExteriorRing échoue, car ST_MakePolygon dit que mes liens ne sont pas un shell)
Nikolaus Krismer
Je vois deux complications: (a) vous avez besoin non seulement de l'anneau extérieur, mais aussi de tous les segments qui s'étendent vers l'extérieur de cet anneau, et (b) il semble que vos lignes ne se coupent pas réellement à certaines intersections. Vous devrez corriger (b) si vous essayez de construire une géométrie à partir des résultats d'une marche de graphe.
dbaston