2016-09-24 9 views
2

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

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

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. 

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

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

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

EDIT:

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

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 является центральной точкой границы. Я довольно смущен и уверен, что делаю что-то довольно глупое. Любая помощь была бы принята с благодарностью

+1

Поскольку проблема, по-видимому, является чистой почтой, возможно, было бы лучше, если бы вы перефразировали свой вопрос с помощью вместо SQL-релей. По моему опыту, всегда лучше тестировать свои запросы в чистом SQL, прежде чем подключать их к структуре, дает вам гораздо лучший контроль. – tilt

+0

Привет наклон! Спасибо миллион за ваш ответ. Я уверен, что это просто глупо, что я делаю. Я собираюсь изучить, что у меня установлен proj4. Затем я попытаюсь запустить некоторые запросы непосредственно на postgis db, чтобы проверить что-то. – Conor

+1

Можете ли вы дать вывод WKT этой границы? (ST_AsText (граница)). Кроме того, вы уверены, что хотите продолжать использовать географию? Это имеет смысл, если вы работаете с крупномасштабными (за пределами национальных границ) данными, но, как правило, лучше работать в локальной проекции. Я удивлен, что ваши почтовые индексы в любом случае находятся в географии, обычно они будут проецироваться. – tilt

ответ

2

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

Планируется никаких ST_Contains или ST_Within для geography.

1

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

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

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) 

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

Следуя совету Tilt, я распечатал полигон

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 

повешу мою голову от стыда :)

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