Encontrando vizinhos mais próximos entre duas tabelas com localizações de pontos no SpatiaLite?

10

Comecei a jogar com o SpatiaLite hoje e já me deparei com um problema.

Para cada local do ponto armazenado no tableOne, eu gostaria de selecionar um ponto mais próximo (distância linear) da tableTwo.

Até agora, eu vim com uma solução desajeitada que utiliza o VIEW:

CREATE VIEW testview AS 
SELECT 
A.id , 
B.myValue, 
Distance(A.Geometry, B.Geometry) AS distance
FROM tableOne AS A, tableTwo AS B
WHERE distance < 10000
ORDER BY A.Id, distance;

E depois:

SELECT * FROM testview
WHERE distance = (SELECT MIN(distance) FROM testview AS t WHERE t.id = testview.id)

parece fazer o trabalho.

Duas questões:

Existe uma maneira de executar essa consulta sem criar uma VIEW?

Existe alguma outra maneira de otimizar essa consulta para obter melhor desempenho? Em um cenário do mundo real, o tableOne terá centenas de milhares de registros e o tableTwo - 1,3 milhão.

radek
fonte
Posso dar-lhe uma abordagem que é várias ordens de magnitude mais rápido, mas seria necessário que você usar o PostgreSQL índice de 9 knngist vez de SpatiaLite ...
Ragi Yaser Burhum
Na verdade, é mais rápido que GRASS, ArcGIS, QGIS, SQLServer e praticamente qualquer outro GIS espacial de banco de dados / área de trabalho (ainda não tentei a funcionalidade de vizinho mais próximo do Oracle). Deixe-me saber se é uma opção.
Ragi Yaser Burhum
@Ragi: Estou ciente de que o PostGIS seria uma maneira muito mais eficiente de trabalhar com esse problema. No entanto, o objetivo final deste exercício seria criar um aplicativo portátil pequeno e, nesse caso, o SpatiaLite é um vencedor.
Radek #
Qual é a sua plataforma de desenvolvimento para seu aplicativo portátil?
Allan Adair
@ Allan: Trabalhando em ambos: Windows Server 2008 e Ubuntu no momento.
Radek #

Respostas:

5

Acabei de testar este SQL e funciona:

SELECT g1.OGC_FID As id1, g2.OGC_FID As id2, MIN(ST_Distance(g1.GEOMETRY,g2.GEOMETRY)) AS DIST
FROM table_01 As g1, table_02 As g2   
WHERE g1.OGC_FID <> g2.OGC_FID
AND ST_Contains(ST_Expand(g1.geometry,50),g2.geometry)
GROUP BY id1
ORDER BY id1

Como você pode ler aqui "A maneira ingênua de realizar uma consulta vizinha mais próxima é ordenar a tabela candidata à distância da geometria da consulta e, em seguida, registrar o registro com a menor distância".

Cumprimentos,

Andrea

aborruso
fonte
Estou tentando usar essa consulta, mas estou obtendo resultados inesperados - estou obtendo uma tabela resultante, mas com IDs para linhas que vejo que não são o vizinho mais próximo. Estou tentando encontrar a linha mais próxima em uma camada de seqüência de linhas multilinhas para cada ponto em outra camada. Eu sou novo no spatiaLite. Alguma sugestão? Além disso, eu, em última instância deseja executar este em 1 milhão de pontos +
kflaw
Estou também não tenho certeza eu entendo o propósito desta declaração: WHERE g1.OGC_FID <> g2.OGC_FID
kflaw
Além disso, no meu resultado, estou obtendo uma distância nula. Eu tenho jogado ao redor com esta linha: E ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry), bem como o removeu e ainda não obter os valores de distância, mesmo que eu estou recebendo um ID
kflaw
6

Se você não deseja calcular as distâncias entre todas as combinações de pontos, use um índice espacial em uma das tabelas:

SELECT 
  A.id , 
  B.myValue, 
  MIN(Distance(A.Geometry, B.Geometry)) AS distance
FROM tableOne AS A, tableTwo AS B
WHERE A.ROWID IN (
  SELECT ROWID
  FROM SpatialIndex WHERE
    f_table_name = 'A' 
    AND search_frame = BuildCircleMbr(ST_X(B.Geometry), ST_Y(B.Geometry), 10000))
GROUP BY A.id, B.myValue
Samuel
fonte
Tentei usar a solução que você postou, pois preciso usar um índice espacial, mas ele não retorna valores? para a linha f_table_name = 'A', preciso substituir 'A' pelo nome da tabela real (tabela um)? Eu tentei de qualquer maneira e ainda não retorna nada, por isso pode ser isso
kflaw
Você está certo f_table_name = 'A'deveria estar f_table_name = 'tableOne'. Observe que essa solicitação assume espacial> 4.x ( SpatialIndexa tabela virtual é usada). Você tentou ajustar o search_frameseu caso de uso? No exemplo acima, presume-se que os pontos estejam a uma distância máxima de 10.000 metros.
Samuel
Eu brinquei com o valor do quadro de pesquisa, presumo que isso signifique dentro de 10000 metros, o que deve funcionar para mim. Na verdade, eu não sei qual versão do spatialite, criei o banco de dados através do qgis e estou usando o gui no qgis. Deixe-me ver se consigo descobrir isso #
kflaw
É a versão 4.1.1 com sqlite versão 3.7.17, então deve funcionar então? Pergunto-me o que está errado eu vou testá-lo um pouco mais
kflaw
3

Desde a versão 4.4.0, o SpatiaLite suporta um índice de tabela virtual KNN para problemas de vizinhos mais próximos. Aqui está uma consulta que localiza a linha mais próxima em uma tabela de cadeia de linhas para cada ponto em uma tabela de pontos.

SELECT k.* FROM knn k, points p
WHERE f_table_name = 'linestrings' 
AND ref_geometry = p.geometry
AND max_items = 1;
Greg Krakow
fonte
2

Você pode simplificar sua consulta assim.

SELECT 
   A.id , 
   B.myValue, 
   MIN(Distance(A.Geometry, B.Geometry)) AS distance
FROM tableOne AS A, tableTwo AS B
GROUP BY A.id, B.myValue

Para uma solução mais genérica, talvez valha a pena tentar converter essa função de vizinho mais próximo do PostGIS: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/

underdark
fonte
infelizmente, o código resulta em:SQL error: "misuse of aggregate: MIN()"
radek 13/10/11
A partir de PostGIS há também alguns exemplos no site BostonGIS, mas até agora não foi bem sucedido em traduzi-las em SpatiaLite: /
radek