Вернуть все результаты в радиусе 30 км от определенной широты / долготы?

21

У меня есть таблица со столбцом, the_geomкоторый содержит данные, подобные:

0103000020E61000000100000005000000CE473AACFA071E40F27FB23340744740336FE841C6231E40873BED903F744740FC150A0ACE231E40D19E2684637647409C9B443D00081E409A9AF82664764740CE473AACFA071E40F27FB23340744740

Который при применении ST_AsEWKT(the_geom)возвращает функцию :

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

Мне нужно выбрать все данные, которые находятся в радиусе 30 км от конкретной точки широты / долготы, например:

  • широта = 46,8167
  • lng = 6,9333

Однако всякий раз, когда я пытался использовать ST_Distance(), я всегда получал значения меньше 1, а использование ST_DWithin()всегда возвращало true.

dan2k3k4
источник

Ответы:

23

Пожалуйста, проверьте следующий запрос для PostgreSQL, чтобы получить данные на определенном расстоянии. Надеюсь, это поможет.

SELECT *
FROM your_table
WHERE ST_Distance_Sphere(the_geom, ST_MakePoint(your_lon,your_lat)) <= radius_mi * 1609.34
Фархат Аббас
источник
1
Удалось заставить его работать с: SELECT * FROM myTable WHERE GeometryType (ST_Centroid (the_geom)) = 'POINT' и ST_Distance_Sphere (ST_Point (ST_X (ST_Centroid (the_geom))), ST_Y (ST_Centroid (the_geom))) (ST_Make) , 46.8167))) <= 18 *
1609.34
Это здорово :)
Фархат Аббас
2
Просто для всех, кто интересуется, цифра 1609,34 - это метры на милю, которую использует базовый блок postgres. Таким образом, чтобы сделать километры, очевидно, умножьте на 1000.
1mike12
2
Педантическое примечание: это 1609.344 точно (по определению)
баррикартер
6

Похоже, вы сохраняете свою геометрию в столбце геометрии, а не в столбце географии.
Это нормально, но функция ST_Distance будет возвращать измерения в единицах проекции, а не всегда в метрах. В вашем случае (4326) это будут градусы.
Простое использование буфера с ST_Within также не сработает, так как ST_Buffer будет измеряться и градусами.

Вы можете либо преобразовать свои данные, чтобы использовать географию вместо геометрии, либо вы можете преобразовать свою точку в некоторую проекцию, которая использует метры, буфер, а затем преобразовать обратно в 4326, чтобы увидеть, что внутри:

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

Это проецирует точку в 3857 , которая является популярной среди веб-карт. Затем он буферизует его на 30 000 метров, затем перепроектирует обратно на 4326, а затем передает его ST_Within.

Злой гений
источник
За исключением того, что псевдомеркатор ненадежен для расстояния, поэтому, если данные не близки к экватору, результаты будут отклонены, особенно на расстоянии 30 км.
Винс
6

В моем мире использование собственного SRID (для Google Maps) работало примерно так:

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

где тип locationявляется геометрией (Point, 3785), и longitude, latitudeи radiusявляются поплавками (например, -100, 44, 30 для 100W / 44N / 30 "единиц" - см. ниже)

См. Каков наилучший способ найти все объекты в радиусе другого объекта? в документах postgis:

Эта ST_DWithin(geometry, geometry, distance)функция - удобный способ поиска по индексируемому расстоянию. Он работает, создавая прямоугольник поиска, достаточно большой, чтобы ограничить радиус расстояния, и затем выполнить точный поиск расстояния по индексируемому подмножеству результатов.

ОБНОВЛЕНИЕ: единицы измерения - это не мили для SRID 3785 ... они кажутся либо радианами, либо градусами, либо чем-то в этом роде. Но спецификация для моего SRID говорит, что его единицы измерения - метры или градусы, и это определенно ни один из них, по крайней мере, без некоторого преобразования:

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
источник
В чем разница между 3785 (в вашем посте) и 3857?
настроено
Это разные проекции. 3875 против 3857 - Я не знаю, лучше ли один, чем другой
AlexChaffee
1
«EPSG 3785 устарела в пользу аналогичного EPSG 3857» - github.com/rgeo/rgeo/pull/61
Ярин
2

Я думаю, что это должно работать:

SELECT gid FROM table 
WHERE ST_DWithin(the_geom, ST_SetSRID(ST_Point(6.9333, 46.8167), 4326), 30000)
Франциско Пуга
источник
3
если вы приведете the_geom к географии, это должно сработать. st_dwithin (география (the_geom), география (<Point, 4326>), 30000)
кавила