如何检查lat long是否在城市范围内

时间:2014-01-27 20:06:18

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

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

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

来源:

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

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

51.55238,0.047032

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

Bounty更新:

我有一个可行的解决方案,但我觉得它不是正确的,我正在使用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数据库

9 个答案:

答案 0 :(得分:3)

这可能是一种矫枉过正,但如果您使用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,可以使用gem来管理空间数据。一个非常好的是:rgeo。我建议您阅读创作者的blog

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

我留下gist,其中包含非常基本的说明,可以使用ubuntu安装 postgis

答案 1 :(得分:3)

你可以使用原始的SQL吗? <@几何运算符将检查point

中是否包含box
select '51.55238,0.047032'::point <@ '-0.489,51.28,0.236,51.686'::box;
 ?column? 
----------
 f

答案 2 :(得分: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,你将获得用户所在的城市。

编辑:小混淆,次要格式化

答案 3 :(得分:2)

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

def self.user_from_dublin(user_id)
  !(near([53.349937,-6.261917], 15).where(id: user_id).empty?)
end
当您的用户不在您的边界内时,

empty?将成立,因此开头的!将取消此操作,当用户不在此范围内时,您的函数将返回false边界。

答案 4 :(得分:2)

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

首先,您需要一个具有您想要的城市限制的小荷航

答案 5 :(得分:1)

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

如果你有伦敦的方框,你可以形成一个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及其地理空间能力将有所帮助。 (但数据库更改是一个严肃的决定)

答案 6 :(得分:1)

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

答案 7 :(得分:1)

我做了一些假设,让我知道它们是否属实:

假设#1:此纬度/经度表示用户居住的位置,并且当用户实际搬家时将不经常更新。

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

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

鉴于这些假设,我认为解决这个问题的最佳方法是将每个人置于&#34; City&#34;桶。创建与用户有多对多关系的城市模型:

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"}})

答案 8 :(得分:1)

你没有确切地说......但如果你想要的是最近城市的Lat / Lon坐标的名称,那么你可以使用谷歌地图Geocoder服务 - 设置一个带LatLng的GeocoderRequest来搜索并发出请求。