【问题标题】:polygon 'contains' and other operations on geometries not supported不支持对几何的多边形“包含”和其他操作
【发布时间】:2023-03-27 00:55:01
【问题描述】:

我对 postgis 和 rgeo 还是很陌生。我怀疑我可能以错误的方式处理事情,但我有点惊讶地发现一些操作,特别是包含和内部,在基于球形的对象上是不可能的。

我有一堆地理分布的对象,我想根据诸如邮政编码之类的东西将它们组合在一起。对于这些分组中的每一个,我都有一个边界,我想检查一个对象是否在该边界内,并检查一个边界是否在另一个边界内。我正在使用 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 和其他证据表明这些操作不支持基于球形工厂的对象

我只是想知道;

  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...

因此,guess contains 对地理位置不起作用。我挖了一圈,发现了 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 是边界的中心点。我很困惑,并且确定我在做一些很愚蠢的事情。任何帮助将不胜感激

【问题讨论】:

  • 由于这个问题似乎是纯粹基于 postgis 的,如果您在 SQL 语句而不是 rails 的帮助下重新表述您的问题可能会更好。根据我的经验,在将查询连接到框架之前,最好在纯 SQL 中测试查询,这样可以更好地控制。
  • 嗨倾斜!感谢您的回复一百万。我敢肯定这只是我在做的傻事。我将考虑确保我安装了 proj4。然后我会尝试直接在 postgis db 上运行一些查询来测试东西
  • 你能给出那个边界的 WKT 输出吗? (ST_AsText(边界))。另外,您确定要继续使用地理吗?如果您使用大规模(超出国界)数据是有道理的,但通常最好在本地投影中工作。我很惊讶您的邮政编码无论如何都在地理上,通常它们会被投影。
  • 没问题,倾斜。以下是边界多边形的多边形((55.4965351 12.889595,555.645967 13.151087,55.4965351 13.151087,55.49653511351.4965351135112.8894595))和中心点是点(13.0108705 55.5790534) SPAN>
  • 啊!!多边形坐标反转!谢谢倾斜!

标签: ruby-on-rails activerecord postgis rgeo


【解决方案1】:

我建议使用ST_DWithin,它很好地支持 PostGIS 的geography 类型。对于半径参数,您可以使用 0 或 10(即,如果您的数据的精度为 10 m)。

没有任何计划使ST_ContainsST_Within 可用于geography 类型。

【讨论】:

    【解决方案2】:

    Mike 和 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 和 Mike。我不确定我是否会找到 ST_DWithin,而专业的 postgist 调试技巧无疑为我节省了很多小时的挫败感

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-20
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      相关资源
      最近更新 更多