Roman
Roman

Reputation: 3941

Python Flask translating calculation for radius filter based on latitude & longitude to SQLalchemy

I am currently trying to implement a calculation for a radius:

I have a DB with rooms, every single room has a different adress and also a latiude and longitude.

A user can enter a cityname in a searchform to search for rooms in this city. Here I want to show a result page with rooms in the searched city + all rooms in other cities in a 60 km radius from the searched city.

Here I found some solutions which I would like to try, but I cant figure out how to translate this into an SQLalchemy query:

SELECT * FROM Places WHERE acos(sin(1.3963) * sin(Lat) + cos(1.3963) * cos(Lat) * cos(Lon - (-0.6981))) * 6371 <= 60;

So theoretically I have to replace 1.3963 and -0.6981 with the latitude and longitude of the searched city and I will get what I want.

Upvotes: 3

Views: 1254

Answers (3)

moshfiqur
moshfiqur

Reputation: 2139

Though @Roman's answer above give me the basic idea how to achieve this, however, his solution didn't work for me. So, here is my solution:

Location.query.filter(
    (func.degrees(
        func.acos(
            func.sin(func.radians(lat)) * func.sin(func.radians(Location.latitude)) + 
            func.cos(func.radians(lat)) * func.cos(func.radians(Location.latitude)) * 
            func.cos(func.radians(lon-Location.longitude))
        )
    ) * 60 * 1.1515 * 1.609344) <= distance)

Here lat and lon is the given coords from where I was calculating the distance. The distance was limited by given distance in the query. This query is calculating the distance in km, if you want to calculate in mile, remove the * 1.609344 from the query.

Upvotes: 0

Roman
Roman

Reputation: 3941

Here is the solution in sqlalchemy:

loc is the location which someone searches for. For example a user entering a cityname.

Zimmer is my table, where all DB entries are, every Zimmer has a lat and lng, these are used to calculate the radius of the input loc. To limit the radius we use <= 100 (for 100km).

all_rooms_in_city = Zimmer.query.filter(func.acos(func.sin(func.radians(loc_latitude)) * func.sin(func.radians(Zimmer.zimmer_lat)) + func.cos(func.radians(loc_latitude)) * func.cos(func.radians(Zimmer.zimmer_lat)) * func.cos(func.radians(Zimmer.zimmer_lng) - (func.radians(loc_longitude)))) * 6371 <= 100)

Ordering is also possible by simply putting the same code in an order_by() and leave out the limiting <= 100 radius:

.order_by(asc(func.acos(func.sin(func.radians(loc_latitude)) * func.sin(func.radians(Zimmer.zimmer_lat)) + func.cos(func.radians(loc_latitude)) * func.cos(func.radians(Zimmer.zimmer_lat)) * func.cos(func.radians(Zimmer.zimmer_lng) - (func.radians(loc_longitude)))) * 6371))

Dont forget here to import func from sqlalchemy, because otherwise the math methods wont work in the query. If you have saved your lat and lng in a Radians Column in your database you dont need the typecast. I used func.radians() because my lat and lng where saved in a Float Column

Upvotes: 4

BaL
BaL

Reputation: 93

you can use SQL alchemy's from_statement(statement) method, as described here :

http://docs.sqlalchemy.org/en/latest/orm/query.html

With this, you do not need to translate this to SQL Alchemy "syntax", the QueryAPI will do it for you.

So you could do something like:

q = session.query(PlacesClass).from_statement(
    'SELECT * FROM Places WHERE acos(sin(1.3963) * sin(Lat) + '\
    'cos(1.3963) * cos(Lat) * cos(Lon - (-0.6981))) * 6371 <= 60;')

Upvotes: 0

Related Questions