【问题标题】:How to check if lat long is in the city limits如何检查经纬度是否在城市范围内
【发布时间】:2014-01-27 20:06:17
【问题描述】:

我如何检查我的纬度/经度是否在城市范围内,例如,大伦敦被包围:

[bbox=-0.489,51.28,0.236,51.686]

来源:

http://wiki.openstreetmap.org/wiki/Bounding_Box

如何检查位置(纬度/经度):

51.55238,0.047032

已经有这方面的宝石了吗?或者最好的方法是什么?

赏金更新:

我有一个可行的解决方案,但我觉得它不是正确的,我使用的是 geocoder gem,我的用户有 geocoded_by 和 lat/long 属性。所以这就是我的做法:

def self.user_from_dublin(user_id)
  near([53.349937,-6.261917], 15).pluck(:id).include?(user_id)
end

因此,这会从都柏林加载所有用户,并将用户的 id 与都柏林的用户进行比较。如果找到该 id,则认为该用户来自都柏林。

我不认为当有更多用户时这会很好用。

更新

我正在使用 postgres 数据库

【问题讨论】:

  • 如果 box.lowx
  • @David 感谢您的回复。你能写一个答案吗?和/或从 bbox 值解释 box.lowx 和 box.lowy?
  • @GandalfStormCrow rubygeocoder.com 你可以使用支持基于地理编码查询的mongodb
  • @bjhaid 我可以在没有 mongodb 的情况下做到这一点,我真的需要 mongo db 吗?我的 postgres 表中已经有了纬度/经度,我需要检查的是边界框中的用户纬度/经度
  • @GandalfStormCrow 我给了geocoder gem主页的链接,并建议使用mongodb作为替代

标签: ruby-on-rails ruby ruby-on-rails-3 postgresql


【解决方案1】:

也许这有点矫枉过正,但如果您使用postgres,您可以安装postgis 扩展来管理空间数据。然后在 irb 中你可以这样做:

result = ActiveRecord::Base.connection.execute('SELECT
ST_Contains(ST_SetSRID(ST_MakeBox2D(ST_Point(-0.489, 51.28), ST_Point(0.236, 51.686)), 4326),
ST_SetSRID(ST_Point(-0.1265, 51.483), 4326))')

查询使用ST_contains function检查点是否在给定的bbox内

这将返回:

=> #<PG::Result:0x007fa517fcbe08 @connection=#<PG::Connection:0x007fa5167f8970 @socket_io=nil, @notice_receiver=nil, @notice_processor=nil>>

那么你可以这样做:

result.first

这将返回:

{"st_contains"=>"t"}

对于这一点 -0.7265, 44.483(bbox 外的一个点),结果将是:

{"st_contains"=>"f"}

如果您不想使用原始 sql,您可以使用 gems 来管理空间数据。一个很好的是:rgeo。推荐阅读创作者的blog

使用rgeo,您可以使用点、多边形等“地理类型”为模型定义属性,然后使用contains?等函数

我留给你一个 gist ,其中包含使用 ubuntu 安装 postgis 的非常基本的说明。

【讨论】:

    【解决方案2】:

    你可以使用原始 sql 吗? &lt;@ 几何运算符将检查point 是否包含在box

    select '51.55238,0.047032'::point <@ '-0.489,51.28,0.236,51.686'::box;
     ?column? 
    ----------
     f
    

    【讨论】:

    • 这将是特定于数据库的。
    • @seand 该问题仅标记为postgresql
    【解决方案3】:

    你不需要任何特别的东西。

    Bounding Box 是一个矩形:

    bbox = left,bottom,right,top
    
      +-----------(10,0)
      |              |
      |              |
      |              |
      |              |
      |              |
    (0,10)-----------+
    

    奇怪的(左下)到(右上)编号是因为,经度是从左到右测量的(为了简化,-180° 是地球的左侧,+180° 是地球的右侧) ,而纬度是从下到上测量的(为了简化,-90° 是地球的底部,+90° 是地球的顶部)。

    所以按照例子,

    (0,10)   = left, bottom = min longitude, min latitude
    (10,10)  = right, top   = max longitude, max latitude
    

    现在你想知道一个点 (5,5) 是否落在它里面。

      +-----------(10,10)
      |              |
      |              |
      |     (5,5)    |
      |              |
      |              |
    (0,0)------------+
    

    所以你所要做的就是在 Postgres(或任何数据库)中为城市创建一个表:

    CREATE TABLE cities(
      city_name      VARCHAR(xxx),
      min_longitude  DECIMAL,
      min_latitude   DECIMAL,
      max_longitude  DECIMAL,
      max_latitude   DECIMAL
    );
    

    现在假设您想找出哪个城市位于latitude=5, longitude=6 的位置,那么只需使用以下查询:

    SELECT city_name 
    FROM cities 
    WHERE (min_latitude  >= 6 AND max_latitude  <= 6)
    AND   (min_longitude >= 5 AND max_longitude <= 5)
    

    用用户的纬度/经度代替5和6,你会得到用户所在的城市。

    编辑:小混淆,小格式

    【讨论】:

    • 我认为您缺少跨越赤道或本初子午线的用例。这将是改用已建立的 gem 的一个很好的理由。
    • 我不明白为什么赤道等会出现问题,因为纬度/经度只是在连续函数上越过 0。但它会在两极上失败。只要您不在格陵兰岛北部的大片土地上进行拳击之类的活动,这似乎是一个不错的简单解决方案。
    • 实际上,您“将”在本初子午线的另一侧出现不连续性。越过这条线,经度 180 变为 -180。
    【解决方案4】:

    如果它是布尔值,为什么不使用where 将结果限制为仅限您的用户。这样,它将只涉及一个 SQL 查询,该查询将为空或大小为 1(您的用户是唯一的记录)

    def self.user_from_dublin(user_id)
      !(near([53.349937,-6.261917], 15).where(id: user_id).empty?)
    end
    

    empty? 将在您的用户不在您的边界内时为真,因此开头的 ! 将否定这一点,当用户不在边界内时您的函数将返回 false

    【讨论】:

      【解决方案5】:

      你可以使用这个宝石 https://github.com/square/border_patrol

      首先,您需要一个小型 KLM,并在您想要的城市范围内

      【讨论】:

        【解决方案6】:

        KISS 解决方案可能会有所帮助,因为您的用户模型具有 latlon 属性。

        如果你有 伦敦的盒子,你可以形成一个activerecord 作用域来实现

        LONDON_BOX = [-0.489,51.28,0.236,51.686]
        scope :inside_london -> {
          where(lat: LONDON_BOX[1]..LONDON_BOX[3]).
          where(lon: LONDON_BOX[0]..LONDON_BOX[2])
        }
        

        从而使您能够使用
        User.inside_london

        请记住,这种类型的双范围查询不会执行得那么好,它不能在 MySQL 中使用适当的索引。对于@david 提供了有用的评论,mongodb 的使用及其地理空间功能将有所帮助。 (但更改数据库是一个严肃的决定)

        【讨论】:

          【解决方案7】:

          BoundBox 是矩形而不是精确的城市边界。 并且在每个查询中计算城市边界效率不高。 我认为您可以在使用地理编码器保存记录之前在回调中保存城市关系。 如果您有更好的解决方案,请分享您的解决方案。

          【讨论】:

            【解决方案8】:

            我做了一些假设,如果它们是真的,请告诉我:

            假设 #1: 该纬度/经度表示用户居住的地方,并且在用户实际搬家时不会经常更新。

            假设#2:这个纬度/经度不代表用户当前位置,会不断更新

            假设 #3:您有所有希望包含在系统中的城市的边界框

            鉴于这些假设,我认为处理此问题的最佳方法是将每个人放入“城市”存储桶中。创建与用户具有多对多关系的城市模型:

            class City < ActiveRecord::Base
              has_many :user_city
              has_many :users, :through => :user_city
            
              attr_accessible :name, :bounding_box #bounding box is an array with 4 values which represent the corners of the city limits.
            end
            
            class User < ActiveRecord::Base
              after_validation :geocode 
              after_create :assign_city
            
              has_many :user_city
              has_many :city, :through => :user_city
            
              attr_accessible :email, :created_at, :password, :updated_at, :latitude, :longitude
            
              def assign_city
                City.all.each do |city|
                  inCity = self.within_bounding_box(city.bounding_box)
                  if inCity
                   UserCity.create(:user=>self,:city=>city)
                   break
                  end
                end
              end
            
            end
            
            class UserCity < ActiveRecord::Base
              belongs_to :user
              belongs_to :city
            
              attr_accessible :user, :city
            end
            

            现在您可以通过以下方式查询城市中的用户:

            User.joins(:city).where({:cities=>{:name=>"London"}})
            

            【讨论】:

              【解决方案9】:

              您并没有完全说...但是如果您想要的是与任何地方的纬度/经度坐标最近的城市的名称,那么您可以使用谷歌地图地理编码器服务 - 设置一个带有 LatLng 的 GeocoderRequest 进行搜索并发出请求。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-05-01
                • 1970-01-01
                • 2012-07-15
                • 2021-09-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多