Python - SqlAlchemy:如何按大圆距离过滤查询?
我正在使用Python和Sqlalchemy把经纬度值存储到Sqlite数据库里。我为我的位置对象创建了一个混合方法。
@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_lat
和sin_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_lng
和other.rad_lng
的值时,我得到了,比如说,
self.rad_lng: Location.rad_lng
other.rad_lng: -1.29154947064
我到底哪里做错了呢?
3 个回答
0
看起来你做的都没错,但不知道为什么这个方法并没有真正被“混合”。你有没有可能犯了什么低级错误,比如在你的源代码里根本没有真正加上装饰器?
0
显然,你无法从那个字符串中得到一个浮点数。
这是因为你使用了“self”,它作为调用的第一个参数,表示这个方法是对象的一部分,而不是你可以传递的某个变量。
你应该试试这个:
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”部分。
7
你不能这样使用 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
你需要在 @great_circle_distance.expression
方法 中用 sqalchemy.func.*
替代 math.*
来实现这些聪明的功能。不过,不幸的是,sqlite 也不支持这个;它 不提供三角函数。你可以使用 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