How to compute the distance matrix of many origins and destinations

65 views Asked by At

I'm trying to compute the distance matrix of multiple origins and destinations in my django view. I was previously looping through the data and making individual call to the google distance matrix api and saw it was inefficient as it took too long to return the final response. Then I found out that it's possible to make a single batch request. But unfortunately, I'm not getting the desired result as expected.

Here's my view below showing what I have

class GetAvailableRidersView(AsyncAPIView):
    permission_classes = [IsAuthenticated]
    SEARCH_RADIUS_METERS = 10

    def get_google_maps_client(self):
        """Initialize and return the asynchronous Google Maps API client."""
        return GoogleMapsClient(key=settings.GOOGLE_MAPS_API_KEY)

    async def validate_parameters(self, order_location, item_capacity, is_fragile):
        """Validate input parameters."""
        try:
            item_capacity = float(item_capacity)
            is_fragile = str_to_bool(is_fragile)
        except (ValueError, TypeError):
            return False, "Invalid or missing parameters"

        if not all(
            [
                order_location is not None and isinstance(order_location, str),
                item_capacity is not None and isinstance(item_capacity, (int, float)),
                is_fragile is not None and isinstance(is_fragile, bool),
            ]
        ):
            return False, "Invalid or missing parameters"
        return True, ""

    def handle_google_maps_api_error(self, e):
        """Handle Google Maps API errors."""
        return Response(
            {"status": "error", "message": f"Google Maps API error: {str(e)}"},
            status=400,
        )

    def handle_internal_error(self, e):
        """Handle internal server errors."""
        logger.error(str(e))
        raise Exception(str(e))

    async def get(self, request, *args, **kwargs):
        order_location = request.GET.get("origin")
        item_capacity = request.GET.get("item_capacity")
        is_fragile = request.GET.get("is_fragile")

        # Handle Missing or Invalid Parameters
        is_valid, validation_message = await self.validate_parameters(
            order_location, item_capacity, is_fragile
        )
        if not is_valid:
            return Response(
                {"status": "error", "message": validation_message}, status=400
            )

        try:
            # Initialize asynchronous Google Maps API client
            gmaps = await sync_to_async(self.get_google_maps_client)()

            # Optimize Database Queries
            available_riders = await self.get_available_riders(
                gmaps, order_location, item_capacity, is_fragile
            )
            print("Available Riders: ", available_riders)
            return Response({"status": "success", "riders": available_riders})
        except ApiError as e:
            logger.error(f"Google Maps API error: {str(e)}")
            return self.handle_google_maps_api_error(e)
        except Exception as e:
            return self.handle_internal_error(e)

    def get_available_riders_django(self, email, is_fragile, item_capacity):
        queryset = Rider.objects.filter(
            user__email=email,
            is_available=True,
            fragile_item_allowed=is_fragile,
            min_capacity__lte=item_capacity,
            max_capacity__gte=item_capacity,
        )
        return queryset.exists(), queryset.first()

    async def get_available_riders(
        self, gmaps, order_location, item_capacity, is_fragile
    ):
        try:
            riders_collection = riderexpert_db.collection("riders").stream()

            riders_within_radius = []
            rider_locations = []

            # Prepare a list of rider locations
            async for firestore_rider in riders_collection:
                firestore_data = firestore_rider.to_dict()
                
                email = firestore_data.get("email")

                exists, django_rider = await sync_to_async(
                    self.get_available_riders_django
                )(email, is_fragile, item_capacity)

                if exists:
                    rider_location = (
                        firestore_data.get("current_latitude"),
                        firestore_data.get("current_longitude"),
                    )
                    rider_locations.append(rider_location)

            # Make a single call to the distance matrix API
            if rider_locations:
                origins = [order_location] * len(rider_locations)
                destinations = rider_locations

                distance_matrix_result = await sync_to_async(gmaps.distance_matrix)(
                    origins=origins,
                    destinations=destinations,
                    mode="driving",
                    units="metric",
                )
                print("Distance Matrix: ", distance_matrix_result)

                # Iterate over the response and filter out riders within the desired radius
                for i, distance_row in enumerate(distance_matrix_result["rows"]):
                    distance = distance_row["elements"][0]["distance"]
                    duration = distance_row["elements"][0]["duration"]["text"]
                    print(f"Rider {i} distance: {distance['text']}", f"Rider {i} duration: {duration}")

                    if distance["value"] <= self.SEARCH_RADIUS_METERS * 1000:
                        # Include both distance and duration in the response
                        rider_data = {
                            "rider": await sync_to_async(self.getRiderSerializer)(
                                django_rider
                            ),
                            "distance": distance,
                            "duration": duration,
                        }
                        riders_within_radius.append(rider_data)
            print("Riders: ", riders_within_radius)
            return riders_within_radius

        except ApiError as e:
            return self.handle_google_maps_api_error(e)

    def getRiderSerializer(self, django_rider):
        return RiderSerializer(django_rider).data

The print("Distance Matrix: ", distance_matrix_result) statement isn't printing anything in the terminal. Also the print("Available Riders: ", available_riders) statement is printing Available Riders: <Response status_code=400, "text/html; charset=utf-8"> , so I'm getting an error: TypeError: Object of type Response is not JSON serializable . What seems to be wrong in my distance matrix code?

0

There are 0 answers