Pontos de conexão (pontos de ônibus), que não ficam nas linhas (LINESTRING), à rede?

9

Preciso conectar pontos de ônibus (pontos) a uma camada de rede (dados OSM). Esses pontos de ônibus não ficam diretamente nas linhas (veja a captura de tela) nem devem ser movidos. Uso PostGIS, pgrouting e QGIS e a rede já é roteável com colunas de origem e destino, etc.

insira a descrição da imagem aqui

Principalmente, quero fazer duas coisas depois:

  1. Obter as distâncias entre os pontos de ônibus usando a análise de caminho mais curto.
  2. Criando isócrones com distâncias a pé do ponto de ônibus usando a rede OSM.

Para obter valores exatos, é necessário que o roteiro 'inicie' e 'pare' mais próximo dos pontos de ônibus. Em muitos casos, o nó existente mais próximo estará muito longe para obter valores exatos. Mas não deve haver um roteamento para o local real do ponto de ônibus. No meu exemplo da figura, você pode ver como deve ser o roteamento entre as paradas.

Existe a possibilidade de inserir automaticamente novos nós na rede (LINESTRING) que estão mais próximos aos pontos de ônibus ou é possível iniciar o roteamento em um tipo de 'ponto fictício' definido apenas para a consulta (semelhante ao que a estrada plugin gráfico no QGIS faz)?

Setraworks
fonte

Respostas:

5

A primeira parte da solução é esta:

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;

Isso encaixa os pontos de ônibus nas linhas da rede rodoviária, como você pode ver na figura e funciona bastante fácil.

insira a descrição da imagem aqui

Em seguida, tentarei dividir as linhas nos locais dos pontos. Depois de dividir as linhas, quero usar o pgr_createTopology novamente. Depois disso, deve ser possível criar uma consulta para descobrir os nós mais próximos dos pontos de ônibus, que serão os meus nós recém-gerados nos 'pontos de divisão'.

Ficaria muito grato se alguém tivesse uma dica para mim de como dividir a cadeia de linhas com recursos de pontos no postgis, pois depois de analisar questões semelhantes, não parece haver uma solução fácil para isso no momento.

Setraworks
fonte
ST_Split (algo em foco, lâmina)
simplexio
11
adicionando comentário porque eu não testei isso, o syntac provavelmente está errado ... ... selecione *, st_split (a.lg, a.pg) em (selecione *, lines.g como lg, points.geom como pg a partir de pontos juntar linhas em ST_intersect (p.geom, l.geom)) como uma, mas que a coleta de divisão de retorno para que você ainda precisa obter todas as linhas fora dele ...
simplexio
2

Esta é a minha solução completa. Isso envolve uma espécie de hack para fazer a divisão: eu obtenho os pontos nas linhas (maneiras de usar a terminologia OSM) ST_ClosestPoint, e os amortizo por uma distância muito pequena para que a divisão funcione. Caso contrário, erros de imprecisão / arredondamento impediam a divisão.

Isso tem o problema de gerar duas divisões em cada linha por ponto (devido ao buffer). Para meu uso, isso foi bom, pois mais tarde eu rotei entre os pontos de divisão mais próximos dos pontos originais, que estavam fora da linha, e poderia ser um dos dois pontos de divisão da interseção do buffer de linha.

Comecei baixando os dados do OSM e inserindo-os no 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

Dividindo as maneiras usando um buffer:

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;

Crie a topologia necessária para rotear com 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
fonte
Meu primeiro pensamento foi buffer também. Mas se você puder obter uma 'distância para o mais próximo', amortecer esse valor, criar um ponto nessa interseção ... então você poderá criar uma linha com os pontos finais que consistem no seu ponto original e o ponto 'mais próximo' a ele.
Mox
1

Como estou trabalhando em uma tarefa semelhante, só queria contar sobre a abordagem que estou usando atualmente. Isso faz uso do GRASS GIS, mas, no que diz respeito às minhas experiências com o PostGIS, é bastante complicado adicionar vários novos pontos aos LineStrings existentes dividindo-os nos respectivos locais - embora eu tenha certeza de que existe uma solução conveniente.

I já fez uso do GRASS GIS v.netfunção usando a opção connect. Basta escolher input vector line layere points layer. Há a opção de encaixar os pontos no ponto mais próximo das linhas ou criar novas conexões entre o ponto mais próximo das linhas e o novo ponto.

Aqui está uma imagem antes e depois. No lado direito, para cada ponto da camada de pontos, um nó na rede rodoviária foi adicionado: insira a descrição da imagem aqui

Posteriormente no PostGIS, depois de criar sua ..._vertices_pgrtabela fora da rede rodoviária, atribua seus pontos ao vértice mais próximo para que você possa usá-los em suas solicitações de roteamento. Para esta tarefa, você pode fazer uso da ST_ClosestPointfunção conforme executada por @Setraworks em sua resposta.

As desvantagens dessa abordagem são:

  • a conexão de pontos com linhas deve ser feita no GRASS GIS
  • as rotas calculadas podem consistir em muitos componentes (dependendo da quantidade de pontos adicionados recentemente)
  • adição dinâmica de novos pontos não é possível

Essa abordagem funciona bem se você tiver um número definido de pontos a serem adicionados à rede rodoviária (como no exemplo da pergunta com pontos de ônibus).

Se alguém puder fornecer um exemplo de trabalho usando o PostGIS, eu adoraria ler sobre isso!

SchoGeo
fonte
0

Há uma postagem que discute um problema semelhante. Você pode vê-la no seguinte local: http://osdir.com/ml/qgis-user-gis/2011-11/msg00220.html

Ryan Garnett
fonte
Isso é apenas parte de uma solução possível, porque, depois de alinhar os pontos nas linhas, os pontos ficam diretamente nas linhas, mas ainda não fazem parte da rede.
Setraworks
Se você espera obter uma resposta que forneça todos os seus requisitos, pode ficar desapontado. Isso pode levá-lo a meio caminho, então você pode se concentrar na outra parte que está faltando.
Ryan Garnett
Eu acho que você está certo, Ryan. Eu já consegui encaixar os pontos nas linhas, então o próximo passo será descobrir como dividir cadeias de linhas com pontos no postgis. Obrigado pela vossa ajuda até agora!
Setraworks
Ainda bem que pude ajudar. Existem ferramentas que dividirão uma linha com um ponto, mas continuarei procurando uma opção no PostGIS. Boa sorte
Ryan Garnett
@ Setraworks, você pode procurar a seguinte opção PostGIS (ST_Split) postgis.net/docs/ST_Split.html . Você pode dividir uma linha com um ponto, eis a explicação do PostGIS: A função suporta dividir uma linha por ponto, uma linha por linha, um polígono por linha. A geometria retornada é sempre uma coleção.
Ryan Garnett