0
votes

Magasins à proximité, sans utiliser les packages Geo, Django Rest

J'ai besoin de répertorier les magasins à proximité d'une adresse Voici mon code:

 class NearbyShops(APIView):
    permission_classes = [IsAuthenticated]
    serializer_class = NearbyShopsSerializer

    def post(self, request):
       data = request.data
       serializer = NearbyShopsSerializer(data=data)
       if serializer.is_valid(raise_exception=True):
          try:
            address = DeliveryAddress.objects.get(client=request.user, lat=serializer.data.get('address_lat'),
                                                  long=serializer.data.get('address_long'))
          except DeliveryAddress().DoesNotExist:
            return Response({"error": "This address doesn't exist"}, status=status.HTTP_404_NOT_FOUND)       

          try:
            shops = Shop.objects.filter(city=address.city)
          except Shop().DoesNotExist:
            return Response({"error": "No shops in this city address"},
                            status=status.HTTP_417_EXPECTATION_FAILED)
          list = {}
          for shop in shops:
             location = (address.lat, address.long)
             dis = shop.distance_shop(location)
             shops = shops.annotate(distance=dis).order_by('distance')
          closest = Shop.objects.filter(distance__lt=10.0)
          for close in closest:
            list['name'] = close.name
            list['long'] = close.long
            list['lat'] = close.lat
          return Response({'shops': list}, status=status.HTTP_200_OK)

####################

    #this is the function I used for calculating distance I used haversine distance(origin, destination)
    def distance(origin, destination):
        lat1, lon1 = origin
        lat2, lon2 = destination
        radius = 6371  # km
        dlat = math.radians(lat2 - lat1)
        dlon = math.radians(lon2 - lon1)
        a = math.sin(dlat / 2) * math.sin(dlat / 2) +      math.cos(math.radians(lat1))* math.cos(math.radians(lat2)) * math.sin(dlon/2)*math.sin(dlon / 2)
        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
        d = radius * c
        return d

    

dans mon apiView en utilisant la méthode post recevant une adresse lat et longue j'ai fait ceci:

class Shop(TimeStamp):
    city = models.CharField(max_length=15, choices=CITIES, blank=True)
    lat = models.DecimalField(decimal_places=6, max_digits=10, verbose_name='latitude', default=None)
    long = models.DecimalField(decimal_places=6, max_digits=10, verbose_name='longitude', default=None)
   
    def distance_shop(self, location):
        return distance((self.lat, self.long), location)

Je ne sais pas pourquoi, mais j'obtiens en retour cette erreur: p>

QuerySet.annotate () a reçu des non-expression (s): 4783.728105194982


0 commentaires

3 Réponses :


0
votes

Il est préférable de stocker un géopoint dans votre table plutôt que de stocker la longitude et la latitude séparément. Cela améliorera les performances de votre vue et simplifiera votre code

votre boutique, le modèle d'adresse devrait ressembler à ceci

geopoint = Point(address_long, address_lat)
closest_shops = Shop.objects.filter(geopoint__distance_lte=(place.geometry, D(m=10.0)).annotate(distance=Distance('geometry', geopoint))

Ensuite, vous pouvez utiliser quelque chose comme ceci dans votre vue:

class MyModel(models.Model:
    geopoint = GeometryField(null=True, blank=True)

S'il vous plaît laissez-moi savoir si vous avez besoin de plus de précisions


2 commentaires

J'aimerais pouvoir utiliser geopoint et tous les outils postgis, mais je ne peux pas, je dois utiliser la latitude et la longitude séparément, c'est ce que je reçois du point de terminaison du client. J'apprécie votre aide, merci!


la plupart des bases de données prennent en charge les requêtes spatiales, alors essayez de le faire. Je tiens également à souligner que vous pouvez construire le point à partir de ce que vous obtenez du client comme indiqué dans ma slution



0
votes

Tout d'abord, pendant que vous essayez de créer une application de géolocalisation, vous devez utiliser postgresql et son extension postgis , et django a un package puissant nommé geodjango , vous n'avez plus besoin d'utiliser deux noms de champs différents comme vous fait long , lat

from django.views import generic
from django.contrib.gis.geos import fromstr , Point
from django.contrib.gis.db.models.functions import Distance
from .models import Shop


Latitude = 31.2551 # for example , you can get long and lat from user using js
Longitude = 49.8824 #for example
user_location = Point(Latitude , Longitude , srid=4326)

class NearestShop(generic.ListView):
    model = Shop
    context_object_name = 'shops'
    queryset = Shop.objects.annotate(distance=Distance('long_lat ',user_location)).order_by('distance').distinct('distance')

puis dans vos vues.py

from django.contrib.gis.db import models
class Shop(models.Model):
    city= models.CharField(max_length=100)
    long_lat = models.PointField()

si vous utilisez Windows et que vous ne pouvez pas configurer postgis , je vous encourage à utiliser la ligne de commande ubuntu sur Windows


3 commentaires

Merci de votre aide. Mais dans mon cas, je ne peux pas du tout utiliser pstgis. Je sais si bien à quel point geodjango est utile, mais je n'ai qu'à faire du calcul géographique moi-même,


alors utilisez la requête que j'ai écrite


votre requête nécessite l'importation de SIG, et je ne suis pas en mesure de le faire nécessite de nombreuses bibliothèques qui ralentiront mon application



0
votes

J'ai fini par faire cela au lieu d'utiliser annotate (distance). sans utiliser l'extension Postgis.

       #### see code in Question
        list = {}
        location = (address.lat, address.long)
        for shop in shops:
            dis = shop.distance_shop(location)
            if dis <= 10.0:
                list['name'] = shop.name
                list['long'] = shop.long
                list['lat'] = shop.lat
            return Response({'message': 'no close shops to your address'}, status=status.HTTP_303_SEE_OTHER)
        return Response({'shops in district': list}, status=status.HTTP_200_OK)


0 commentaires