可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
If its a boolean why not use where
to limit the results to only your user. This way it will just involve one SQL query that will either be empty or have a size of 1 (your user being the only record)
def self.user_from_dublin(user_id)
!(near([53.349937,-6.261917], 15).where(id: user_id).empty?)
end
empty?
will be true when your user is not within your boundary, so the !
at the beginning will negate this and your function will return false
when the user is not within the boundary.
回答2:
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.
回答3:
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
回答4:
You don't need anything special for this.
Bounding Box is a rectangle:
bbox = left,bottom,right,top
+-----------(10,0)
| |
| |
| |
| |
| |
(0,10)-----------+
The weird (left-bottom) to (top-right) numbering is because, Longitude is measured left-to-right (to simplify, -180° is left of the globe, +180° is right of the globe), whereas Latitude is measured bottom-to-top (to simplify, -90° is bottom of the globe, +90° is top of the globe).
So as per the example,
(0,10) = left, bottom = min longitude, min latitude
(10,10) = right, top = max longitude, max latitude
Now you want to know whether a point (5,5) falls inside it.
+-----------(10,10)
| |
| |
| (5,5) |
| |
| |
(0,0)------------+
So all you have to do is just create a table for cities in Postgres (or any database for that matter):
CREATE TABLE cities(
city_name VARCHAR(xxx),
min_longitude DECIMAL,
min_latitude DECIMAL,
max_longitude DECIMAL,
max_latitude DECIMAL
);
Now suppose you want to find out which city falls under a position latitude=5, longitude=6
, then just use the following query:
SELECT city_name
FROM cities
WHERE (min_latitude >= 6 AND max_latitude <= 6)
AND (min_longitude >= 5 AND max_longitude <= 5)
Substitute 5 and 6 with the user's latitude/longitude, and you will get the city where the user falls under.
EDIT: Small mix-up, minor formatting
回答5:
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
回答6:
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:
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.
回答8:
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"}})
回答9:
You don't exactly say... but if what you want is the name of the nearest city to a Lat/Lon coordinate anywhere, then you could use google maps Geocoder services - set up a GeocoderRequest with a LatLng to search and issue a request.