Exemple #1
0
        public void QuadrantsJoinWhenAtLowCapacity()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>(1, 10, 25);

            List <ConcurrentGeoQuadtree <object> .IElement> elements = new List <ConcurrentGeoQuadtree <object> .IElement>();

            for (int i = 0; i < 20; ++i)
            {
                elements.Add(quadtree.InsertElement(null, new GeoCoordinates(0, 0)));
            }

            Assert.Equal(0, quadtree.GetLargestSubdivisionLevel());
            quadtree.EfficientlyReindex();

            int prevSubdivLevel = quadtree.GetLargestSubdivisionLevel();

            Assert.True(prevSubdivLevel >= 1);

            foreach (var element in elements)
            {
                quadtree.RemoveElement(element);
            }

            quadtree.EfficientlyReindex();
            Assert.True(quadtree.GetLargestSubdivisionLevel() < prevSubdivLevel);
        }
Exemple #2
0
        public void QuadrantDoesntJoinIfWouldBeAboveCapacity()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>(2, 10, 25);

            List <ConcurrentGeoQuadtree <object> .IElement> elements = new List <ConcurrentGeoQuadtree <object> .IElement>();

            for (int i = 0; i < 10; ++i)
            {
                elements.Add(quadtree.InsertElement(null, new GeoCoordinates(1, 1)));
            }

            for (int i = 0; i < 10; ++i)
            {
                quadtree.InsertElement(null, new GeoCoordinates(-1, -1));
            }

            quadtree.EfficientlyReindex();
            Assert.True(quadtree.GetLargestSubdivisionLevel() >= 1);

            // Remove all but one element from the top-right quadrant but not the
            // bottom-left quadrant.
            foreach (var element in elements.Take(9))
            {
                quadtree.RemoveElement(element);
            }

            quadtree.EfficientlyReindex();
            Assert.True(quadtree.GetLargestSubdivisionLevel() >= 1);
        }
Exemple #3
0
        public void CannotMoveRemovedElement()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>();

            var element = quadtree.InsertElement(null, new GeoCoordinates(0, 0));

            quadtree.RemoveElement(element);

            Assert.False(quadtree.MoveElement(element, new GeoCoordinates(1, 1)));
        }
Exemple #4
0
        public void MovingElementChangesPosition()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>();

            var element = quadtree.InsertElement(null, new GeoCoordinates(0, 0));

            Assert.True(element.Coordinates.Equals(new GeoCoordinates(0, 0)));

            Assert.True(quadtree.MoveElement(element, new GeoCoordinates(1, 1)));

            Assert.True(element.Coordinates.Equals(new GeoCoordinates(1, 1)));
        }
Exemple #5
0
        public void QuadrantsSubdivideAfterPassingMaxCapacity()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>(1, 10, 25);

            Assert.Equal(0, quadtree.GetLargestSubdivisionLevel());

            for (int i = 0; i < 20; ++i)
            {
                quadtree.InsertElement(null, new GeoCoordinates(0, 0));
            }

            quadtree.EfficientlyReindex();

            Assert.True(quadtree.GetLargestSubdivisionLevel() > 0);
        }
Exemple #6
0
        public void QuadrantsContinueSubdividingAfterCapacityEvenWhenNoInsert()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>(1, 10, 25);

            for (int i = 0; i < 20; ++i)
            {
                quadtree.InsertElement(null, new GeoCoordinates(0, 0));
            }

            Assert.Equal(0, quadtree.GetLargestSubdivisionLevel());

            for (int i = 0; i < 10; ++i)
            {
                quadtree.EfficientlyReindex();
                Assert.True(quadtree.GetLargestSubdivisionLevel() > i);
            }
        }
Exemple #7
0
        public void IntersectionTestFindsCorrectData()
        {
            ConcurrentGeoQuadtree <object> quadtree = new ConcurrentGeoQuadtree <object>(1, 5, 25);
            Random rand = new Random(12345);

            List <ConcurrentGeoQuadtree <object> .IElement> inElements = new List <ConcurrentGeoQuadtree <object> .IElement>();

            for (int i = 0; i < 10; ++i)
            {
                inElements.Add(quadtree.InsertElement(null,
                                                      new GeoCoordinates(rand.NextDouble() * 5 - 2.5, rand.NextDouble() * 5 - 2.5)));
            }

            List <ConcurrentGeoQuadtree <object> .IElement> outElements = new List <ConcurrentGeoQuadtree <object> .IElement>();

            for (int i = 0; i < 10; ++i)
            {
                outElements.Add(quadtree.InsertElement(null,
                                                       new GeoCoordinates(rand.NextDouble() * 5 + 40, rand.NextDouble() * 5 + 40)));
            }

            quadtree.EfficientlyReindex();

            // All inElements should be in here, no outElements should be in here.
            Rect inRect = new Rect(-3, -3, 6, 6);

            var foundElements = quadtree.GetElementsInside(inRect.Intersects);

            foreach (var elt in inElements)
            {
                Assert.True(foundElements.Contains(elt));
            }

            foreach (var elt in outElements)
            {
                Assert.False(foundElements.Contains(elt));
            }
        }
Exemple #8
0
        public async Task <(IList <PendingRideRequestCenter.MatchableRideRequest>, RideInfo)> GetRide(
            UserRideOffer offer,
            ConcurrentGeoQuadtree <PendingRideRequestCenter.MatchableRideRequest> origins,
            ConcurrentGeoQuadtree <PendingRideRequestCenter.MatchableRideRequest> destinations)
        {
            if (origins.Count == 0)
            {
                return(
                    new List <PendingRideRequestCenter.MatchableRideRequest>(),
                    new RideInfo(offer.User.UserInfo.UserId, offer.RideOffer.Car, new Route(new Route.Stop[]
                {
                    new Route.Stop(offer.RideOffer.Trip.Source),
                    new Route.Stop(offer.RideOffer.Trip.Destination)
                }))
                    );
            }

            RequestElement element          = origins.GetElementsInside((r) => true).First();
            UserInfo       passenger        = element.Data.Request.User.UserInfo;
            GeoCoordinates passengerPickUp  = element.Coordinates;
            GeoCoordinates passengerDropOff = element.Data.DestinationElement.Coordinates;

            return(
                new List <PendingRideRequestCenter.MatchableRideRequest>
            {
                element.Data
            },
                new RideInfo(offer.User.UserInfo.UserId, offer.RideOffer.Car, new Route(new Route.Stop[]
            {
                new Route.Stop(offer.RideOffer.Trip.Source),
                new Route.Stop(passengerPickUp, passenger, true),
                new Route.Stop(passengerDropOff, passenger),
                new Route.Stop(offer.RideOffer.Trip.Destination)
            }))
                );
        }
        GetRide(UserRideOffer offer,
                ConcurrentGeoQuadtree <MatchableRideRequest> origins,
                ConcurrentGeoQuadtree <MatchableRideRequest> destinations)
        {
            RouteInfo driverRoute = await GetRoute(offer);

            var originsTask      = GetElementsInsideAsync(origins, NearRoute);
            var destinationsTask = GetElementsInsideAsync(destinations, NearRoute);

            // Only consider passengers whose origins and destinations are near
            // the driver's route.
            var potentialPassengers = new HashSet <MatchableRideRequest>(
                from element in await originsTask
                select element.Data);

            potentialPassengers.IntersectWith(
                from element in await destinationsTask
                select element.Data);

            // Find a passenger going in the same direction as the driver such that
            // picking up the passenger does not put the driver too far out of their way.
            foreach (var passenger in potentialPassengers.Where(GoingInDriversDirection))
            {
                RouteInfo routeWithPassenger = await GetRouteWithPassenger(offer, passenger);

                // Reject route if it's too far out of the way according to
                // the driver's settings.
                if (driverRoute.drivingTime.HasValue && routeWithPassenger.drivingTime.HasValue)
                {
                    TimeSpan originalTime = driverRoute.drivingTime.Value;
                    TimeSpan newTime      = routeWithPassenger.drivingTime.Value;
                    TimeSpan maxTime      = originalTime + TimeSpan.FromMinutes(offer.RideOffer.MaxTimeOutOfWay);

                    if (newTime > maxTime)
                    {
                        // Output debug info for demos.
                        Program.LogError($"Matched {offer.User.UserInfo.UserId} with {passenger.Request.User.UserInfo.UserId}" +
                                         " but resulting route was too long." +
                                         $" Original trip duration: {originalTime.Minutes} mins." +
                                         $" Matched trip duration: {newTime.Minutes} mins." +
                                         $" Driver's max time out of way: {offer.RideOffer.MaxTimeOutOfWay} mins.");
                        continue;
                    }
                }

                return(RideWithPassenger(offer, passenger));
            }

            return(EmptyRide(offer, driverRoute));


            /// <summary>
            /// Tests whether any point in the rect is close enough to <see cref="route"/>.
            /// </summary>
            bool NearRoute(Rect rect)
            {
                // Ignore passengers more than approximately 1km of the route.
                // TODO Take large max-time-out-of-way values into account when choosing max-dist-out-of-way.
                double maxDistMeters  = 1000;
                double maxDistDegrees = offer.RideOffer.Trip.Source.DegreesUpperBound(maxDistMeters);

                GeoPolyline route = driverRoute.overviewPolyline;

                return(route.RectWithinDistance(rect, maxDistDegrees));
            }

            /// <summary>
            /// Tests whether this passenger is going in the same direction as the driver.
            /// </summary>
            bool GoingInDriversDirection(MatchableRideRequest request)
            {
                var driverDest = offer.RideOffer.Trip.Destination;
                var driverOrig = offer.RideOffer.Trip.Source;
                var driverSeg  = new GeoSegment(driverOrig, driverDest);

                var passDest = request.Request.RideRequest.Trip.Destination;
                var passOrig = request.Request.RideRequest.Trip.Source;
                var passSeg  = new GeoSegment(passOrig, passDest);

                // Use GeoSegments so that this works near prime meridian.
                var passDiff   = passSeg.Point2Representative - passSeg.Point1Representative;
                var driverDiff = driverSeg.Point2Representative - driverSeg.Point1Representative;

                // Compute the dot product of the vectors. This is a pretty rough
                // estimate and doesn't take into account the Earth's curvature.
                return(passDiff.Dot(driverDiff) > 0);
            }
        }
 GetElementsInsideAsync(
     ConcurrentGeoQuadtree <MatchableRideRequest> quadtree,
     Predicate <Rect> area)
 {
     return(Task.Run(() => quadtree.GetElementsInside(area)));
 }