Python - SqlAlchemy:按大圆距离过滤查询?

时间:2011-09-29 09:14:43

标签: python database sqlite sqlalchemy pylons

我正在使用Python和Sqlalchemy在Sqlite数据库中存储纬度和经度值。我为我的Location对象创建了hybrid method

@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

我做错了什么?

3 个答案:

答案 0 :(得分: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

您将sqalchemy.func.*组合math.*代替@great_circle_distance.expression method,以获得所有这种聪明才智。不幸的是,你不能用sqlite做到这一点;它doesn't provide trig functions 您可以使用PostgreSQL,或者您可以尝试add these functions to sqlite yourself:

编辑实际上并不难为sqlite添加功能:这是 NOT 测试。

必须将数学函数添加到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

答案 1 :(得分: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”部分。

答案 2 :(得分:0)

看起来你已经完成了所有事情,但不知何故,这种方法实际上并没有“杂交”。你有没有做过蠢事,比如实际将装饰器放在你的源代码中?