Python - SqlAlchemy: Filter query by great circle distance?

后端 未结 3 1901
醉酒成梦
醉酒成梦 2021-01-07 08:24

I am using Python and Sqlalchemy to store latitude and longitude values in a Sqlite database. I have created a hybrid method for my Location object,

@hybrid_         


        
3条回答
  •  萌比男神i
    2021-01-07 08:38

    You can't really use the math module that way:

    >>> 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 "", line 1, in 
    TypeError: a float is required
    

    You'll have combine sqalchemy.func.* in place of math.* in a @great_circle_distance.expression method for all of that kind of cleverness. Unfortunately, you can't do that with sqlite, either; it doesn't provide trig functions You could use PostgreSQL, which does, or you can try to add these functions to sqlite yourself:

    EDIT It's actually not to hard to add functions to sqlite: This is NOT tested.

    Have to add the math functions to 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
    

提交回复
热议问题