Created
January 26, 2022 21:29
-
-
Save Safrone/d03d6173296a9bd4642364232070ed3d to your computer and use it in GitHub Desktop.
Django ordering by Haversine Distance for model with latitude and longitude
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db.models import CASCADE, SET_DEFAULT, SET_NULL, PROTECT, QuerySet, Q | |
from django.db.models.expressions import F | |
from django.db.models.functions import Cos, Sqrt, ASin | |
class ExampleModel(models.Model): | |
# Example model that could use this function | |
latitude = models.FloatField() | |
longitude = models.FloatField() | |
def order_closest(queryset: QuerySet, latitude: float, longitude: float) -> QuerySet: | |
"""Annotates the queryset with distance from the given latitude and longitude and orders by that field. Distance is in kilometers""" | |
# Adapted for Django from https://stackoverflow.com/a/41337005/5314541 | |
degree_to_radian = 0.017453292519943295 # 2*pi/360 | |
earth_diameter = 12742 # Diameter of the earth in kM, use 7912 (earth diameter in miles) to have the calculated distances be in miles | |
# convert latitude/longitude to radians | |
lat1 = latitude * degree_to_radian | |
lon1 = longitude * degree_to_radian | |
lat2 = F('latitude') * degree_to_radian | |
lon2 = F('longitude') * degree_to_radian | |
haversine_function = 0.5 - Cos(lat2 - lat1)/2 + Cos(lat1)*Cos(lat2) * (1 - Cos(lon2 - lon1) / 2) | |
distance_function = earth_diameter * ASin(Sqrt(haversine_function)) | |
return queryset.annotate(haversine_distance=distance_function).order_by('haversine_distance') | |
if __name__ == '__main__': | |
order_closestExampleMode.objects.all() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment