How to check if lat long is in the city limits

2020-06-25 04:46发布

How can I check if my lat/long is in the city limits or example, Greater London is enclosed by:

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

Source :

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

How can I check if a location (lat/lon):

51.55238,0.047032

Is there a gem already for this? Or what would be the best way to do this?

Bounty update:

I have a working solution but I feel its not the right one, I'm using geocoder gem, and my user has geocoded_by, and lat/long attributes. So here is how I do it :

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

So this loads all users from Dublin and compares the id of the user to those users in Dublin. If the id is found the user is considered to be from Dublin.

I don't think this will work well when there is more users.

Update

I'm using postgres database

9条回答
Animai°情兽
2楼-- · 2020-06-25 04:55

Can you use raw sql? The <@ geometric operator will check if the point is contained in the box

select '51.55238,0.047032'::point <@ '-0.489,51.28,0.236,51.686'::box;
 ?column? 
----------
 f
查看更多
太酷不给撩
3楼-- · 2020-06-25 04:58

Maybe this is an overkill, but if you are using postgres you can install postgis extension to manage spatial data. Then in a irb you can do something like this:

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))')

The query is checking if the point is inside the given bbox using the ST_contains function

This will return:

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

Then you can do:

result.first

This will return:

{"st_contains"=>"t"}

With this point -0.7265, 44.483 (a point outside the bbox) the result will be:

{"st_contains"=>"f"}

If you don't want to use raw sql you can use gems to manage spatial data. A very good one is: rgeo. I recommend to read the creator's blog

Using rgeo you can define attributes for your models with "geotypes" like points, polygons, etc. and then use functions like contains?

I leave you with a gist with very basic instructions to install postgis with ubuntu.

查看更多
看我几分像从前
4楼-- · 2020-06-25 05:01

BoundBox is rectangle and not an exact city boundary. And it's not efficient to calculate city boundary in each query. I think you can save city relation in callback before save record using geocoder. Please share your solution if you have better one.

查看更多
Viruses.
5楼-- · 2020-06-25 05:07

I've made a few assumptions, let me know if they are true:

Assumption #1: This lat/long represents where a user lives and will be updated infrequently when a user physically moves his home.

Assumption #2: This lat/long does not represent a users current position and will be updated constantly

Assumption #3: You have bounding boxes for all of the cities you wish to include in the system

Given these assumptions, I think the best way to handle this is to place each person into "City" buckets. Create a city model with a many to many relationship with user:

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

Now you can query users in a city with:

User.joins(:city).where({:cities=>{:name=>"London"}})
查看更多
Evening l夕情丶
6楼-- · 2020-06-25 05:12

A KISS solution might be helpful since you have lat and lon attributes to the user model.

If you have London's box you could form an activerecord scope which would implement

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

thus enabling you to use
User.inside_london

Keep in mind that this type of double range query will not perform that good, it cannot use a proper index in MySQL. For that @david has provided a useful comment, the use of mongodb and its geospatial abilities will help. (But a DB change is a serious decision)

查看更多
爱情/是我丢掉的垃圾
7楼-- · 2020-06-25 05:17

You can use this Gem https://github.com/square/border_patrol

First you need a small KLM with the limits of the city you want

查看更多
登录 后发表回答