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


问题内容

我正在使用Python和Sqlalchemy将纬度和经度值存储在Sqlite数据库中。我已经为Location对象创建了一个混合方法

@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

我究竟做错了什么?


问题答案:

您不能真的那样使用该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方法,对所有的那种聪明的。不幸的是,您也不能使用sqlite做到这一点;它不提供触发函数,
您可以使用PostgreSQL,也可以使用PostgreSQL,或者您可以尝试将以下函数添加到sqlite中:

编辑 它实际上不是要拼命的功能添加到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