2011-09-29 106 views
0

我正在使用Python和Sqlalchemy將經度和緯度值存儲在Sqlite數據庫中。我創建了一個hybrid method爲我的地址對象,Python - SqlAlchemy:按大圓距離過濾查詢?

@hybrid_method 
def great_circle_distance(self, other): 
    """ 
    Tries to calculate the great circle distance between the two locations 

    If it succeeds, it will return the great-circle distance 
    multiplied by 3959, which calculates the distance in miles. 

    If it cannot, it will return None. 

    """ 
    return math.acos( self.cos_rad_lat 
        * other.cos_rad_lat 
        * math.cos(self.rad_lng - other.rad_lng) 
        + self.sin_rad_lat 
        * other.sin_rad_lat 
        ) * 3959 

所有值一樣cos_rad_latsin_rad_lat是我預先計算優化的計算值。總之,當我運行下面的查詢,

pq = Session.query(model.Location).filter(model.Location.great_circle_distance(loc) < 10) 

我碰到下面的錯誤,

line 809, in great_circle_distance 
    * math.cos(self.rad_lng - other.rad_lng) 
TypeError: a float is required 

當我打印值self.rad_lngother.rad_lng我得到的,例如,

self.rad_lng: Location.rad_lng 
other.rad_lng: -1.29154947064 

我在做什麼錯?

+1

這不是haversine公式,它是餘弦公式的球形法律。請參閱(例如)http://www.movable-type.co.uk/scripts/latlong.html –

+0

我們可以看到一個更完整的「Location」類示例嗎?特別是,每個屬性如何成爲現實。他們是否是階級屬性? – SingleNegationElimination

+0

哎呀,你是對的。不是馬丁,只是大圓距離。 – john

回答

5

你真的不能使用math模塊方式:

>>> c = toyschema.Contact() 
>>> c.lat = 10 
>>> c.lat 
10 
>>> import math 
>>> math.cos(c.lat) 
-0.83907152907645244 
>>> math.cos(toyschema.Contact.lat) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: a float is required 

你必須代替math.*@great_circle_distance.expression method結合sqalchemy.func.*所有的那種聰明的。不幸的是,你不能用sqlite來做到這一點;它doesn't provide trig functions 你可以使用PostgreSQL,它不,或者你可以嘗試add these functions to sqlite yourself:

編輯它實際上不是要拼命的功能添加到sqlite的:這是測試。

必須將數學函數添加到sqlite的:

engine = sqlalchemy.create_engine("sqlite:///:memory:/") 
raw_con = engine.raw_connection() 
raw_con.create_function("cos", 1, math.cos) 
raw_con.create_function("acos", 1, math.acos) 

class Location(...): 
    ... 
    @hybrid_method 
    def great_circle_distance(self, other): 
     """ 
     Tries to calculate the great circle distance between 
     the two locations by using the Haversine formula. 

     If it succeeds, it will return the Haversine formula 
     multiplied by 3959, which calculates the distance in miles. 

     If it cannot, it will return None. 

     """ 
     return math.acos( self.cos_rad_lat 
         * other.cos_rad_lat 
         * math.cos(self.rad_lng - other.rad_lng) 
         + self.sin_rad_lat 
         * other.sin_rad_lat 
         ) * 3959 

    @great_circle_distance.expression 
    def great_circle_distance(cls, other): 
     return sqlalchemy.func.acos( cls.cos_rad_lat 
         * other.cos_rad_lat 
         * sqlalchemy.func.cos(cls.rad_lng - other.rad_lng) 
         + cls.sin_rad_lat 
         * other.sin_rad_lat 
         ) * 3959 
+0

謝謝!我有一種感覺,它與該做的事。我會嘗試這個今晚稍後,讓你知道!再次 – john

+0

完美!謝謝:) – john

+0

此外,還有一兩件事,我就得到了當SQLite數據庫是在內存中運行,但對於一個這已經創造了我似乎無法添加在你所描述的方式的功能。我如何添加功能已經被創建SQLite數據庫? – john

0

顯然,你不能從該字符串獲得浮點數。

這是因爲你正在使用「self」,它作爲調用的第一個參數,表示該方法是對象的一部分,而不是你可能傳遞的某個var。

你應該試試這個:

def great_circle_distance(self, first, other): 
    """ 
    Tries to calculate the great circle distance between 
    the two locations by using the Haversine formula. 

    If it succeeds, it will return the Haversine formula 
    multiplied by 3959, which calculates the distance in miles. 

    If it cannot, it will return None. 

    """ 
    return math.acos( self.cos_rad_lat 
        * other.cos_rad_lat 
        * math.cos(first.rad_lng - other.rad_lng) 
        + self.sin_rad_lat 
        * other.sin_rad_lat 
        ) * 3959 

我在這裏想上面說的全局變量「self.cos_rad_lat」和「self.sin_rad_lat」 用在你的程序正確的值在其他地方發起的,可能是在同一對象的「init」部分。

+1

這不考慮SQLAlchemy的「混合」裝飾器如何工作。 – Dave

0

它確實看起來像你所做的一切正確,但不知何故該方法實際上並沒有得到'雜交'。你可以做一些愚蠢的事情,如實際上把你的源代碼中的裝飾器?

+0

我用這個導入,「從sqlalchemy.ext。混合動力進口hybrid_property,hybrid_method」 – john