2014-01-27 82 views
3

我如何檢查我的緯度/長在城市範圍或例如,大倫敦被封閉:如何檢查是否Lat Long網是在市區範圍

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

來源:

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

我如何檢查是否一個位置(經/緯度):

51.55238,0.047032 

這有沒有寶石?或者什麼是最好的方式來做到這一點?

賞金更新:

我有一個有效的解決方案,但我覺得它不是正確的,我使用的地理編碼器的寶石,和我的用戶有geocoded_by和緯度/經度屬性。因此,這裏是我如何做到這一點:

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

所以這個加載來自都柏林的所有用戶和用戶的ID進行比較,以在都柏林的那些用戶。如果找到該用戶,則認爲該用戶來自都柏林。

我不認爲這會適用於有更多的用戶。

更新

我使用Postgres數據庫

+0

if box.lowx <= x <= box.highx and box.lowy <= y <= box.highy? – David

+0

@David感謝您的回覆。你能寫一個答案嗎?和/或從bbox值解釋box.lowx和box.lowy? –

+0

@GandalfStormCrow http://www.rubygeocoder.com/,你可以使用它支持基於地理編碼查詢的mongodb – bjhaid

回答

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

1

A 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及其地理空間能力將有所幫助。 (但數據庫更改是一個嚴重的決定)

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你可以使用寶石來管理空間數據。一個很好的是:rgeo。我建議閱讀創作者的blog

使用rgeo你可以定義你的模型屬性與「geotypes」,如點,多邊形等,然後使用功能,如contains?

我留給你一個gist非常基本的說明安裝postgis與Ubuntu的。

1

BoundBox是矩形的,而不是一個確切的城市邊界。 在每個查詢中計算城市邊界效率不高。 我認爲你可以在使用地理編碼器保存記錄之前在回調中保存城市關係。 如果您有更好的解決方案,請分享您的解決方案。

3

你可以用raw sql嗎?該<@幾何運算符將檢查point包含在box

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

這將是數據庫特定的。 – seand

+1

@seand問題僅標記爲'postgresql'。 –

3

你不需要任何特別的東西。

包圍盒是一個矩形:

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與用戶的緯度/經度,您將獲得該用戶屬於城下。

編輯:小混淆,少量格式

+0

我認爲你錯過了穿過赤道或主子午線的用例。這將是使用已建立的寶石代替的一個很好的理由。 – eabraham

+1

我不明白爲什麼在赤道上會出現問題,因爲lat/longt只是在連續函數上穿過0。它會在兩極上失敗。只要你不在北格陵蘭島的大片土地上進行拳擊,這似乎是一個體面的簡單解決方案。 – seand

+1

其實你會「會」在主要子午線的對面有不連續性。越過線和經度180變成-180。 – seand

1

我做了一些假設,讓我知道,如果他們是真正的:

假設1:這個緯度/長表示其中一個用戶居住並且在用戶身體移動他的家時將不經常更新。

假設2:這LAT /長並不代表用戶的當前位置,並且將不斷更新

假設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"}}) 
1

你並不確切地說......但如果你想要的是離任何地方最近的城市的緯度/經度座標的名稱,那麼你可以使用谷歌地圖地理編碼器服務 - 設置與LatLng GeocoderRequest搜索和發出請求。

相關問題