Skip to content

Commit

Permalink
Filter accommodations around a given center point and a radius.
Browse files Browse the repository at this point in the history
  • Loading branch information
mlvernay committed Dec 23, 2024
1 parent 786d514 commit 8701029
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
14 changes: 13 additions & 1 deletion accommodation/filters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.gis.geos import Polygon
from django.contrib.gis.geos import Point, Polygon
from django.contrib.gis.measure import Distance
from django_filters.rest_framework import FilterSet, filters
from rest_framework.exceptions import ValidationError

Expand All @@ -8,6 +9,7 @@
class AccommodationFilter(FilterSet):
bbox = filters.CharFilter(method="filter_bbox", label="Bounding box")
is_accessible = filters.BooleanFilter(method="filter_is_accessible", label="Only accessible accommodations")
center = filters.CharFilter(method="filter_center", label="Center point for radius filtering (lon,lat)")

def filter_bbox(self, queryset, name, value):
try:
Expand All @@ -25,6 +27,16 @@ def filter_is_accessible(self, queryset, name, value):
return queryset.filter(nb_accessible_apartments__gt=0)
return queryset

def filter_center(self, queryset, name, value):
try:
lon, lat = map(float, value.split(","))
point = Point(lon, lat)
radius = float(self.data.get("radius") or 10)
distance = Distance(km=radius)
return queryset.filter(geom__distance_lte=(point, distance))
except (ValueError, TypeError):
raise ValidationError("Invalid center format. Should be 'longitude,latitude'. Radius must be a number.")

class Meta:
model = Accommodation
fields = []
12 changes: 12 additions & 0 deletions accommodation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ class AccommodationDetailView(generics.RetrieveAPIView):
description="Filter to return only accommodations with accessible apartments (nb_accessible_apartments > 0).",
required=False,
),
OpenApiParameter(
"center",
OpenApiTypes.STR,
description="Center point for radius filtering. Format: longitude,latitude.",
required=False,
),
OpenApiParameter(
"radius",
OpenApiTypes.NUMBER,
description="Radius in kilometers for filtering accommodations around the center point.",
required=False,
),
],
responses=AccommodationGeoSerializer,
)
Expand Down
34 changes: 34 additions & 0 deletions tests/accommodation/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,37 @@ def test_accommodation_list_view_filters(self):
returned_ids = [feature["id"] for feature in results["results"]["features"]]
assert self.accommodation_nantes_accessible.id in returned_ids
assert self.accommodation_nantes_non_accessible.id not in returned_ids

def test_accommodation_list_center_radius(self):
center = "-1.5536,47.2184" # Nantes (near the accessible accommodation)

response = self.client.get(reverse("accommodation-list"), {"center": center, "radius": 0.2})
results = response.json()

assert len(results["results"]["features"]) == 1

returned_ids = [feature["id"] for feature in results["results"]["features"]]
assert self.accommodation_nantes_accessible.id in returned_ids
assert self.accommodation_nantes_non_accessible.id not in returned_ids

response = self.client.get(reverse("accommodation-list"), {"center": center, "radius": 2})
results = response.json()

assert len(results["results"]["features"]) == 2

returned_ids = [feature["id"] for feature in results["results"]["features"]]
assert self.accommodation_nantes_accessible.id in returned_ids
assert self.accommodation_nantes_non_accessible.id in returned_ids

center_paris = "2.35,48.85" # Paris

response = self.client.get(reverse("accommodation-list"), {"center": center_paris, "radius": 10})
results = response.json()

assert len(results["results"]["features"]) == 1

returned_ids = [feature["id"] for feature in results["results"]["features"]]
assert self.accommodation_paris.id in returned_ids
assert self.accommodation_lyon.id not in returned_ids
assert self.accommodation_nantes_accessible.id not in returned_ids
assert self.accommodation_nantes_non_accessible.id not in returned_ids

0 comments on commit 8701029

Please sign in to comment.