Retornar todos os resultados em um raio de 30 km de um ponto latente / longo específico?

21

Eu tenho uma tabela com uma coluna the_geomque contém dados semelhantes a:

0103000020E61000000100000005000000CE473AACFA071E40F27FB23340744740336FE841C6231E40873BED903F744740FC150A0ACE231E40D19E2684637647409C9B443D00081E409A9AF82664764740CE473AACFA071E40F27FB23340744740

Que ao aplicar a função ST_AsEWKT(the_geom)retorna:

SRID=4326;POLYGON((7.5077921782085 46.9082092877942,7.53493597966353 46.9081898840296,7.53496566473541 46.9249119938446,7.50781341296434 46.9249314035307,7.5077921782085 46.9082092877942))

Preciso selecionar todos os dados que estejam dentro de um raio de 30 km de um ponto latente / longo específico, por exemplo:

  • lat = 46.8167
  • lng = 6.9333

No entanto, sempre que tentei usar ST_Distance(), sempre recebi valores menores que 1 e ST_DWithin()sempre usei true.

dan2k3k4
fonte

Respostas:

23

Por favor, verifique a seguinte consulta do PostgreSQL para obter dados a uma certa distância. Eu espero que isso ajude.

SELECT *
FROM your_table
WHERE ST_Distance_Sphere(the_geom, ST_MakePoint(your_lon,your_lat)) <= radius_mi * 1609.34
Farhat Abbas
fonte
1
Foi possível fazê-lo funcionar com: SELECT * FROM myTable WHERE GeometryType (ST_Centroid (the_geom)) = 'POINT' AND ST_Distance_Sphere (ST_Point (ST_X (ST_Centroid (the_geom)), ST_Y (ST_Centroid (the_geom))), (ST_MakePoint (6.9333) (46.8167))) <<18 * 1609.34 #
dan2k3k4 11/11
Isso é ótimo :)
Farhat Abbas 11/11
2
Apenas para quem quiser saber, o número 1609,34 é de metros por milha, que é a unidade base que o postgres está usando. Então, para fazer quilómetros, obviamente, multiplicar por 1000.
1mike12
2
Nota pedante: é 1609.344 exatamente (por definição)
barrycarter
6

Parece que você está armazenando sua geometria em uma coluna de geometria, não em uma coluna de geografia.
Tudo bem, mas a função ST_Distance retornará medições em unidades de projeção em vez de sempre em metros. No seu caso (4326), isso será graus.
O simples uso de um buffer com ST_Within também não funcionará, pois o ST_Buffer também será medido em graus.

Você pode converter seus dados para usar a geografia em vez da geometria, ou pode converter seu ponto em alguma projeção que use metros, buffer e depois converta novamente em 4326 para ver o que está dentro:

SELECT
    *
FROM <your data>
WHERE ST_Within(the_geom, 
                ST_Transform(ST_Buffer(ST_Transform(ST_SetSRID(ST_MakePoint(6.9333, 46.8167), 4326), 3857), 30000), 4326)) = 1

Isso projeta o ponto em 3857 , que é uma projeção popular entre os mapas da web. Em seguida, ele o amortece em 30.000 metros e o reprojeta novamente em 4326 antes de passá-lo para ST_Within.

Gênio do mal
fonte
Exceto que um pseudo-Mercator não é confiável para a distância, portanto, a menos que os dados estejam próximos do equador, os resultados serão desativados, especialmente com uma distância de 30 km.
Vince
6

No meu mundo, usando um SRID personalizado (para Google Maps), algo como isto funcionou:

SELECT * FROM addresses WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(longitude, latitude), 3785), radius);

onde o tipo de locationé uma geometria (ponto, 3785) e longitude, latitudee radiussão flutuadores (por exemplo, -100, 44, 30 para 100W / 44N / 30 "unidades" - veja abaixo)

Consulte Qual é a melhor maneira de encontrar todos os objetos no raio de outro objeto? nos documentos do postgis:

A ST_DWithin(geometry, geometry, distance)função é uma maneira prática de realizar uma pesquisa à distância indexada. Ele funciona criando um retângulo de pesquisa grande o suficiente para incluir o raio da distância e, em seguida, executando uma pesquisa de distância exata no subconjunto indexado de resultados.

ATUALIZAÇÃO: as unidades não são milhas para o SRID 3785 ... elas parecem ser radianos ou graus ou algo parecido. Mas a especificação para o meu SRID diz que suas unidades são metros ou graus e é definitivamente nenhuma dessas, pelo menos não sem alguma conversão:

alex=# select * from spatial_ref_sys where srid=3785; srid | auth_name | auth_srid | srtext | proj4text
3785 | EPSG | 3785 | PROJCS["Popular Visualisation CRS / Mercator (deprecated)",GEOGCS["Popular Visualisation CRS",DATUM["Popular_Visualisation_Datum",SPHEROID["Popular Visualisation Sphere",6378137,0,AUTHORITY["EPSG","7059"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6055"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4055"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3785"],AXIS["X",EAST],AXIS["Y",NORTH]] | +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs

AlexChaffee
fonte
Qual é a diferença entre 3785 (no seu post) e 3857?
sintonizou
São projeções diferentes. 3875 vs 3857 - Eu não sei se um é melhor do que o outro
AlexChaffee
1
"O EPSG 3785 foi descontinuado em favor do EPSG 3857 idêntico" - github.com/rgeo/rgeo/pull/61
Yarin
2

Eu acho que isso deve funcionar:

SELECT gid FROM table 
WHERE ST_DWithin(the_geom, ST_SetSRID(ST_Point(6.9333, 46.8167), 4326), 30000)
Francisco Puga
fonte
3
se você converter the_geom na geografia que deve funcionar. st_dwithin (geography (the_geom), geography (<Point, 4326>), 30000)
cavila