我对postgis和rgeo很新。我怀疑我可能会以错误的方式处理事情,但我发现一些操作,特别是包含& amp;在球形物体内部,不可能。
我有一堆地理上分布的对象,我想基于邮政编码之类的东西将它们组合在一起。对于这些分组中的每一个,我都有一个边界,我想检查一个对象是否在该边界内,并检查一个边界是否在另一个边界内。我使用rails,这是用于设置我的收藏模型的迁移
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
目前,边界来自谷歌反向地理编码查找。 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 issue以及其他证据表明基于球形工厂的物体不支持这些操作
我只是想知道;
编辑:
我跟进了倾斜的建议并直接在我的数据库上运行了一些查询。首先,只是为了验证我的设置
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是边界的中心点。我很困惑,确定我做了一件非常愚蠢的事情。任何帮助将不胜感激
答案 0 :(得分:2)
我建议使用ST_DWithin,这对PostGIS&#39;提供了很好的支持。 geography
类型。对于radius参数,您可以使用0或10(即如果您的数据精度为10 m)。
没有任何计划可以为geography
类型提供ST_Contains或ST_Within。
答案 1 :(得分:1)
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和Mike。我不确定我是否会找到ST_DWithin并且专业的postgist调试技巧确实为我节省了数小时的挫折感