Полигон "содержит" и другие операции с геометриями не поддерживаются

Я совершенно новичок в postgis и rgeo. Я подозреваю, что, возможно, я поступаю неправильно, но я был немного удивлен, обнаружив, что некоторые операции, в частности, содержат и внутри, невозможны на сферических объектах.

У меня есть куча географически распределенных объектов, которые я хотел бы сгруппировать на основе таких вещей, как почтовый индекс. Для каждой из этих группировок у меня есть граница, и я хотел бы проверить, находится ли объект внутри этой границы, а также проверить, находится ли одна граница внутри другой. Я использую рельсы, и это миграция, используемая для настройки моей модели коллекции

class CreateGeoCollectionDefinition < ActiveRecord::Migration[5.0]
  def change
     create_table :geo_collection_definitions do |t|
       t.string :name
       t.string :geo_place_id
       t.string :geo_place_types, array: true, default: []
       t.st_polygon :boundary, geographic: true
       t.jsonb :boundary_json
       t.st_point :latlng, geographic: true
     end
   end
end

В настоящее время граница исходит от обратного поиска геокодов в Google. Координаты ограничивающего прямоугольника NorthEast и SouthWest передаются в этот метод при создании объекта

GEO_FACTORY = RGeo::Geographic.spherical_factory(srid: 4326)

def self.createBoundary(pointOne, pointTwo)
  point1 = GEO_FACTORY.point(pointOne['lat'], pointOne['lng'])
  point2 = GEO_FACTORY.point(pointTwo['lat'], pointTwo['lng'])
  boundingBox = RGeo::Cartesian::BoundingBox.create_from_points(point1,    point2).to_geometry
  boundingBox
end

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

 # ------------------
 # --- Caused by: ---
 # PG::UndefinedFunction:
 #   ERROR:  function st_contains(geography, geography) does not exist
 #   LINE 1: ...COUNT(*) FROM "geo_collection_definitions" WHERE (ST_Contain...
 #                                                                ^
 #   HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Когда я пытаюсь выполнить запрос типа (я знаю, что это немного глупо)

 GeoCollectionDefinition.where("ST_Contains(boundary, boundary)")

или если я попытаюсь использовать объект RGEO напрямую

it "should be possible to test is points belong in GeoCollection.boundary" do
  factory = RGeo::Geographic.spherical_factory(srid: 4326)
  externalPoint = factory.point(EmptyGeocodeLatLag['lng'], EmptyGeocodeLatLag['lat'])
  expect(someplace_def.boundary.contains?(someplace_def.latlng)).to be_truthy
  expect(someplace_def.boundary.contains?(externalPoint)).to be_falsy
end

я получил

  RGeo::Error::UnsupportedOperation:
   Method Geometry#contains? not defined.

Покопавшись, я обнаружил эту проблему rgeo и другие доказательства того, что эти операции просто не поддерживаются для сферических объектов, основанных на фабрике.

Мне просто интересно;

  1. Это действительно так?
  2. Моделирование границы и расположения объектов так, как описано в моей миграции, кажется, имеет смысл для меня, но я предполагаю, что я не прав?
  3. Как мне узнать, находятся ли несколько точек внутри многоугольника, используя postgis, rgeo и адаптер активной записи?
  4. Можно ли проверить, находится ли один многоугольник внутри другого?

РЕДАКТИРОВАТЬ:

Я выполнил предложение Tilt и выполнил несколько запросов непосредственно на моей базе данных. Во-первых, просто чтобы проверить мои настройки

SELECT PostGIS_full_version();

 NOTICE:  Function postgis_topology_scripts_installed() not found. Is topology support enabled and topology.sql installed?                                  postgis_full_version

 POSTGIS="2.1.7 r13414" GEOS="3.5.0-CAPI-1.9.0 r4084" PROJ="Rel.   4.9.2, 08 September 2015" GDAL="GDAL 1.11.5, released 2016/07/01" LIBXML="2.9.2" LIBJSON="UNKNOWN" RASTER

Я также провел несколько запросов;

  SELECT name  FROM geo_collection_definitions WHERE st_contains(latlng, boundary);
 ERROR:  function st_contains(geography, geography) does not exist
 LINE 1: ...ELECT name  FROM geo_collection_definitions WHERE   st_contain...

Так что думаю, что содержит только не будет работать на географии. Я покопался и нашел st_covers

  SELECT name  FROM geo_collection_definitions WHERE st_covers(boundary, latlng);
  name
  ------
  (0 rows)

 SELECT name  FROM geo_collection_definitions WHERE st_covers(boundary, ST_GeomFromText('POINT(12.9549709 55.5563043)', 4326));
  name
  ------
  (0 rows)

Это действительно удивительно, так как latlng является центральной точкой границы. Я довольно смущен и уверен, что делаю что-то довольно глупое. Любая помощь будет принята с благодарностью

2 ответа

Решение

Я рекомендую использовать ST_DWithin, который хорошо поддерживается для PostGIS' geography тип. Для параметра радиуса вы можете использовать 0 или, возможно, 10 (то есть, если ваши данные имеют точность 10 м).

Нет планов сделать ST_Contains или ST_Within доступными для geography тип.

Ответы Майка и Тилта помогли мне разобраться в этом, и это немного неловко, но на всякий случай любой из этих вещей может помочь кому-то еще в дальнейшем...

ST_D - это, безусловно, способ решить мою конкретную проблему, и было здорово получить от этого информацию. Я изменил некоторые из специальных запросов на такие вещи

  collectionDefs = GeoCollectionDefinition.where("ST_DWithin(boundary, '#{someplace.latlng}', 10)")
  expect(collectionDefs.count).to eq(2)
  collectionDefs = GeoCollectionDefinition.where("ST_DWithin(boundary, 'Point(0.0 0.0)', 10)")
  expect(collectionDefs.count).to eq(0) 

Запрос прошел нормально, но я все еще не получил ожидаемых результатов. Это неловко немного

Следуя совету Тилта, я распечатал многоугольник

POLYGON((55.4965351 12.8894595,55.6445967 12.8894595,55.6445967 13.151087,55.4965351 13.151087,55.4965351 12.8894595)) and the centre point is POINT(13.0108705 55.5790534)

и быстро обнаружил, что координаты в неправильном направлении. Ущерб был нанесен в createBoundary метод я изложил выше. Я создавал точки определения границ и передавал значение lat вместо lng и наоборот. Я обновил код до следующего

 def self.createBoundary(pointOne, pointTwo)
    point1 = GEO_FACTORY.point(pointOne['lng'], pointOne['lat'])
    point2 = GEO_FACTORY.point(pointTwo['lng'], pointTwo['lat'])
    boundingBox =    RGeo::Cartesian::BoundingBox.create_from_points(point1, point2).to_geometry
   boundingBox
 end

Я опущу голову от стыда:)

Спасибо миллион Тилт и Майк. Я не уверен, что нашел бы ST_DWithin и советы по отладке про постгиста определенно спасли меня от многих часов разочарования

Другие вопросы по тегам