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); }
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); }
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))); }
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))); }
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); }
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); } }
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)); } }
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))); }