Skip to content

Instantly share code, notes, and snippets.

@Safrone
Created January 26, 2022 21:29
Show Gist options
  • Save Safrone/d03d6173296a9bd4642364232070ed3d to your computer and use it in GitHub Desktop.
Save Safrone/d03d6173296a9bd4642364232070ed3d to your computer and use it in GitHub Desktop.
Django ordering by Haversine Distance for model with latitude and longitude
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