4

I'm currently trying to get a list of points contained within a radius but I can't get it working. Here is the code of my view so far:

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance

class AreaInfoViewSet(viewsets.ViewSet):
    queryset = models.AreaInfoRequest.objects.all()
    serializer_class = serializers.AreaInfoRequestRequestSerializer

    def list(self, request):
        center_point = 'POINT(48.80033 2.49175)'
        radius = "50.0"

        data = {"center_point": center_point, "radius": radius, "source_ip": utils.get_client_ip(request)}
        serializer = serializers.AreaInfoRequestRequestSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        # Contains an object with field "from_location"="SRID=4326;POINT (48.80029 2.49157)"
        objs = models.PointsResult.objects.all()

        float_radius = serializer.data["radius"]
        center_point = serializer.data["center_point"] # Point object

        res = models.PointsResult.objects.filter(from_location__distance_lte=(
            center_point, Distance({"meter": float_radius})))
        # Here the res doesn't contain the unique object in the db even if it's within the radius

        return Response(res)

Any idea why it's not working? Thank you

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
E-Kami
  • 2,529
  • 5
  • 30
  • 50

2 Answers2

2

I can see two issues here:

  1. You haven't specified the SRID on your center point, which means you're comparing two different SRIDs. You need to set the SRID on center_point:

    center_point = 'SRID=4326;POINT(48.80033 2.49175)'
    
  2. Your inline comment says POINT (2.49157 48.80029), but your code uses POINT(48.80033 2.49175) - note that the latitude and longitude have swapped places. I don't know which of these you intend to use, but they refer to quite different locations.

solarissmoke
  • 30,039
  • 14
  • 71
  • 73
  • Sorry I swapped the two after an experiment. The actual one is actually not swapped. I edited my question and fixed this issue. – E-Kami Jul 22 '19 at 04:13
  • Even using `center_point = 'SRID=4326;POINT(48.80033 2.49175)'` doesn't work and I believe by default `SRID=4326` is used on all `Point` object I create – E-Kami Jul 22 '19 at 04:15
  • Please provide sample data for the points you expect to match? – solarissmoke Jul 22 '19 at 10:19
  • The one in the comment should be found: ``` # Contains an object with field "from_location"="SRID=4326;POINT (48.80029 2.49157)" objs = models.PointsResult.objects.all() ``` – E-Kami Jul 22 '19 at 18:00
2

I do believe that you run into a similar problem to the one described here: GeoDjango & SpatiaLite - filtering nearby objects

First things first I would suggest using dwithin instead of distance_lte for the filtering because it is optimized for that use (a very sort explanation here)
From the dwithin docs we read:

Returns models where the distance to the geometry field from the lookup geometry are within the given distance from one another. Note that you can only provide Distance objects if the targeted geometries are in a projected system. For geographic geometries, you should use units of the geometry field (e.g. degrees for WGS84).

Therefore you must change your query to something like this:

meters_to_degrees = CONVERT YOUR METERS TO DEGREES
res = models.PointsResult.objects.filter(
    from_location__dwithin=(center_point, meters_to_degrees)
)

A calculation of meters (specifically of km) to degrees can be found here: How do I convert kilometres to degrees in Geodjango/GEOS?

As a final note, on a DRF perspective, you are doing a model instance creation inside the list method of the ViewSet. That is absolutely wrong and should be avoided. You should override the create method if you like to interfere with the object creation process.

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
  • 1
    Thanks a lot for this response! I think this is what I was looking for, will try this shortly. Also thanks for the DRF suggestion, I'll change this view to APIView and use a `get` instead – E-Kami Jul 30 '19 at 21:51
  • @E-Kami happy to help :) – John Moutafis Jul 30 '19 at 21:53