예제 #1
0
        private bool CheckGeneralShape(LinePath rndfPath, CarTimestamp rndfPathTimestamp, LocalLaneModel centerLaneModel)
        {
            // bad newz bears
            if (centerLaneModel.LanePath == null || centerLaneModel.LanePath.Count < 2)
            {
                return(false);
            }

            // project the lane model's path into the rndf path's timestamp
            RelativeTransform relTransform  = Services.RelativePose.GetTransform(localRoadModel.Timestamp, rndfPathTimestamp);
            LinePath          laneModelPath = centerLaneModel.LanePath.Transform(relTransform);

            // get the zero point of the lane model path
            LinePath.PointOnPath laneZeroPoint = laneModelPath.ZeroPoint;

            // get the first waypoint on the RNDF path
            LinePath.PointOnPath rndfZeroPoint = rndfPath.ZeroPoint;
            Coordinates          firstWaypoint = rndfPath[rndfZeroPoint.Index + 1];

            // get the projection of the first waypoint onto the lane path
            LinePath.PointOnPath laneFirstWaypoint = laneModelPath.GetClosestPoint(firstWaypoint);

            // get the offset vector
            Coordinates offsetVector = laneFirstWaypoint.Location - firstWaypoint;

            // start iterating through the waypoints forward and project them onto the rndf path
            for (int i = rndfZeroPoint.Index + 1; i < rndfPath.Count; i++)
            {
                // get the waypoint
                Coordinates waypoint = rndfPath[i];

                // adjust by the first waypoint offset
                waypoint += offsetVector;

                // project onto the lane model
                LinePath.PointOnPath laneWaypoint = laneModelPath.GetClosestPoint(waypoint);

                // check the distance from the zero point on the lane model
                if (laneModelPath.DistanceBetween(laneZeroPoint, laneWaypoint) > road_model_max_dist)
                {
                    break;
                }

                // check the devation from the rndf
                double deviation = waypoint.DistanceTo(laneWaypoint.Location);

                // if the deviation is over some threshold, then we reject the model
                if (deviation > road_deviation_reject_threshold)
                {
                    return(false);
                }
            }

            // we got this far, so this stuff is OK
            return(true);
        }
예제 #2
0
        public HitTestResult HitTest(Coordinates loc, float tol)
        {
            if (line == null)
            {
                return(HitTestResult.NoHit);
            }

            LinePath.PointOnPath pt = line.GetClosestPoint(loc);
            if (!pt.Valid)
            {
                return(HitTestResult.NoHit);
            }
            Coordinates closestPoint = pt.Location;

            if (closestPoint.DistanceTo(loc) < tol)
            {
                return(new HitTestResult(this, true, closestPoint, null));
            }

            return(HitTestResult.NoHit);
        }
예제 #3
0
        public HitTestResult HitTest(Coordinates loc, float tol)
        {
            if (laneModel == null || laneModel.LanePath == null || laneModel.LanePath.Count <= 1)
            {
                return(HitTestResult.NoHit);
            }

            // test for a hit on the center line
            Coordinates closestPoint = laneModel.LanePath.GetClosestPoint(loc).Location;
            double      dist         = closestPoint.DistanceTo(loc);

            if (prevLeftBound != null)
            {
                Coordinates leftPoint = prevLeftBound.GetClosestPoint(loc).Location;
                double      newDist   = leftPoint.DistanceTo(loc);
                if (newDist < dist)
                {
                    dist         = newDist;
                    closestPoint = leftPoint;
                }
            }

            if (prevRightBound != null)
            {
                Coordinates rightPoint = prevRightBound.GetClosestPoint(loc).Location;
                double      newDist    = rightPoint.DistanceTo(loc);
                if (newDist < dist)
                {
                    dist         = newDist;
                    closestPoint = rightPoint;
                }
            }

            if (closestPoint.DistanceTo(loc) < tol)
            {
                return(new HitTestResult(this, true, closestPoint, null));
            }

            return(HitTestResult.NoHit);
        }
예제 #4
0
        /// <summary>
        /// Gets the closest component to a location
        /// </summary>
        /// <param name="loc"></param>
        /// <returns></returns>
        public SLComponentType ClosestComponent(Coordinates loc)
        {
            LinePath iLp  = this.InitialPath(loc);
            double   init = iLp.GetPoint(iLp.GetClosestPoint(loc)).DistanceTo(loc);

            LinePath fLp = this.FinalPath(loc);
            double   fin = fLp.GetPoint(fLp.GetClosestPoint(loc)).DistanceTo(loc);

            double inter = Interconnect.DistanceTo(loc);

            if (init < inter && init < fin)
            {
                return(SLComponentType.Initial);
            }
            else if (fin < inter && fin < init)
            {
                return(SLComponentType.Final);
            }
            else
            {
                return(SLComponentType.Interconnect);
            }
        }
예제 #5
0
        public Coordinates?ClosestPointToLine(LinePath path, VehicleState vs)
        {
            Coordinates?closest = null;
            double      minDist = Double.MaxValue;

            for (int i = 0; i < this.trackedCluster.relativePoints.Length; i++)
            {
                Coordinates c    = this.TransformCoordAbs(this.trackedCluster.relativePoints[i], vs);
                double      dist = path.GetClosestPoint(c).Location.DistanceTo(c);

                if (!closest.HasValue)
                {
                    closest = c;
                    minDist = dist;
                }
                else if (dist < minDist)
                {
                    closest = c;
                    minDist = dist;
                }
            }

            return(closest);
        }
 public Coordinates ClosestToPolygon(Polygon p, Coordinates c)
 {
     LinePath lp = new LinePath(p);
     return lp.GetPoint(lp.GetClosestPoint(c));
 }
        /// <summary>
        /// Plans over the zone
        /// </summary>
        /// <param name="planningState"></param>
        /// <param name="navigationalPlan"></param>
        /// <param name="vehicleState"></param>
        /// <param name="vehicles"></param>
        /// <param name="obstacles"></param>
        /// <param name="blockages"></param>
        /// <returns></returns>
        public Maneuver Plan(IState planningState, INavigationalPlan navigationalPlan, 
            VehicleState vehicleState, SceneEstimatorTrackedClusterCollection vehicles,
            SceneEstimatorUntrackedClusterCollection obstacles, List<ITacticalBlockage> blockages)
        {
            #region Zone Travelling State

            if (planningState is ZoneTravelingState)
            {
                // check blockages
                /*if (blockages != null && blockages.Count > 0 && blockages[0] is ZoneBlockage)
                {
                    // create the blockage state
                    EncounteredBlockageState ebs = new EncounteredBlockageState(blockages[0], CoreCommon.CorePlanningState);

                    // go to a blockage handling tactical
                    return new Maneuver(new NullBehavior(), ebs, TurnDecorators.NoDecorators, vehicleState.Timestamp);
                }*/

                // cast state
                ZoneState zs = (ZoneState)planningState;

                // plan over state and zone
                ZonePlan zp = (ZonePlan)navigationalPlan;

                // check zone path does not exist
                if (zp.RecommendedPath.Count < 2)
                {
                    // zone startup again
                    ZoneStartupState zss = new ZoneStartupState(zs.Zone, true);
                    return new Maneuver(new HoldBrakeBehavior(), zss, TurnDecorators.NoDecorators, vehicleState.Timestamp);
                }

                // final path seg
                LinePath.PointOnPath endBack = zp.RecommendedPath.AdvancePoint(zp.RecommendedPath.EndPoint, -TahoeParams.VL);
                LinePath lp = zp.RecommendedPath.SubPath(endBack, zp.RecommendedPath.EndPoint);
                LinePath lB = lp.ShiftLateral(TahoeParams.T);
                LinePath rB = lp.ShiftLateral(-TahoeParams.T);

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));

                // get speed command
                ScalarSpeedCommand sc = new ScalarSpeedCommand(2.24);

                // Behavior
                Behavior b = new ZoneTravelingBehavior(zp.Zone.ZoneId, zp.Zone.Perimeter.PerimeterPolygon, zp.Zone.StayOutAreas.ToArray(),
                    sc, zp.RecommendedPath, lB, rB);

                // maneuver
                return new Maneuver(b, CoreCommon.CorePlanningState, TurnDecorators.NoDecorators, vehicleState.Timestamp);
            }

            #endregion

            #region Parking State

            else if (planningState is ParkingState)
            {
                // get state
                ParkingState ps = (ParkingState)planningState;

                // determine stay out areas to use
                List<Polygon> stayOuts = new List<Polygon>();
                foreach (Polygon p in ps.Zone.StayOutAreas)
                {
                    if (!p.IsInside(ps.ParkingSpot.NormalWaypoint.Position) && !p.IsInside(ps.ParkingSpot.Checkpoint.Position))
                        stayOuts.Add(p);
                }

                LinePath rB = ps.ParkingSpot.GetRightBound();
                LinePath lB = ps.ParkingSpot.GetLeftBound();

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));

                // create behavior
                ZoneParkingBehavior zpb = new ZoneParkingBehavior(ps.Zone.ZoneId, ps.Zone.Perimeter.PerimeterPolygon, stayOuts.ToArray(), new ScalarSpeedCommand(2.24),
                    ps.ParkingSpot.GetSpotPath(), lB, rB, ps.ParkingSpot.SpotId, 1.0);

                // maneuver
                return new Maneuver(zpb, ps, TurnDecorators.NoDecorators, vehicleState.Timestamp);
            }

            #endregion

            #region Pulling Out State

            else if (planningState is PullingOutState)
            {
                // get state
                PullingOutState pos = (PullingOutState)planningState;

                // plan over state and zone
                ZonePlan zp = (ZonePlan)navigationalPlan;

                // final path seg
                Coordinates endVec = zp.RecommendedPath[0] - zp.RecommendedPath[1];
                Coordinates endBack = zp.RecommendedPath[0] + endVec.Normalize(TahoeParams.VL * 2.0);
                LinePath rp = new LinePath(new Coordinates[]{pos.ParkingSpot.Checkpoint.Position, pos.ParkingSpot.NormalWaypoint.Position,
                    zp.RecommendedPath[0], endBack});
                LinePath lp = new LinePath(new Coordinates[]{zp.RecommendedPath[0], endBack});
                LinePath lB = lp.ShiftLateral(TahoeParams.T * 2.0);
                LinePath rB = lp.ShiftLateral(-TahoeParams.T * 2.0);

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rp, ArbiterInformationDisplayObjectType.leftBound));

                // determine stay out areas to use
                List<Polygon> stayOuts = new List<Polygon>();
                foreach (Polygon p in pos.Zone.StayOutAreas)
                {
                    if (!p.IsInside(pos.ParkingSpot.NormalWaypoint.Position) && !p.IsInside(pos.ParkingSpot.Checkpoint.Position))
                        stayOuts.Add(p);
                }

                // get speed command
                ScalarSpeedCommand sc = new ScalarSpeedCommand(2.24);

                // Behavior
                Behavior b = new ZoneParkingPullOutBehavior(zp.Zone.ZoneId, zp.Zone.Perimeter.PerimeterPolygon, stayOuts.ToArray(),
                    sc, pos.ParkingSpot.GetSpotPath(), pos.ParkingSpot.GetLeftBound(), pos.ParkingSpot.GetRightBound(), pos.ParkingSpot.SpotId,
                    rp, lB, rB);

                // maneuver
                return new Maneuver(b, pos, TurnDecorators.NoDecorators, vehicleState.Timestamp);
            }

            #endregion

            #region Zone Startup State

            else if (planningState is ZoneStartupState)
            {
                // state
                ZoneStartupState zss = (ZoneStartupState)planningState;

                // get the zone
                ArbiterZone az = zss.Zone;

                // navigational edge
                INavigableNode inn = CoreCommon.RoadNetwork.ArbiterWaypoints[CoreCommon.Mission.MissionCheckpoints.Peek().WaypointId];

                // check over all the parking spaces
                foreach (ArbiterParkingSpot aps in az.ParkingSpots)
                {
                    // inside both parts of spot
                    if ((vehicleState.VehiclePolygon.IsInside(aps.NormalWaypoint.Position) && vehicleState.VehiclePolygon.IsInside(aps.Checkpoint.Position)) ||
                        (vehicleState.VehiclePolygon.IsInside(aps.NormalWaypoint.Position)))
                    {
                        // want to just park in it again
                        return new Maneuver(new HoldBrakeBehavior(), new ParkingState(az, aps), TurnDecorators.NoDecorators, vehicleState.Timestamp);
                    }
                }

                Polygon forwardPolygon = vehicleState.ForwardPolygon;
                Polygon rearPolygon = vehicleState.RearPolygon;

                Navigator nav = CoreCommon.Navigation;
                List<ZoneNavigationEdgeSort> forwardForward = new List<ZoneNavigationEdgeSort>();
                List<ZoneNavigationEdgeSort> reverseReverse = new List<ZoneNavigationEdgeSort>();
                List<ZoneNavigationEdgeSort> perpendicularPerpendicular = new List<ZoneNavigationEdgeSort>();
                List<ZoneNavigationEdgeSort> allEdges = new List<ZoneNavigationEdgeSort>();
                List<ZoneNavigationEdgeSort> allEdgesNoLimits = new List<ZoneNavigationEdgeSort>();
                foreach (NavigableEdge ne in az.NavigableEdges)
                {
                    if (!(ne.End is ArbiterParkingSpotWaypoint) && !(ne.Start is ArbiterParkingSpotWaypoint))
                    {
                        // get distance to edge
                        LinePath lp = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                        double distTmp = lp.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);

                        // get direction along segment
                        DirectionAlong da = vehicleState.DirectionAlongSegment(lp);

                        // check dist reasonable
                        if (distTmp > TahoeParams.VL)
                        {
                            // zone navigation edge sort item
                            ZoneNavigationEdgeSort znes = new ZoneNavigationEdgeSort(distTmp, ne, lp);

                            // add to lists
                            if (da == DirectionAlong.Forwards &&
                                (forwardPolygon.IsInside(ne.Start.Position) || forwardPolygon.IsInside(ne.End.Position)))
                                forwardForward.Add(znes);
                            /*else if (da == DirectionAlong.Perpendicular &&
                                !(forwardPolygon.IsInside(ne.Start.Position) || forwardPolygon.IsInside(ne.End.Position)) &&
                                !(rearPolygon.IsInside(ne.Start.Position) || rearPolygon.IsInside(ne.End.Position)))
                                perpendicularPerpendicular.Add(znes);
                            else if (rearPolygon.IsInside(ne.Start.Position) || rearPolygon.IsInside(ne.End.Position))
                                reverseReverse.Add(znes);*/

                            // add to all edges
                            allEdges.Add(znes);
                        }
                    }
                }

                // sort by distance
                forwardForward.Sort();
                reverseReverse.Sort();
                perpendicularPerpendicular.Sort();
                allEdges.Sort();

                ZoneNavigationEdgeSort bestZnes = null;

                for (int i = 0; i < allEdges.Count; i++)
                {
                    // get line to initial
                    Line toInitial = new Line(vehicleState.Front, allEdges[i].edge.Start.Position);
                    Line toFinal = new Line(vehicleState.Front, allEdges[i].edge.End.Position);
                    bool intersects = false;
                    Coordinates[] interPts;
                    foreach (Polygon sop in az.StayOutAreas)
                    {
                        if (!intersects &&
                            (sop.Intersect(toInitial, out interPts) && sop.Intersect(toFinal, out interPts)))
                            intersects = true;
                    }

                    if (!intersects)
                    {
                        allEdges[i].zp = nav.PlanZone(az, allEdges[i].edge.End, inn);
                        allEdges[i].zp.Time += vehicleState.Front.DistanceTo(allEdges[i].lp.GetClosestPoint(vehicleState.Front).Location) / 2.24;
                        ZoneNavigationEdgeSort tmpZnes = allEdges[i];
                        if ((bestZnes == null && tmpZnes.zp.RecommendedPath.Count > 1) ||
                                (bestZnes != null && tmpZnes.zp.RecommendedPath.Count > 1 && tmpZnes.zp.Time < bestZnes.zp.Time))
                            bestZnes = tmpZnes;
                    }

                    if (i > allEdges.Count / 2 && bestZnes != null)
                        break;
                }

                if (bestZnes != null)
                {
                    ArbiterOutput.Output("Found good edge to start in zone");
                    return new Maneuver(new HoldBrakeBehavior(), new ZoneOrientationState(az, bestZnes.edge), TurnDecorators.NoDecorators, vehicleState.Timestamp);
                }
                else
                {
                    ArbiterOutput.Output("Could not find good edge to start, choosing random not blocked");

                    List<ZoneNavigationEdgeSort> okZnes = new List<ZoneNavigationEdgeSort>();
                    foreach (NavigableEdge tmpOk in az.NavigableEdges)
                    {
                        // get line to initial
                        LinePath edgePath = new LinePath(new Coordinates[] { tmpOk.Start.Position, tmpOk.End.Position });
                        double dist = edgePath.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);
                        ZoneNavigationEdgeSort tmpZnes = new ZoneNavigationEdgeSort(dist, tmpOk, edgePath);
                        tmpZnes.zp = nav.PlanZone(az, tmpZnes.edge.End, inn);
                        tmpZnes.zp.Time += vehicleState.Front.DistanceTo(tmpZnes.lp.GetClosestPoint(vehicleState.Front).Location) / 2.24;
                        if (tmpZnes.zp.RecommendedPath.Count >= 2)
                            okZnes.Add(tmpZnes);
                    }

                    if (okZnes.Count == 0)
                        okZnes = allEdges;
                    else
                        okZnes.Sort();

                    // get random close edge
                    Random rand = new Random();
                    int chosen = rand.Next(Math.Min(5, okZnes.Count));

                    // get closest edge not part of a parking spot, get on it
                    NavigableEdge closest = okZnes[chosen].edge;//null;
                    //double distance = Double.MaxValue;
                    /*foreach (NavigableEdge ne in az.NavigableEdges)
                    {
                        if (!(ne.End is ArbiterParkingSpotWaypoint) && !(ne.Start is ArbiterParkingSpotWaypoint))
                        {
                            // get distance to edge
                            LinePath lp = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                            double distTmp = lp.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);
                            if (closest == null || distTmp < distance)
                            {
                                closest = ne;
                                distance = distTmp;
                            }
                        }
                    }*/
                    return new Maneuver(new HoldBrakeBehavior(), new ZoneOrientationState(az, closest), TurnDecorators.NoDecorators, vehicleState.Timestamp);
                }
            }

            #endregion

            #region Unknown

            else
            {
                // non-handled state
                throw new ArgumentException("Unknown state", "CoreCommon.CorePlanningState");
            }

            #endregion
        }
예제 #8
0
        public static Polygon PartitionPolygon(ArbiterLanePartition alp)
        {
            if (alp.Initial.PreviousPartition != null &&
                alp.Final.NextPartition != null &&
                alp.Length < 30.0 && alp.Length > 4.0)
            {
                // get partition turn direction
                ArbiterTurnDirection pTD = PartitionTurnDirection(alp);

                // check if angles of previous and next are such that not straight through
                if (pTD != ArbiterTurnDirection.Straight)
                {
                    // get partition poly
                    ArbiterInterconnect tmpAi = alp.ToInterconnect;
                    tmpAi.TurnDirection = pTD;
                    GenerateInterconnectPolygon(tmpAi);
                    Polygon pPoly = tmpAi.TurnPolygon;

                    // here is default partition polygon
                    LinePath alplb = alp.PartitionPath.ShiftLateral(-alp.Lane.Width / 2.0);
                    LinePath alprb = alp.PartitionPath.ShiftLateral(alp.Lane.Width / 2.0);
                    alprb.Reverse();
                    List <Coordinates> alpdefaultPoly = alplb;
                    alpdefaultPoly.AddRange(alprb);

                    // get full poly
                    pPoly.AddRange(alpdefaultPoly);
                    pPoly = Polygon.GrahamScan(pPoly);

                    return(pPoly);
                }
            }
            else if (alp.Length >= 30)
            {
                Polygon pBase = GenerateSimplePartitionPolygon(alp, alp.PartitionPath, alp.Lane.Width);

                if (alp.Initial.PreviousPartition != null && Math.Abs(FinalIntersectionAngle(alp.Initial.PreviousPartition)) > 15)
                {
                    // initial portion
                    Coordinates i1   = alp.Initial.Position - alp.Initial.PreviousPartition.Vector().Normalize(15.0);
                    Coordinates i2   = alp.Initial.Position;
                    Coordinates i3   = i2 + alp.Vector().Normalize(15.0);
                    LinePath    il12 = new LinePath(new Coordinates[] { i1, i2 });
                    LinePath    il23 = new LinePath(new Coordinates[] { i2, i3 });
                    LinePath    il13 = new LinePath(new Coordinates[] { i1, i3 });
                    Coordinates iCC  = il13.GetClosestPoint(i2).Location;
                    if (GeneralToolkit.TriangleArea(i1, i2, i3) < 0)
                    {
                        il13 = il13.ShiftLateral(iCC.DistanceTo(i2) + alp.Lane.Width / 2.0);
                    }
                    else
                    {
                        il13 = il13.ShiftLateral(-iCC.DistanceTo(i2) + alp.Lane.Width / 2.0);
                    }
                    LinePath.PointOnPath iCCP = il13.GetClosestPoint(iCC);
                    iCCP = il13.AdvancePoint(iCCP, -10.0);
                    il13 = il13.SubPath(iCCP, 20.0);
                    Polygon iBase = GenerateSimplePolygon(il23, alp.Lane.Width);
                    iBase.Add(il13[1]);
                    Polygon iP = Polygon.GrahamScan(iBase);
                    pBase = PolygonToolkit.PolygonUnion(new List <Polygon>(new Polygon[] { pBase, iP }));
                }

                if (alp.Final.NextPartition != null && Math.Abs(FinalIntersectionAngle(alp)) > 15)
                {
                    // initial portion
                    Coordinates i1   = alp.Final.Position - alp.Vector().Normalize(15.0);
                    Coordinates i2   = alp.Final.Position;
                    Coordinates i3   = i2 + alp.Final.NextPartition.Vector().Normalize(15.0);
                    LinePath    il12 = new LinePath(new Coordinates[] { i1, i2 });
                    LinePath    il23 = new LinePath(new Coordinates[] { i2, i3 });
                    LinePath    il13 = new LinePath(new Coordinates[] { i1, i3 });
                    Coordinates iCC  = il13.GetClosestPoint(i2).Location;
                    if (GeneralToolkit.TriangleArea(i1, i2, i3) < 0)
                    {
                        il13 = il13.ShiftLateral(iCC.DistanceTo(i2) + alp.Lane.Width / 2.0);
                    }
                    else
                    {
                        il13 = il13.ShiftLateral(-iCC.DistanceTo(i2) + alp.Lane.Width / 2.0);
                    }
                    LinePath.PointOnPath iCCP = il13.GetClosestPoint(iCC);
                    iCCP = il13.AdvancePoint(iCCP, -10.0);
                    il13 = il13.SubPath(iCCP, 20.0);
                    Polygon iBase = GenerateSimplePolygon(il12, alp.Lane.Width);
                    iBase.Add(il13[0]);
                    Polygon iP = Polygon.GrahamScan(iBase);
                    pBase = PolygonToolkit.PolygonUnion(new List <Polygon>(new Polygon[] { pBase, iP }));
                }

                return(pBase);
            }

            // fall out
            return(null);
        }
        public Coordinates ClosestToPolygon(Polygon p, Coordinates c)
        {
            LinePath lp = new LinePath(p);

            return(lp.GetPoint(lp.GetClosestPoint(c)));
        }
        private ILaneModel GetLaneModel(LocalLaneModel laneModel, LinePath rndfPath, double rndfPathWidth, CarTimestamp rndfPathTimestamp)
        {
            // check the lane model probability
            if (laneModel.Probability < lane_probability_reject_threshold) {
                // we're rejecting this, just return a path lane model
                return new PathLaneModel(rndfPathTimestamp, rndfPath, rndfPathWidth);
            }

            // project the lane model's path into the rndf path's timestamp
            RelativeTransform relTransform = Services.RelativePose.GetTransform(localRoadModel.Timestamp, rndfPathTimestamp);
            LinePath laneModelPath = laneModel.LanePath.Transform(relTransform);

            // iterate through the waypoints in the RNDF path and project onto the lane model
            // the first one that is over the threshold, we consider the waypoint before as a potential ending point
            LinePath.PointOnPath laneModelDeviationEndPoint = new LinePath.PointOnPath();
            // flag indicating if any of the waypoint tests failed because of the devation was too high
            bool anyDeviationTooHigh = false;
            // number of waypoints accepted
            int numWaypointsAccepted = 0;

            // get the vehicle's position on the rndf path
            LinePath.PointOnPath rndfZeroPoint = rndfPath.ZeroPoint;

            // get the vehicle's position on the lane model
            LinePath.PointOnPath laneModelZeroPoint = laneModelPath.ZeroPoint;

            // get the last point we want to consider on the lane model
            LinePath.PointOnPath laneModelFarthestPoint = laneModelPath.AdvancePoint(laneModelZeroPoint, lane_model_max_dist);

            // start walking forward through the waypoints on the rndf path
            // this loop will implicitly exit when we're past the end of the lane model as the waypoints
            //		will stop being close to the lane model (GetClosestPoint returns the end point if we're past the
            //    end of the path)
            for (int i = rndfZeroPoint.Index+1; i < rndfPath.Count; i++) {
                // get the waypoint
                Coordinates rndfWaypoint = rndfPath[i];

                // get the closest point on the lane model
                LinePath.PointOnPath laneModelClosestPoint = laneModelPath.GetClosestPoint(rndfWaypoint);

                // compute the distance between the two
                double deviation = rndfWaypoint.DistanceTo(laneModelClosestPoint.Location);

                // if this is above the deviation threshold, leave the loop
                if (deviation > lane_deviation_reject_threshold || laneModelClosestPoint > laneModelFarthestPoint) {
                    // if we're at the end of the lane model path, we don't want to consider this a rejection
                    if (laneModelClosestPoint < laneModelFarthestPoint) {
                        // mark that at least on deviation was too high
                        anyDeviationTooHigh = true;
                    }
                    break;
                }

                // increment the number of waypoint accepted
                numWaypointsAccepted++;

                // update the end point of where we're valid as the local road model was OK up to this point
                laneModelDeviationEndPoint = laneModelClosestPoint;
            }

            // go through and figure out how far out the variance is within tolerance
            LinePath.PointOnPath laneModelVarianceEndPoint = new LinePath.PointOnPath();
            // walk forward from this point until the end of the lane mode path
            for (int i = laneModelZeroPoint.Index+1; i < laneModelPath.Count; i++) {
                // check if we're within the variance toleration
                if (laneModel.LaneYVariance[i] <= y_var_reject_threshold) {
                    // we are, update the point on path
                    laneModelVarianceEndPoint = laneModelPath.GetPointOnPath(i);
                }
                else {
                    // we are out of tolerance, break out of the loop
                    break;
                }
            }

            // now figure out everything out
            // determine waypoint rejection status
            WaypointRejectionResult waypointRejectionResult;
            if (laneModelDeviationEndPoint.Valid) {
                // if the point is valid, that we had at least one waypoint that was ok
                // check if any waypoints were rejected
                if (anyDeviationTooHigh) {
                    // some waypoint was ok, so we know that at least one waypoint was accepted
                    waypointRejectionResult = WaypointRejectionResult.SomeWaypointsAccepted;
                }
                else {
                    // no waypoint triggered a rejection, but at least one was good
                    waypointRejectionResult = WaypointRejectionResult.AllWaypointsAccepted;
                }
            }
            else {
                // the point is not valid, so we either had no waypoints or we had all rejections
                if (anyDeviationTooHigh) {
                    // the first waypoint was rejected, so all are rejected
                    waypointRejectionResult = WaypointRejectionResult.AllWaypointsRejected;
                }
                else {
                    // the first waypoint (if any) was past the end of the lane model
                    waypointRejectionResult = WaypointRejectionResult.NoWaypoints;
                }
            }

            // criteria for determining if this path is valid:
            //	- if some or all waypoints were accepted, than this is probably a good path
            //		- if some of the waypoints were accepted, we go no farther than the last waypoint that was accepted
            //	- if there were no waypoints, this is a potentially dangerous situation since we can't reject
            //    or confirm the local road model. for now, we'll assume that it is correct but this may need to change
            //    if we start handling intersections in this framework
            //  - if all waypoints were rejected, than we don't use the local road model
            //  - go no farther than the laneModelVarianceEndPoint, which is the last point where the y-variance of
            //    the lane model was in tolerance

            // now build out the lane model
            ILaneModel finalLaneModel;

            // check if we rejected all waypoints or no lane model points satisified the variance threshold
            if (waypointRejectionResult == WaypointRejectionResult.AllWaypointsRejected || !laneModelVarianceEndPoint.Valid) {
                // want to just use the path lane model
                finalLaneModel = new PathLaneModel(rndfPathTimestamp, rndfPath, rndfPathWidth);
            }
            else {
                // we'll use the lane model
                // need to build up the center line as well as left and right bounds

                // to build up the center line, use the lane model as far as we feel comfortable (limited by either variance
                // or by rejections) and then use the rndf lane after that.
                LinePath centerLine = new LinePath();

                // figure out the max distance
                // if there were no waypoints, set the laneModelDeviationEndPoint to the end of the lane model
                if (waypointRejectionResult == WaypointRejectionResult.NoWaypoints) {
                    laneModelDeviationEndPoint = laneModelFarthestPoint;
                }

                // figure out the closer of the end points
                LinePath.PointOnPath laneModelEndPoint = (laneModelDeviationEndPoint < laneModelVarianceEndPoint) ? laneModelDeviationEndPoint : laneModelVarianceEndPoint;
                bool endAtWaypoint = laneModelEndPoint == laneModelDeviationEndPoint;

                // add the lane model to the center line
                centerLine.AddRange(laneModelPath.GetSubpathEnumerator(laneModelZeroPoint, laneModelEndPoint));

                // create a list to hold the width expansion values
                List<double> widthValue = new List<double>();

                // make the width expansion values the width of the path plus the 1-sigma values
                for (int i = laneModelZeroPoint.Index; i < laneModelZeroPoint.Index+centerLine.Count; i++) {
                    widthValue.Add(laneModel.Width/2.0 + Math.Sqrt(laneModel.LaneYVariance[i]));
                }

                // now figure out how to add the rndf path
                // get the projection of the lane model end point on the rndf path
                LinePath.PointOnPath rndfPathStartPoint = rndfPath.GetClosestPoint(laneModelEndPoint.Location);

                // if the closest point is past the end of rndf path, then we don't want to tack anything on
                if (rndfPathStartPoint != rndfPath.EndPoint) {
                    // get the last segment of the new center line
                    Coordinates centerLineEndSegmentVec = centerLine.EndSegment.UnitVector;
                    // get the last point of the new center line
                    Coordinates laneModelEndLoc = laneModelEndPoint.Location;

                    // now figure out the distance to the next waypoint
                    LinePath.PointOnPath rndfNextPoint = new LinePath.PointOnPath();

                    // figure out if we're ending at a waypoint or not
                    if (endAtWaypoint) {
                        rndfNextPoint = rndfPath.GetPointOnPath(rndfPathStartPoint.Index+1);

                        // if the distance from the start point to the next point is less than rndf_dist_min, then
                        // use the waypont after
                        double dist = rndfPath.DistanceBetween(rndfPathStartPoint, rndfNextPoint);

                        if (dist < rndf_dist_min) {
                            if (rndfPathStartPoint.Index < rndfPath.Count-2) {
                                rndfNextPoint = rndfPath.GetPointOnPath(rndfPathStartPoint.Index + 2);
                            }
                            else if (rndfPath.DistanceBetween(rndfPathStartPoint, rndfPath.EndPoint) < rndf_dist_min) {
                                rndfNextPoint = LinePath.PointOnPath.Invalid;
                            }
                            else {
                                rndfNextPoint = rndfPath.AdvancePoint(rndfPathStartPoint, rndf_dist_min*2);
                            }
                        }
                    }
                    else {
                        // track the last angle we had
                        double lastAngle = double.NaN;

                        // walk down the rndf path until we find a valid point
                        for (double dist = rndf_dist_min; dist <= rndf_dist_max; dist += rndf_dist_step) {
                            // advance from the start point by dist
                            double distTemp = dist;
                            rndfNextPoint = rndfPath.AdvancePoint(rndfPathStartPoint, ref distTemp);

                            // if the distTemp is > 0, then we're past the end of the path
                            if (distTemp > 0) {
                                // if we're immediately past the end, we don't want to tack anything on
                                if (dist == rndf_dist_min) {
                                    rndfNextPoint = LinePath.PointOnPath.Invalid;
                                }

                                break;
                            }

                            // check the angle made by the last segment of center line and the segment
                            // formed between the end point of the center line and this new point
                            double angle = Math.Acos(centerLineEndSegmentVec.Dot((rndfNextPoint.Location-laneModelEndLoc).Normalize()));

                            // check if the angle satisfies the threshold or we're increasing the angle
                            if (Math.Abs(angle) < rndf_angle_threshold || (!double.IsNaN(lastAngle) && angle > lastAngle)) {
                                // break out of the loop, we're done searching
                                break;
                            }

                            lastAngle = angle;
                        }
                    }

                    // tack on the rndf starting at next point going to the end
                    if (rndfNextPoint.Valid) {
                        LinePath subPath = rndfPath.SubPath(rndfNextPoint, rndfPath.EndPoint);
                        centerLine.AddRange(subPath);

                        // insert the lane model end point into the sub path
                        subPath.Insert(0, laneModelEndLoc);

                        // get the angles
                        List<Pair<int, double>> angles = subPath.GetIntersectionAngles(0, subPath.Count-1);

                        // add the width of the path inflated by the angles
                        for (int i = 0; i < angles.Count; i++) {
                            // calculate the width expansion factor
                            // 90 deg, 3x width
                            // 45 deg, 1.5x width
                            // 0 deg, 1x width
                            double widthFactor = Math.Pow(angles[i].Right/(Math.PI/2.0), 2)*2 + 1;

                            // add the width value
                            widthValue.Add(widthFactor*laneModel.Width/2);
                        }

                        // add the final width
                        widthValue.Add(laneModel.Width/2);
                    }

                    // set the rndf path start point to be the point we used
                    rndfPathStartPoint = rndfNextPoint;
                }

                // for now, calculate the left and right bounds the same way we do for the path lane model
                // TODO: figure out if we want to do this more intelligently using knowledge of the lane model uncertainty
                LinePath leftBound = centerLine.ShiftLateral(widthValue.ToArray());
                // get the next shifts
                for (int i = 0; i < widthValue.Count; i++) { widthValue[i] = -widthValue[i]; }
                LinePath rightBound = centerLine.ShiftLateral(widthValue.ToArray());

                // build the final lane model
                finalLaneModel = new CombinedLaneModel(centerLine, leftBound, rightBound, laneModel.Width, rndfPathTimestamp);
            }

            SendLaneModelToUI(finalLaneModel, rndfPathTimestamp);

            // output the fit result
            return finalLaneModel;
        }
        /// <summary>
        /// Plans over the zone
        /// </summary>
        /// <param name="planningState"></param>
        /// <param name="navigationalPlan"></param>
        /// <param name="vehicleState"></param>
        /// <param name="vehicles"></param>
        /// <param name="obstacles"></param>
        /// <param name="blockages"></param>
        /// <returns></returns>
        public Maneuver Plan(IState planningState, INavigationalPlan navigationalPlan,
                             VehicleState vehicleState, SceneEstimatorTrackedClusterCollection vehicles,
                             SceneEstimatorUntrackedClusterCollection obstacles, List <ITacticalBlockage> blockages)
        {
            #region Zone Travelling State

            if (planningState is ZoneTravelingState)
            {
                // check blockages

                /*if (blockages != null && blockages.Count > 0 && blockages[0] is ZoneBlockage)
                 * {
                 *      // create the blockage state
                 *      EncounteredBlockageState ebs = new EncounteredBlockageState(blockages[0], CoreCommon.CorePlanningState);
                 *
                 *      // go to a blockage handling tactical
                 *      return new Maneuver(new NullBehavior(), ebs, TurnDecorators.NoDecorators, vehicleState.Timestamp);
                 * }*/

                // cast state
                ZoneState zs = (ZoneState)planningState;

                // plan over state and zone
                ZonePlan zp = (ZonePlan)navigationalPlan;

                // check zone path does not exist
                if (zp.RecommendedPath.Count < 2)
                {
                    // zone startup again
                    ZoneStartupState zss = new ZoneStartupState(zs.Zone, true);
                    return(new Maneuver(new HoldBrakeBehavior(), zss, TurnDecorators.NoDecorators, vehicleState.Timestamp));
                }

                // final path seg
                LinePath.PointOnPath endBack = zp.RecommendedPath.AdvancePoint(zp.RecommendedPath.EndPoint, -TahoeParams.VL);
                LinePath             lp      = zp.RecommendedPath.SubPath(endBack, zp.RecommendedPath.EndPoint);
                LinePath             lB      = lp.ShiftLateral(TahoeParams.T);
                LinePath             rB      = lp.ShiftLateral(-TahoeParams.T);

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));

                // get speed command
                ScalarSpeedCommand sc = new ScalarSpeedCommand(2.24);

                // Behavior
                Behavior b = new ZoneTravelingBehavior(zp.Zone.ZoneId, zp.Zone.Perimeter.PerimeterPolygon, zp.Zone.StayOutAreas.ToArray(),
                                                       sc, zp.RecommendedPath, lB, rB);

                // maneuver
                return(new Maneuver(b, CoreCommon.CorePlanningState, TurnDecorators.NoDecorators, vehicleState.Timestamp));
            }

            #endregion

            #region Parking State

            else if (planningState is ParkingState)
            {
                // get state
                ParkingState ps = (ParkingState)planningState;

                // determine stay out areas to use
                List <Polygon> stayOuts = new List <Polygon>();
                foreach (Polygon p in ps.Zone.StayOutAreas)
                {
                    if (!p.IsInside(ps.ParkingSpot.NormalWaypoint.Position) && !p.IsInside(ps.ParkingSpot.Checkpoint.Position))
                    {
                        stayOuts.Add(p);
                    }
                }

                LinePath rB = ps.ParkingSpot.GetRightBound();
                LinePath lB = ps.ParkingSpot.GetLeftBound();

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));

                // create behavior
                ZoneParkingBehavior zpb = new ZoneParkingBehavior(ps.Zone.ZoneId, ps.Zone.Perimeter.PerimeterPolygon, stayOuts.ToArray(), new ScalarSpeedCommand(2.24),
                                                                  ps.ParkingSpot.GetSpotPath(), lB, rB, ps.ParkingSpot.SpotId, 1.0);

                // maneuver
                return(new Maneuver(zpb, ps, TurnDecorators.NoDecorators, vehicleState.Timestamp));
            }

            #endregion

            #region Pulling Out State

            else if (planningState is PullingOutState)
            {
                // get state
                PullingOutState pos = (PullingOutState)planningState;

                // plan over state and zone
                ZonePlan zp = (ZonePlan)navigationalPlan;

                // final path seg
                Coordinates endVec  = zp.RecommendedPath[0] - zp.RecommendedPath[1];
                Coordinates endBack = zp.RecommendedPath[0] + endVec.Normalize(TahoeParams.VL * 2.0);
                LinePath    rp      = new LinePath(new Coordinates[] { pos.ParkingSpot.Checkpoint.Position, pos.ParkingSpot.NormalWaypoint.Position,
                                                                       zp.RecommendedPath[0], endBack });
                LinePath lp = new LinePath(new Coordinates[] { zp.RecommendedPath[0], endBack });
                LinePath lB = lp.ShiftLateral(TahoeParams.T * 2.0);
                LinePath rB = lp.ShiftLateral(-TahoeParams.T * 2.0);

                // add to info
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(lB, ArbiterInformationDisplayObjectType.leftBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rB, ArbiterInformationDisplayObjectType.rightBound));
                CoreCommon.CurrentInformation.DisplayObjects.Add(new ArbiterInformationDisplayObject(rp, ArbiterInformationDisplayObjectType.leftBound));

                // determine stay out areas to use
                List <Polygon> stayOuts = new List <Polygon>();
                foreach (Polygon p in pos.Zone.StayOutAreas)
                {
                    if (!p.IsInside(pos.ParkingSpot.NormalWaypoint.Position) && !p.IsInside(pos.ParkingSpot.Checkpoint.Position))
                    {
                        stayOuts.Add(p);
                    }
                }

                // get speed command
                ScalarSpeedCommand sc = new ScalarSpeedCommand(2.24);

                // Behavior
                Behavior b = new ZoneParkingPullOutBehavior(zp.Zone.ZoneId, zp.Zone.Perimeter.PerimeterPolygon, stayOuts.ToArray(),
                                                            sc, pos.ParkingSpot.GetSpotPath(), pos.ParkingSpot.GetLeftBound(), pos.ParkingSpot.GetRightBound(), pos.ParkingSpot.SpotId,
                                                            rp, lB, rB);

                // maneuver
                return(new Maneuver(b, pos, TurnDecorators.NoDecorators, vehicleState.Timestamp));
            }

            #endregion

            #region Zone Startup State

            else if (planningState is ZoneStartupState)
            {
                // state
                ZoneStartupState zss = (ZoneStartupState)planningState;

                // get the zone
                ArbiterZone az = zss.Zone;

                // navigational edge
                INavigableNode inn = CoreCommon.RoadNetwork.ArbiterWaypoints[CoreCommon.Mission.MissionCheckpoints.Peek().WaypointId];

                // check over all the parking spaces
                foreach (ArbiterParkingSpot aps in az.ParkingSpots)
                {
                    // inside both parts of spot
                    if ((vehicleState.VehiclePolygon.IsInside(aps.NormalWaypoint.Position) && vehicleState.VehiclePolygon.IsInside(aps.Checkpoint.Position)) ||
                        (vehicleState.VehiclePolygon.IsInside(aps.NormalWaypoint.Position)))
                    {
                        // want to just park in it again
                        return(new Maneuver(new HoldBrakeBehavior(), new ParkingState(az, aps), TurnDecorators.NoDecorators, vehicleState.Timestamp));
                    }
                }

                Polygon forwardPolygon = vehicleState.ForwardPolygon;
                Polygon rearPolygon    = vehicleState.RearPolygon;

                Navigator nav = CoreCommon.Navigation;
                List <ZoneNavigationEdgeSort> forwardForward             = new List <ZoneNavigationEdgeSort>();
                List <ZoneNavigationEdgeSort> reverseReverse             = new List <ZoneNavigationEdgeSort>();
                List <ZoneNavigationEdgeSort> perpendicularPerpendicular = new List <ZoneNavigationEdgeSort>();
                List <ZoneNavigationEdgeSort> allEdges         = new List <ZoneNavigationEdgeSort>();
                List <ZoneNavigationEdgeSort> allEdgesNoLimits = new List <ZoneNavigationEdgeSort>();
                foreach (NavigableEdge ne in az.NavigableEdges)
                {
                    if (!(ne.End is ArbiterParkingSpotWaypoint) && !(ne.Start is ArbiterParkingSpotWaypoint))
                    {
                        // get distance to edge
                        LinePath lp      = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                        double   distTmp = lp.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);

                        // get direction along segment
                        DirectionAlong da = vehicleState.DirectionAlongSegment(lp);

                        // check dist reasonable
                        if (distTmp > TahoeParams.VL)
                        {
                            // zone navigation edge sort item
                            ZoneNavigationEdgeSort znes = new ZoneNavigationEdgeSort(distTmp, ne, lp);

                            // add to lists
                            if (da == DirectionAlong.Forwards &&
                                (forwardPolygon.IsInside(ne.Start.Position) || forwardPolygon.IsInside(ne.End.Position)))
                            {
                                forwardForward.Add(znes);
                            }

                            /*else if (da == DirectionAlong.Perpendicular &&
                             *      !(forwardPolygon.IsInside(ne.Start.Position) || forwardPolygon.IsInside(ne.End.Position)) &&
                             *      !(rearPolygon.IsInside(ne.Start.Position) || rearPolygon.IsInside(ne.End.Position)))
                             *      perpendicularPerpendicular.Add(znes);
                             * else if (rearPolygon.IsInside(ne.Start.Position) || rearPolygon.IsInside(ne.End.Position))
                             *      reverseReverse.Add(znes);*/

                            // add to all edges
                            allEdges.Add(znes);
                        }
                    }
                }

                // sort by distance
                forwardForward.Sort();
                reverseReverse.Sort();
                perpendicularPerpendicular.Sort();
                allEdges.Sort();

                ZoneNavigationEdgeSort bestZnes = null;

                for (int i = 0; i < allEdges.Count; i++)
                {
                    // get line to initial
                    Line          toInitial  = new Line(vehicleState.Front, allEdges[i].edge.Start.Position);
                    Line          toFinal    = new Line(vehicleState.Front, allEdges[i].edge.End.Position);
                    bool          intersects = false;
                    Coordinates[] interPts;
                    foreach (Polygon sop in az.StayOutAreas)
                    {
                        if (!intersects &&
                            (sop.Intersect(toInitial, out interPts) && sop.Intersect(toFinal, out interPts)))
                        {
                            intersects = true;
                        }
                    }

                    if (!intersects)
                    {
                        allEdges[i].zp       = nav.PlanZone(az, allEdges[i].edge.End, inn);
                        allEdges[i].zp.Time += vehicleState.Front.DistanceTo(allEdges[i].lp.GetClosestPoint(vehicleState.Front).Location) / 2.24;
                        ZoneNavigationEdgeSort tmpZnes = allEdges[i];
                        if ((bestZnes == null && tmpZnes.zp.RecommendedPath.Count > 1) ||
                            (bestZnes != null && tmpZnes.zp.RecommendedPath.Count > 1 && tmpZnes.zp.Time < bestZnes.zp.Time))
                        {
                            bestZnes = tmpZnes;
                        }
                    }

                    if (i > allEdges.Count / 2 && bestZnes != null)
                    {
                        break;
                    }
                }

                if (bestZnes != null)
                {
                    ArbiterOutput.Output("Found good edge to start in zone");
                    return(new Maneuver(new HoldBrakeBehavior(), new ZoneOrientationState(az, bestZnes.edge), TurnDecorators.NoDecorators, vehicleState.Timestamp));
                }
                else
                {
                    ArbiterOutput.Output("Could not find good edge to start, choosing random not blocked");

                    List <ZoneNavigationEdgeSort> okZnes = new List <ZoneNavigationEdgeSort>();
                    foreach (NavigableEdge tmpOk in az.NavigableEdges)
                    {
                        // get line to initial
                        LinePath edgePath = new LinePath(new Coordinates[] { tmpOk.Start.Position, tmpOk.End.Position });
                        double   dist     = edgePath.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);
                        ZoneNavigationEdgeSort tmpZnes = new ZoneNavigationEdgeSort(dist, tmpOk, edgePath);
                        tmpZnes.zp       = nav.PlanZone(az, tmpZnes.edge.End, inn);
                        tmpZnes.zp.Time += vehicleState.Front.DistanceTo(tmpZnes.lp.GetClosestPoint(vehicleState.Front).Location) / 2.24;
                        if (tmpZnes.zp.RecommendedPath.Count >= 2)
                        {
                            okZnes.Add(tmpZnes);
                        }
                    }

                    if (okZnes.Count == 0)
                    {
                        okZnes = allEdges;
                    }
                    else
                    {
                        okZnes.Sort();
                    }

                    // get random close edge
                    Random rand   = new Random();
                    int    chosen = rand.Next(Math.Min(5, okZnes.Count));

                    // get closest edge not part of a parking spot, get on it
                    NavigableEdge closest = okZnes[chosen].edge;                    //null;
                    //double distance = Double.MaxValue;

                    /*foreach (NavigableEdge ne in az.NavigableEdges)
                     * {
                     *      if (!(ne.End is ArbiterParkingSpotWaypoint) && !(ne.Start is ArbiterParkingSpotWaypoint))
                     *      {
                     *              // get distance to edge
                     *              LinePath lp = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                     *              double distTmp = lp.GetClosestPoint(vehicleState.Front).Location.DistanceTo(vehicleState.Front);
                     *              if (closest == null || distTmp < distance)
                     *              {
                     *                      closest = ne;
                     *                      distance = distTmp;
                     *              }
                     *      }
                     * }*/
                    return(new Maneuver(new HoldBrakeBehavior(), new ZoneOrientationState(az, closest), TurnDecorators.NoDecorators, vehicleState.Timestamp));
                }
            }

            #endregion

            #region Unknown

            else
            {
                // non-handled state
                throw new ArgumentException("Unknown state", "CoreCommon.CorePlanningState");
            }

            #endregion
        }
예제 #12
0
        /// <summary>
        /// Gets primary maneuver given our position and the turn we are traveling upon
        /// </summary>
        /// <param name="vehicleState"></param>
        /// <returns></returns>
        public Maneuver PrimaryManeuver(VehicleState vehicleState, List <ITacticalBlockage> blockages, TurnState turnState)
        {
            #region Check are planning over the correct turn

            if (CoreCommon.CorePlanningState is TurnState)
            {
                TurnState ts = (TurnState)CoreCommon.CorePlanningState;
                if (this.turn == null || !this.turn.Equals(ts.Interconnect))
                {
                    this.turn           = ts.Interconnect;
                    this.forwardMonitor = new TurnForwardMonitor(ts.Interconnect, null);
                }
                else if (this.forwardMonitor.turn == null || !this.forwardMonitor.turn.Equals(ts.Interconnect))
                {
                    this.forwardMonitor = new TurnForwardMonitor(ts.Interconnect, null);
                }
            }

            #endregion

            #region Blockages

            // check blockages
            if (blockages != null && blockages.Count > 0 && blockages[0] is TurnBlockage)
            {
                // create the blockage state
                EncounteredBlockageState ebs = new EncounteredBlockageState(blockages[0], CoreCommon.CorePlanningState);

                // check not at highest level already
                if (turnState.Saudi != SAUDILevel.L1 || turnState.UseTurnBounds)
                {
                    // check not from a dynamicly moving vehicle
                    if (blockages[0].BlockageReport.BlockageType != BlockageType.Dynamic ||
                        (TacticalDirector.ValidVehicles.ContainsKey(blockages[0].BlockageReport.TrackID) &&
                         TacticalDirector.ValidVehicles[blockages[0].BlockageReport.TrackID].IsStopped))
                    {
                        // go to a blockage handling tactical
                        return(new Maneuver(new NullBehavior(), ebs, TurnDecorators.NoDecorators, vehicleState.Timestamp));
                    }
                    else
                    {
                        ArbiterOutput.Output("Turn blockage reported for moving vehicle, ignoring");
                    }
                }
                else
                {
                    ArbiterOutput.Output("Turn blockage, but recovery escalation already at highest state, ignoring report");
                }
            }

            #endregion

            #region Intersection Check

            if (!this.CanGo(vehicleState))
            {
                if (turn.FinalGeneric is ArbiterWaypoint)
                {
                    TravelingParameters tp = this.GetParameters(0.0, 0.0, (ArbiterWaypoint)turn.FinalGeneric, vehicleState, false);
                    return(new Maneuver(tp.Behavior, CoreCommon.CorePlanningState, tp.NextState.DefaultStateDecorators, vehicleState.Timestamp));
                }
                else
                {
                    // get turn params
                    LinePath finalPath;
                    LineList leftLL;
                    LineList rightLL;
                    IntersectionToolkit.ZoneTurnInfo(this.turn, (ArbiterPerimeterWaypoint)this.turn.FinalGeneric, out finalPath, out leftLL, out rightLL);

                    // hold brake
                    IState       nextState = new TurnState(this.turn, this.turn.TurnDirection, null, finalPath, leftLL, rightLL, new ScalarSpeedCommand(0.0));
                    TurnBehavior b         = new TurnBehavior(null, finalPath, leftLL, rightLL, new ScalarSpeedCommand(0.0), this.turn.InterconnectId);
                    return(new Maneuver(b, CoreCommon.CorePlanningState, nextState.DefaultStateDecorators, vehicleState.Timestamp));
                }
            }

            #endregion

            #region Final is Lane Waypoint

            if (turn.FinalGeneric is ArbiterWaypoint)
            {
                // final point
                ArbiterWaypoint final = (ArbiterWaypoint)turn.FinalGeneric;

                // plan down entry lane
                RoadPlan rp = navigation.PlanNavigableArea(final.Lane, final.Position,
                                                           CoreCommon.RoadNetwork.ArbiterWaypoints[CoreCommon.Mission.MissionCheckpoints.Peek().WaypointId], new List <ArbiterWaypoint>());

                // point of interest downstream
                DownstreamPointOfInterest dpoi = rp.BestPlan.laneWaypointOfInterest;

                // get path this represents
                List <Coordinates> pathCoordinates = new List <Coordinates>();
                pathCoordinates.Add(vehicleState.Position);
                foreach (ArbiterWaypoint aw in final.Lane.WaypointsInclusive(final, final.Lane.WaypointList[final.Lane.WaypointList.Count - 1]))
                {
                    pathCoordinates.Add(aw.Position);
                }
                LinePath lp = new LinePath(pathCoordinates);

                // list of all parameterizations
                List <TravelingParameters> parameterizations = new List <TravelingParameters>();

                // get lane navigation parameterization
                TravelingParameters navigationParameters = this.NavigationParameterization(vehicleState, dpoi, final, lp);
                parameterizations.Add(navigationParameters);

                // update forward tracker and get vehicle parameterizations if forward vehicle exists
                this.forwardMonitor.Update(vehicleState, final, lp);
                if (this.forwardMonitor.ShouldUseForwardTracker())
                {
                    // get vehicle parameterization
                    TravelingParameters vehicleParameters = this.VehicleParameterization(vehicleState, lp, final);
                    parameterizations.Add(vehicleParameters);
                }

                // sort and return funal
                parameterizations.Sort();

                // get the final behavior
                TurnBehavior tb = (TurnBehavior)parameterizations[0].Behavior;

                // get vehicles to ignore
                tb.VehiclesToIgnore = this.forwardMonitor.VehiclesToIgnore;

                // add persistent information about saudi level
                if (turnState.Saudi == SAUDILevel.L1)
                {
                    tb.Decorators = new List <BehaviorDecorator>(tb.Decorators.ToArray());
                    tb.Decorators.Add(new ShutUpAndDoItDecorator(SAUDILevel.L1));
                }

                // add persistent information about turn bounds
                if (!turnState.UseTurnBounds)
                {
                    tb.LeftBound  = null;
                    tb.RightBound = null;
                }

                //  return the behavior
                return(new Maneuver(tb, CoreCommon.CorePlanningState, tb.Decorators, vehicleState.Timestamp));
            }

            #endregion

            #region Final is Zone Waypoint

            else if (turn.FinalGeneric is ArbiterPerimeterWaypoint)
            {
                // get inteconnect path
                Coordinates entryVec = ((ArbiterPerimeterWaypoint)turn.FinalGeneric).Perimeter.PerimeterPolygon.BoundingCircle.center -
                                       turn.FinalGeneric.Position;
                entryVec = entryVec.Normalize(TahoeParams.VL / 2.0);
                LinePath ip = new LinePath(new Coordinates[] { turn.InitialGeneric.Position, turn.FinalGeneric.Position, entryVec + this.turn.FinalGeneric.Position });

                // get distance from end
                double d = ip.DistanceBetween(
                    ip.GetClosestPoint(vehicleState.Front),
                    ip.EndPoint);

                // get speed command
                SpeedCommand sc = null;
                if (d < TahoeParams.VL)
                {
                    sc = new StopAtDistSpeedCommand(d);
                }
                else
                {
                    sc = new ScalarSpeedCommand(SpeedTools.GenerateSpeed(d - TahoeParams.VL, 1.7, turn.MaximumDefaultSpeed));
                }

                // final perimeter waypoint
                ArbiterPerimeterWaypoint apw = (ArbiterPerimeterWaypoint)this.turn.FinalGeneric;

                // get turn params
                LinePath finalPath;
                LineList leftLL;
                LineList rightLL;
                IntersectionToolkit.ZoneTurnInfo(this.turn, (ArbiterPerimeterWaypoint)this.turn.FinalGeneric, out finalPath, out leftLL, out rightLL);

                // hold brake
                IState       nextState = new TurnState(this.turn, this.turn.TurnDirection, null, finalPath, leftLL, rightLL, sc);
                TurnBehavior tb        = new TurnBehavior(null, finalPath, leftLL, rightLL, sc, null, new List <int>(), this.turn.InterconnectId);

                // add persistent information about saudi level
                if (turnState.Saudi == SAUDILevel.L1)
                {
                    tb.Decorators = new List <BehaviorDecorator>(tb.Decorators.ToArray());
                    tb.Decorators.Add(new ShutUpAndDoItDecorator(SAUDILevel.L1));
                }

                // add persistent information about turn bounds
                if (!turnState.UseTurnBounds)
                {
                    tb.LeftBound  = null;
                    tb.RightBound = null;
                }

                // return maneuver
                return(new Maneuver(tb, CoreCommon.CorePlanningState, tb.Decorators, vehicleState.Timestamp));
            }

            #endregion

            #region Unknown

            else
            {
                throw new Exception("unrecognized type: " + turn.FinalGeneric.ToString());
            }

            #endregion
        }
예제 #13
0
        /// <summary>
        /// Check if we can go
        /// </summary>
        /// <param name="vs"></param>
        private bool CanGo(VehicleState vs)
        {
            #region Moving Vehicles Inside Turn

            // check if we can still go through this turn
            if (TacticalDirector.VehicleAreas.ContainsKey(this.turn))
            {
                // get the subpath of the interconnect we care about
                LinePath.PointOnPath frontPos  = this.turn.InterconnectPath.GetClosestPoint(vs.Front);
                LinePath             aiSubpath = this.turn.InterconnectPath.SubPath(frontPos, this.turn.InterconnectPath.EndPoint);

                if (aiSubpath.PathLength > 4.0)
                {
                    aiSubpath = aiSubpath.SubPath(aiSubpath.StartPoint, aiSubpath.PathLength - 2.0);

                    // get vehicles
                    List <VehicleAgent> turnVehicles = TacticalDirector.VehicleAreas[this.turn];

                    // loop vehicles
                    foreach (VehicleAgent va in turnVehicles)
                    {
                        // check if inside turn
                        LinePath.PointOnPath vaPop = aiSubpath.GetClosestPoint(va.ClosestPosition);
                        if (!va.IsStopped && this.turn.TurnPolygon.IsInside(va.ClosestPosition) && !vaPop.Equals(aiSubpath.StartPoint) && !vaPop.Equals(aiSubpath.EndPoint))
                        {
                            ArbiterOutput.Output("Vehicle seen inside our turn: " + va.ToString() + ", stopping");
                            return(false);
                        }
                    }
                }
            }

            #endregion

            // test if this turn is part of an intersection
            if (CoreCommon.RoadNetwork.IntersectionLookup.ContainsKey(this.turn.InitialGeneric.AreaSubtypeWaypointId))
            {
                // intersection
                ArbiterIntersection inter = CoreCommon.RoadNetwork.IntersectionLookup[this.turn.InitialGeneric.AreaSubtypeWaypointId];

                // check if priority lanes exist for this interconnect
                if (inter.PriorityLanes.ContainsKey(this.turn))
                {
                    // get all the default priority lanes
                    List <IntersectionInvolved> priorities = inter.PriorityLanes[this.turn];

                    // get the subpath of the interconnect we care about
                    //LinePath.PointOnPath frontPos = this.turn.InterconnectPath.GetClosestPoint(vs.Front);
                    LinePath aiSubpath = new LinePath(new List <Coordinates>(new Coordinates[] { vs.Front, this.turn.FinalGeneric.Position }));                   //this.turn.InterconnectPath.SubPath(frontPos, this.turn.InterconnectPath.EndPoint);

                    // check if path ended
                    if (aiSubpath.Count < 2)
                    {
                        return(true);
                    }

                    // determine all of the new priority lanes
                    List <IntersectionInvolved> updatedPriorities = new List <IntersectionInvolved>();

                    #region Determine new priority areas given position

                    // loop through old priorities
                    foreach (IntersectionInvolved ii in priorities)
                    {
                        // check ii lane
                        if (ii.Area is ArbiterLane)
                        {
                            #region Lane Intersects Turn Path w/ Point of No Return

                            // check if the waypoint is not the last waypoint in the lane
                            if (ii.Exit == null || ((ArbiterWaypoint)ii.Exit).NextPartition != null)
                            {
                                // check where line intersects path
                                Coordinates?intersect = this.LaneIntersectsPath(ii, aiSubpath, this.turn.FinalGeneric);

                                // check for an intersection
                                if (intersect.HasValue)
                                {
                                    // distance to intersection
                                    double distanceToIntersection = (intersect.Value.DistanceTo(vs.Front) + ((ArbiterLane)ii.Area).LanePath().GetClosestPoint(vs.Front).Location.DistanceTo(vs.Front)) / 2.0;

                                    // determine if we can stop before or after the intersection
                                    double distanceToStoppage = RoadToolkit.DistanceToStop(CoreCommon.Communications.GetVehicleSpeed().Value);

                                    // check dist to intersection > distance to stoppage
                                    if (distanceToIntersection > distanceToStoppage)
                                    {
                                        // add updated priority
                                        updatedPriorities.Add(new IntersectionInvolved(new ArbiterWaypoint(intersect.Value, new ArbiterWaypointId(0, ((ArbiterLane)ii.Area).LaneId)),
                                                                                       ii.Area, ArbiterTurnDirection.Straight));
                                    }
                                    else
                                    {
                                        ArbiterOutput.Output("Passed point of No Return for Lane: " + ii.Area.ToString());
                                    }
                                }
                            }

                            #endregion

                            // we know there is an exit and it is the last waypoint of the segment, fuxk!
                            else
                            {
                                #region Turns Intersect

                                // get point to look at if exists
                                ArbiterInterconnect interconnect;
                                Coordinates?        intersect = this.TurnIntersects(aiSubpath, ii.Exit, out interconnect);

                                // check for the intersect
                                if (intersect.HasValue)
                                {
                                    ArbiterLane al = (ArbiterLane)ii.Area;
                                    LinePath    lp = al.LanePath().SubPath(al.LanePath().StartPoint, al.LanePath().GetClosestPoint(ii.Exit.Position));
                                    lp.Add(interconnect.InterconnectPath.EndPoint.Location);

                                    // get our time to the intersection point
                                    //double ourTime = Math.Min(4.0, Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value) < 0.001 ? aiSubpath.PathLength / 1.0 : aiSubpath.PathLength / Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value));

                                    // get our time to the intersection point
                                    double ourSpeed         = Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value);
                                    double stoppedTime      = ourSpeed < 1.0 ? 1.5 : 0.0;
                                    double extraTime        = 1.5;
                                    double interconnectTime = aiSubpath.PathLength / this.turn.MaximumDefaultSpeed;
                                    double ourTime          = Math.Min(6.5, stoppedTime + extraTime + interconnectTime);

                                    // get closest vehicle in that lane to the intersection
                                    List <VehicleAgent> toLook = new List <VehicleAgent>();
                                    if (TacticalDirector.VehicleAreas.ContainsKey(ii.Area))
                                    {
                                        foreach (VehicleAgent tmpVa in TacticalDirector.VehicleAreas[ii.Area])
                                        {
                                            double upstreamDist = al.DistanceBetween(tmpVa.ClosestPosition, ii.Exit.Position);
                                            if (upstreamDist > 0 && tmpVa.PassedDelayedBirth)
                                            {
                                                toLook.Add(tmpVa);
                                            }
                                        }
                                    }
                                    if (TacticalDirector.VehicleAreas.ContainsKey(interconnect))
                                    {
                                        toLook.AddRange(TacticalDirector.VehicleAreas[interconnect]);
                                    }

                                    // check length of stuff to look at
                                    if (toLook.Count > 0)
                                    {
                                        foreach (VehicleAgent va in toLook)
                                        {
                                            // distance along path to location of intersect
                                            double distToIntersect = lp.DistanceBetween(lp.GetClosestPoint(va.ClosestPosition), lp.GetClosestPoint(aiSubpath.GetClosestPoint(va.ClosestPosition).Location));

                                            double speed  = va.Speed == 0.0 ? 0.01 : va.Speed;
                                            double vaTime = distToIntersect / Math.Abs(speed);
                                            if (vaTime > 0 && vaTime < ourTime)
                                            {
                                                ArbiterOutput.Output("va: " + va.ToString() + " CollisionTimer: " + vaTime.ToString("f2") + " < TimeUs: " + ourTime.ToString("F2") + ", NOGO");
                                                return(false);
                                            }
                                        }
                                    }
                                }

                                #endregion
                            }
                        }
                    }

                    #endregion

                    #region Updated Priority Intersection Code

                    // loop through updated priorities
                    bool updatedPrioritiesClear = true;
                    foreach (IntersectionInvolved ii in updatedPriorities)
                    {
                        // lane
                        ArbiterLane al = (ArbiterLane)ii.Area;

                        // get our time to the intersection point
                        double ourSpeed         = Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value);
                        double stoppedTime      = ourSpeed < 1.0 ? 1.5 : 0.0;
                        double extraTime        = 1.5;
                        double interconnectTime = aiSubpath.PathLength / this.turn.MaximumDefaultSpeed;
                        double ourTime          = Math.Min(6.5, stoppedTime + extraTime + interconnectTime);

                        // double outTime = Math.Min(4.0, Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value) < 0.001 ? aiSubpath.PathLength / 1.0 : aiSubpath.PathLength / Math.Abs(CoreCommon.Communications.GetVehicleSpeed().Value));

                        // get closest vehicle in that lane to the intersection
                        if (TacticalDirector.VehicleAreas.ContainsKey(ii.Area))
                        {
                            // get lane vehicles
                            List <VehicleAgent> vas = TacticalDirector.VehicleAreas[ii.Area];

                            // determine for all
                            VehicleAgent closestLaneVa     = null;
                            double       closestDistanceVa = Double.MaxValue;
                            double       closestTime       = Double.MaxValue;
                            foreach (VehicleAgent testVa in vas)
                            {
                                // check upstream
                                double distance = al.DistanceBetween(testVa.ClosestPosition, ii.Exit.Position);

                                // get speed
                                double speed = testVa.Speed;
                                double time  = testVa.StateMonitor.Observed.speedValid ? distance / Math.Abs(speed) : distance / al.Way.Segment.SpeedLimits.MaximumSpeed;

                                // check distance > 0
                                if (distance > 0)
                                {
                                    // check if closer or none other exists
                                    if (closestLaneVa == null || time < closestTime)
                                    {
                                        closestLaneVa     = testVa;
                                        closestDistanceVa = distance;
                                        closestTime       = time;
                                    }
                                }
                            }

                            // check if closest exists
                            if (closestLaneVa != null)
                            {
                                // set va
                                VehicleAgent va       = closestLaneVa;
                                double       distance = closestDistanceVa;

                                // check dist and birth time
                                if (distance > 0 && va.PassedDelayedBirth)
                                {
                                    // check time
                                    double speed = va.Speed == 0.0 ? 0.01 : va.Speed;
                                    double time  = va.StateMonitor.Observed.speedValid ? distance / Math.Abs(speed) : distance / al.Way.Segment.SpeedLimits.MaximumSpeed;

                                    // too close
                                    if (!al.LanePolygon.IsInside(CoreCommon.Communications.GetVehicleState().Front) &&
                                        distance < 25 && (!va.StateMonitor.Observed.speedValid || !va.StateMonitor.Observed.isStopped) &&
                                        CoreCommon.Communications.GetVehicleState().Front.DistanceTo(va.ClosestPosition) < 20)
                                    {
                                        ArbiterOutput.Output("Turn, NOGO, Lane: " + al.ToString() + " vehicle: " + va.ToString() + " possibly moving to close, stopping");
                                        //return false;
                                        updatedPrioritiesClear = false;
                                        return(false);
                                    }
                                    else if (time > 0 && time < ourTime)
                                    {
                                        ArbiterOutput.Output("Turn, NOGO, Lane: " + al.ToString() + ", va: " + va.ToString() + ", stopped: " + va.IsStopped.ToString() + ", timeUs: " + ourTime.ToString("f2") + ", timeThem: " + time.ToString("f2"));
                                        //return false;
                                        updatedPrioritiesClear = false;
                                        return(false);
                                    }
                                    else
                                    {
                                        ArbiterOutput.Output("Turn, CANGO, Lane: " + al.ToString() + ", va: " + va.ToString() + ", stopped: " + va.IsStopped.ToString() + ", timeUs: " + ourTime.ToString("f2") + ", timeThem: " + time.ToString("f2"));
                                        //return true;
                                    }
                                }
                            }
                            else
                            {
                                ArbiterOutput.Output("Turn, CANGO, Lane: " + al.ToString() + " has no traffic vehicles");
                            }
                        }
                    }
                    return(updatedPrioritiesClear);

                    #endregion
                }
            }

            // fall through fine to go
            ArbiterOutput.Output("In Turn, CAN GO, Clear of vehicles upstream");
            return(true);
        }
예제 #14
0
        public override void Process(object param)
        {
            // inform the base that we're beginning processing
            if (!BeginProcess())
            {
                return;
            }

            // check if we were given a parameter
            if (param != null && param is StayInLaneBehavior)
            {
                HandleBehavior((StayInLaneBehavior)param);
            }

            if (reverseGear)
            {
                ProcessReverse();
                return;
            }

            // figure out the planning distance
            double planningDistance = GetPlanningDistance();

            if (sparse && planningDistance > 10)
            {
                planningDistance = 10;
            }

            double?boundStartDistMin = null;
            double?boundEndDistMax   = null;

            if (laneID != null && Services.RoadNetwork != null)
            {
                ArbiterLane          lane          = Services.RoadNetwork.ArbiterSegments[laneID.SegmentId].Lanes[laneID];
                AbsolutePose         pose          = Services.StateProvider.GetAbsolutePose();
                ArbiterLanePartition partition     = lane.GetClosestPartition(pose.xy);
                LinePath             partitionPath = partition.UserPartitionPath;
                LinePath.PointOnPath closestPoint  = partitionPath.GetClosestPoint(pose.xy);
                double remainingDist = planningDistance;
                double totalDist     = partitionPath.DistanceBetween(closestPoint, partitionPath.EndPoint);
                remainingDist -= totalDist;

                if (sparse)
                {
                    // walk ahead and determine where sparsity ends
                    bool nonSparseFound = false;

                    while (remainingDist > 0)
                    {
                        // get the next partition
                        partition = partition.Final.NextPartition;
                        if (partition == null)
                        {
                            break;
                        }

                        if (partition.Type != PartitionType.Sparse)
                        {
                            nonSparseFound = true;
                            break;
                        }
                        else
                        {
                            double dist = partition.UserPartitionPath.PathLength;
                            totalDist     += dist;
                            remainingDist -= dist;
                        }
                    }

                    if (nonSparseFound)
                    {
                        boundStartDistMin = totalDist;
                    }
                }
                else
                {
                    // determine if there is a sparse segment upcoming
                    bool sparseFound = false;
                    while (remainingDist > 0)
                    {
                        partition = partition.Final.NextPartition;

                        if (partition == null)
                        {
                            break;
                        }

                        if (partition.Type == PartitionType.Sparse)
                        {
                            sparseFound = true;
                            break;
                        }
                        else
                        {
                            double dist = partition.Length;
                            totalDist     += dist;
                            remainingDist -= dist;
                        }
                    }

                    if (sparseFound)
                    {
                        boundEndDistMax = totalDist;
                        sparse          = true;
                    }
                }
            }

            BehaviorManager.TraceSource.TraceEvent(TraceEventType.Information, 0, "in stay in lane, planning distance {0}", planningDistance);

            // update the rndf path
            RelativeTransform relTransfrom    = Services.RelativePose.GetTransform(behaviorTimestamp, curTimestamp);
            LinePath          curRndfPath     = rndfPath.Transform(relTransfrom);
            ILaneModel        centerLaneModel = Services.RoadModelProvider.GetLaneModel(curRndfPath, rndfPathWidth + extraWidth, curTimestamp, numLanesLeft, numLanesRight);

            double avoidanceExtra = sparse ? 5 : 7.5;

            LinePath centerLine, leftBound, rightBound;

            if (boundEndDistMax != null || boundStartDistMin != null)
            {
                LinearizeStayInLane(centerLaneModel, planningDistance + avoidanceExtra, null, boundEndDistMax, boundStartDistMin, null, curTimestamp, out centerLine, out leftBound, out rightBound);
            }
            else
            {
                LinearizeStayInLane(centerLaneModel, planningDistance + avoidanceExtra, curTimestamp, out centerLine, out leftBound, out rightBound);
            }
            BehaviorManager.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "in stay in lane, center line count: {0}, left bound count: {1}, right bound count: {2}", centerLine.Count, leftBound.Count, rightBound.Count);

            // calculate time to collision of opposing obstacle if it exists
            LinePath targetLine   = centerLine;
            double   targetWeight = default_lane_alpha_w;

            if (sparse)
            {
                targetWeight = 0.00000025;
            }
            else if (oncomingVehicleExists)
            {
                double shiftDist = -(TahoeParams.T + 1);
                targetLine   = centerLine.ShiftLateral(shiftDist);
                targetWeight = 0.01;
            }
            else if (opposingLaneVehicleExists)
            {
                double timeToCollision = opposingLaneVehicleDist / (Math.Abs(vs.speed) + Math.Abs(opposingLaneVehicleSpeed));
                Services.Dataset.ItemAs <double>("time to collision").Add(timeToCollision, curTimestamp);
                if (timeToCollision < 5)
                {
                    // shift to the right
                    double shiftDist = -TahoeParams.T / 2.0;
                    targetLine = centerLine.ShiftLateral(shiftDist);
                }
            }

            // set up the planning
            AddTargetPath(targetLine, targetWeight);
            if (!sparse || boundStartDistMin != null || boundEndDistMax != null)
            {
                AddLeftBound(leftBound, true);
                if (!oncomingVehicleExists)
                {
                    AddRightBound(rightBound, false);
                }
            }


            double targetDist = Math.Max(centerLine.PathLength - avoidanceExtra, planningDistance);

            smootherBasePath = centerLine.SubPath(centerLine.StartPoint, targetDist);
            maxSmootherBasePathAdvancePoint = smootherBasePath.EndPoint;

            double extraDist = (planningDistance + avoidanceExtra) - centerLine.PathLength;

            extraDist = Math.Min(extraDist, 5);

            if (extraDist > 0)
            {
                centerLine.Add(centerLine[centerLine.Count - 1] + centerLine.EndSegment.Vector.Normalize(extraDist));
            }
            avoidanceBasePath = centerLine;

            Services.UIService.PushLineList(centerLine, curTimestamp, "subpath", true);
            Services.UIService.PushLineList(leftBound, curTimestamp, "left bound", true);
            Services.UIService.PushLineList(rightBound, curTimestamp, "right bound", true);

            // get the overall max speed looking forward from our current point
            settings.maxSpeed = GetMaxSpeed(curRndfPath, curRndfPath.AdvancePoint(curRndfPath.ZeroPoint, vs.speed * TahoeParams.actuation_delay));
            // get the max speed at the end point
            settings.maxEndingSpeed = GetMaxSpeed(curRndfPath, curRndfPath.AdvancePoint(curRndfPath.ZeroPoint, planningDistance));
            useAvoidancePath        = false;
            if (sparse)
            {
                // limit to 5 mph
                laneWidthAtPathEnd = 20;
                pathAngleCheckMax  = 50;
                pathAngleMax       = 5 * Math.PI / 180.0;
                settings.maxSpeed  = Math.Min(settings.maxSpeed, 2.2352);
                maxAvoidanceBasePathAdvancePoint = avoidanceBasePath.AdvancePoint(avoidanceBasePath.EndPoint, -2);
                //maxSmootherBasePathAdvancePoint = smootherBasePath.AdvancePoint(smootherBasePath.EndPoint, -2);

                LinePath leftEdge  = RoadEdge.GetLeftEdgeLine();
                LinePath rightEdge = RoadEdge.GetRightEdgeLine();
                if (leftEdge != null)
                {
                    leftLaneBounds.Add(new Boundary(leftEdge, 0.1, 1, 100, false));
                }
                if (rightEdge != null)
                {
                    rightLaneBounds.Add(new Boundary(rightEdge, 0.1, 1, 100, false));
                }

                PlanningResult            result           = new PlanningResult();
                ISteeringCommandGenerator commandGenerator = SparseArcVoting.SparcVote(ref prevCurvature, goalPoint);
                if (commandGenerator == null)
                {
                    // we have a block, report dynamically infeasible
                    result = OnDynamicallyInfeasible(null, null);
                }
                else
                {
                    result.steeringCommandGenerator = commandGenerator;
                    result.commandLabel             = commandLabel;
                }
                Track(result, commandLabel);
                return;
            }
            else if (oncomingVehicleExists)
            {
                laneWidthAtPathEnd = 10;
            }

            BehaviorManager.TraceSource.TraceEvent(TraceEventType.Information, 0, "max speed set to {0}", settings.maxSpeed);

            disablePathAngleCheck = false;

            SmoothAndTrack(commandLabel, true);
        }
        protected virtual void DetermineBlockageDistancesAndDeceleration(IList<Obstacle> obstacles, LinePath measurePath)
        {
            LinePath.PointOnPath zeroPoint = measurePath.ZeroPoint;
            foreach (Obstacle obs in obstacles) {
                if (obs.avoidanceStatus == AvoidanceStatus.Collision && obs.collisionPoints != null && obs.collisionPoints.Count > 0) {
                    // project each obstacle point onto the measure path and get the distance
                    double minDist = double.MaxValue;
                    foreach (Coordinates pt in obs.collisionPoints) {
                        LinePath.PointOnPath closestPt = measurePath.GetClosestPoint(pt);
                        double dist = measurePath.DistanceBetween(zeroPoint, closestPt);
                        if (dist < minDist) {
                            minDist = dist;
                        }
                    }

                    // the distance will be short by the amount we expanded out the c-space polygon
                    minDist += TahoeParams.T / 2.0;
                    // decrease the distance by the length to the front bumper
                    minDist -= TahoeParams.FL;

                    obs.obstacleDistance = minDist;

                    minDist -= stopped_spacing_dist;

                    // don't have negative distance
                    if (minDist <= 0.01) {
                        minDist = 0.01;
                    }

                    double decel = vs.speed*vs.speed/(2*minDist);
                    obs.requiredDeceleration = decel;
                }
            }
        }
        public void NavigationHitTest(Coordinates c, out INavigableNode node, out NavigableEdge edge)
        {
            if (this.zt.current != null)
            {
                // Determine size of bounding box
                float scaled_offset = 1 / this.rd.WorldTransform.Scale;

                // invert the scale
                float scaled_size = DrawingUtility.cp_large_size;

                // assume that the world transform is currently applied correctly to the graphics
                RectangleF rect = new RectangleF((float)c.X - scaled_size / 2, (float)c.Y - scaled_size / 2, scaled_size, scaled_size);

                foreach (ArbiterParkingSpotWaypoint apsw in this.zt.current.ParkingSpotWaypoints.Values)
                {
                    if (apsw.Position.DistanceTo(c) < 1.0)
                    {
                        node = apsw;
                        edge = null;
                        return;
                    }
                }

                foreach (ArbiterPerimeterWaypoint apw in this.zt.current.Perimeter.PerimeterPoints.Values)
                {
                    if ((apw.IsExit || apw.IsEntry) && rect.Contains(DrawingUtility.ToPointF(apw.Position)) &&
                        (this.PreviousNode == null || !this.PreviousNode.Equals(apw)) &&
                        apw.Position.DistanceTo(c) < 1.0)
                    {
                        node = apw;
                        edge = null;
                        return;
                    }
                }

                foreach (INavigableNode inn in this.zt.current.NavigationNodes)
                {
                    if (rect.Contains(DrawingUtility.ToPointF(inn.Position)) &&
                        (this.PreviousNode == null || !this.PreviousNode.Equals(inn)) &&
                        inn.Position.DistanceTo(c) < 1.0)
                    {
                        node = inn;
                        edge = null;
                        return;
                    }
                }

                NavigableEdge closest = null;
                double distance = double.MaxValue;
                foreach (NavigableEdge ne in this.zt.current.NavigableEdges)
                {
                    LinePath lp = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                    double dist = lp.GetClosestPoint(c).Location.DistanceTo(c);
                    if (dist < rect.Width && (dist < distance || closest == null) && dist < 1.0)
                    {
                        closest = ne;
                        distance = dist;
                    }
                }

                if (closest != null)
                {
                    node = null;
                    edge = closest;
                    return;
                }
            }

            node = null;
            edge = null;
            return;
        }
        public Coordinates? ClosestPointToLine(LinePath path, VehicleState vs)
        {
            Coordinates? closest = null;
            double minDist = Double.MaxValue;

            for (int i = 0; i < this.StateMonitor.Observed.relativePoints.Length; i++)
            {
                Coordinates c = this.TransformCoordAbs(this.StateMonitor.Observed.relativePoints[i], vs);
                double dist = path.GetClosestPoint(c).Location.DistanceTo(c);

                if (!closest.HasValue)
                {
                    closest = c;
                    minDist = dist;
                }
                else if (dist < minDist)
                {
                    closest = c;
                    minDist = dist;
                }
            }

            return closest;
        }
예제 #18
0
        private LaneModelTestResult TestLane(LinePath rndfPath, CarTimestamp rndfPathTimestamp, LocalLaneModel laneModel)
        {
            // construct the result object to hold stuff
            LaneModelTestResult result = new LaneModelTestResult();

            // project the lane model's path into the rndf path's timestamp
            RelativeTransform relTransform  = Services.RelativePose.GetTransform(localRoadModel.Timestamp, rndfPathTimestamp);
            LinePath          laneModelPath = laneModel.LanePath.Transform(relTransform);

            // get the zero point of the lane model path
            LinePath.PointOnPath laneZeroPoint = laneModelPath.ZeroPoint;

            // get the first waypoint on the RNDF path
            LinePath.PointOnPath rndfZeroPoint = rndfPath.ZeroPoint;

            // get the heading of the rndf path at its zero point and the heading of the lane model at
            // the rndf's zero point
            LinePath.PointOnPath laneStartPoint   = laneModelPath.GetClosestPoint(rndfZeroPoint.Location);
            Coordinates          laneModelHeading = laneModelPath.GetSegment(laneStartPoint.Index).UnitVector;
            Coordinates          rndfHeading      = rndfPath.GetSegment(rndfZeroPoint.Index).UnitVector;
            double angle = Math.Acos(laneModelHeading.Dot(rndfHeading));

            // check if the angle is within limits for comparing offset
            if (angle < 30 * Math.PI / 180.0)
            {
                // get the deviation between lane zero point and rndf zero point
                result.rndf_deviation = rndfZeroPoint.Location.DistanceTo(laneZeroPoint.Location);
            }

            // now start check for how many waypoints are accepted
            for (int i = rndfZeroPoint.Index + 1; i < rndfPath.Count; i++)
            {
                // check the distance along the rndf path
                double rndfDistAlong = rndfPath.DistanceBetween(rndfZeroPoint, rndfPath.GetPointOnPath(i));
                // break out if we're too far along the rndf
                if (rndfDistAlong > 50)
                {
                    break;
                }

                // get the waypoint
                Coordinates waypoint = rndfPath[i];

                // project on to lane path
                LinePath.PointOnPath laneWaypoint = laneModelPath.GetClosestPoint(waypoint);

                // check if we're too far along the lane path
                double distAlong = laneModelPath.DistanceBetween(laneZeroPoint, laneWaypoint);
                if (distAlong > lane_model_max_dist || distAlong < 0)
                {
                    break;
                }

                // check if the deviation
                double dist = waypoint.DistanceTo(laneWaypoint.Location);

                // increment appropriate counts
                if (dist < lane_deviation_reject_threshold)
                {
                    result.forward_match_count++;
                }
                else
                {
                    result.forward_rejection_count++;
                }
            }

            // return the result
            return(result);
        }
예제 #19
0
        private ILaneModel GetLaneModel(LocalLaneModel laneModel, LinePath rndfPath, double rndfPathWidth, CarTimestamp rndfPathTimestamp)
        {
            // check the lane model probability
            if (laneModel.Probability < lane_probability_reject_threshold)
            {
                // we're rejecting this, just return a path lane model
                return(new PathLaneModel(rndfPathTimestamp, rndfPath, rndfPathWidth));
            }

            // project the lane model's path into the rndf path's timestamp
            RelativeTransform relTransform  = Services.RelativePose.GetTransform(localRoadModel.Timestamp, rndfPathTimestamp);
            LinePath          laneModelPath = laneModel.LanePath.Transform(relTransform);

            // iterate through the waypoints in the RNDF path and project onto the lane model
            // the first one that is over the threshold, we consider the waypoint before as a potential ending point
            LinePath.PointOnPath laneModelDeviationEndPoint = new LinePath.PointOnPath();
            // flag indicating if any of the waypoint tests failed because of the devation was too high
            bool anyDeviationTooHigh = false;
            // number of waypoints accepted
            int numWaypointsAccepted = 0;

            // get the vehicle's position on the rndf path
            LinePath.PointOnPath rndfZeroPoint = rndfPath.ZeroPoint;

            // get the vehicle's position on the lane model
            LinePath.PointOnPath laneModelZeroPoint = laneModelPath.ZeroPoint;

            // get the last point we want to consider on the lane model
            LinePath.PointOnPath laneModelFarthestPoint = laneModelPath.AdvancePoint(laneModelZeroPoint, lane_model_max_dist);

            // start walking forward through the waypoints on the rndf path
            // this loop will implicitly exit when we're past the end of the lane model as the waypoints
            //		will stop being close to the lane model (GetClosestPoint returns the end point if we're past the
            //    end of the path)
            for (int i = rndfZeroPoint.Index + 1; i < rndfPath.Count; i++)
            {
                // get the waypoint
                Coordinates rndfWaypoint = rndfPath[i];

                // get the closest point on the lane model
                LinePath.PointOnPath laneModelClosestPoint = laneModelPath.GetClosestPoint(rndfWaypoint);

                // compute the distance between the two
                double deviation = rndfWaypoint.DistanceTo(laneModelClosestPoint.Location);

                // if this is above the deviation threshold, leave the loop
                if (deviation > lane_deviation_reject_threshold || laneModelClosestPoint > laneModelFarthestPoint)
                {
                    // if we're at the end of the lane model path, we don't want to consider this a rejection
                    if (laneModelClosestPoint < laneModelFarthestPoint)
                    {
                        // mark that at least on deviation was too high
                        anyDeviationTooHigh = true;
                    }
                    break;
                }

                // increment the number of waypoint accepted
                numWaypointsAccepted++;

                // update the end point of where we're valid as the local road model was OK up to this point
                laneModelDeviationEndPoint = laneModelClosestPoint;
            }

            // go through and figure out how far out the variance is within tolerance
            LinePath.PointOnPath laneModelVarianceEndPoint = new LinePath.PointOnPath();
            // walk forward from this point until the end of the lane mode path
            for (int i = laneModelZeroPoint.Index + 1; i < laneModelPath.Count; i++)
            {
                // check if we're within the variance toleration
                if (laneModel.LaneYVariance[i] <= y_var_reject_threshold)
                {
                    // we are, update the point on path
                    laneModelVarianceEndPoint = laneModelPath.GetPointOnPath(i);
                }
                else
                {
                    // we are out of tolerance, break out of the loop
                    break;
                }
            }

            // now figure out everything out
            // determine waypoint rejection status
            WaypointRejectionResult waypointRejectionResult;

            if (laneModelDeviationEndPoint.Valid)
            {
                // if the point is valid, that we had at least one waypoint that was ok
                // check if any waypoints were rejected
                if (anyDeviationTooHigh)
                {
                    // some waypoint was ok, so we know that at least one waypoint was accepted
                    waypointRejectionResult = WaypointRejectionResult.SomeWaypointsAccepted;
                }
                else
                {
                    // no waypoint triggered a rejection, but at least one was good
                    waypointRejectionResult = WaypointRejectionResult.AllWaypointsAccepted;
                }
            }
            else
            {
                // the point is not valid, so we either had no waypoints or we had all rejections
                if (anyDeviationTooHigh)
                {
                    // the first waypoint was rejected, so all are rejected
                    waypointRejectionResult = WaypointRejectionResult.AllWaypointsRejected;
                }
                else
                {
                    // the first waypoint (if any) was past the end of the lane model
                    waypointRejectionResult = WaypointRejectionResult.NoWaypoints;
                }
            }

            // criteria for determining if this path is valid:
            //	- if some or all waypoints were accepted, than this is probably a good path
            //		- if some of the waypoints were accepted, we go no farther than the last waypoint that was accepted
            //	- if there were no waypoints, this is a potentially dangerous situation since we can't reject
            //    or confirm the local road model. for now, we'll assume that it is correct but this may need to change
            //    if we start handling intersections in this framework
            //  - if all waypoints were rejected, than we don't use the local road model
            //  - go no farther than the laneModelVarianceEndPoint, which is the last point where the y-variance of
            //    the lane model was in tolerance

            // now build out the lane model
            ILaneModel finalLaneModel;

            // check if we rejected all waypoints or no lane model points satisified the variance threshold
            if (waypointRejectionResult == WaypointRejectionResult.AllWaypointsRejected || !laneModelVarianceEndPoint.Valid)
            {
                // want to just use the path lane model
                finalLaneModel = new PathLaneModel(rndfPathTimestamp, rndfPath, rndfPathWidth);
            }
            else
            {
                // we'll use the lane model
                // need to build up the center line as well as left and right bounds

                // to build up the center line, use the lane model as far as we feel comfortable (limited by either variance
                // or by rejections) and then use the rndf lane after that.
                LinePath centerLine = new LinePath();

                // figure out the max distance
                // if there were no waypoints, set the laneModelDeviationEndPoint to the end of the lane model
                if (waypointRejectionResult == WaypointRejectionResult.NoWaypoints)
                {
                    laneModelDeviationEndPoint = laneModelFarthestPoint;
                }

                // figure out the closer of the end points
                LinePath.PointOnPath laneModelEndPoint = (laneModelDeviationEndPoint < laneModelVarianceEndPoint) ? laneModelDeviationEndPoint : laneModelVarianceEndPoint;
                bool endAtWaypoint = laneModelEndPoint == laneModelDeviationEndPoint;

                // add the lane model to the center line
                centerLine.AddRange(laneModelPath.GetSubpathEnumerator(laneModelZeroPoint, laneModelEndPoint));

                // create a list to hold the width expansion values
                List <double> widthValue = new List <double>();

                // make the width expansion values the width of the path plus the 1-sigma values
                for (int i = laneModelZeroPoint.Index; i < laneModelZeroPoint.Index + centerLine.Count; i++)
                {
                    widthValue.Add(laneModel.Width / 2.0 + Math.Sqrt(laneModel.LaneYVariance[i]));
                }

                // now figure out how to add the rndf path
                // get the projection of the lane model end point on the rndf path
                LinePath.PointOnPath rndfPathStartPoint = rndfPath.GetClosestPoint(laneModelEndPoint.Location);

                // if the closest point is past the end of rndf path, then we don't want to tack anything on
                if (rndfPathStartPoint != rndfPath.EndPoint)
                {
                    // get the last segment of the new center line
                    Coordinates centerLineEndSegmentVec = centerLine.EndSegment.UnitVector;
                    // get the last point of the new center line
                    Coordinates laneModelEndLoc = laneModelEndPoint.Location;

                    // now figure out the distance to the next waypoint
                    LinePath.PointOnPath rndfNextPoint = new LinePath.PointOnPath();

                    // figure out if we're ending at a waypoint or not
                    if (endAtWaypoint)
                    {
                        rndfNextPoint = rndfPath.GetPointOnPath(rndfPathStartPoint.Index + 1);

                        // if the distance from the start point to the next point is less than rndf_dist_min, then
                        // use the waypont after
                        double dist = rndfPath.DistanceBetween(rndfPathStartPoint, rndfNextPoint);

                        if (dist < rndf_dist_min)
                        {
                            if (rndfPathStartPoint.Index < rndfPath.Count - 2)
                            {
                                rndfNextPoint = rndfPath.GetPointOnPath(rndfPathStartPoint.Index + 2);
                            }
                            else if (rndfPath.DistanceBetween(rndfPathStartPoint, rndfPath.EndPoint) < rndf_dist_min)
                            {
                                rndfNextPoint = LinePath.PointOnPath.Invalid;
                            }
                            else
                            {
                                rndfNextPoint = rndfPath.AdvancePoint(rndfPathStartPoint, rndf_dist_min * 2);
                            }
                        }
                    }
                    else
                    {
                        // track the last angle we had
                        double lastAngle = double.NaN;

                        // walk down the rndf path until we find a valid point
                        for (double dist = rndf_dist_min; dist <= rndf_dist_max; dist += rndf_dist_step)
                        {
                            // advance from the start point by dist
                            double distTemp = dist;
                            rndfNextPoint = rndfPath.AdvancePoint(rndfPathStartPoint, ref distTemp);

                            // if the distTemp is > 0, then we're past the end of the path
                            if (distTemp > 0)
                            {
                                // if we're immediately past the end, we don't want to tack anything on
                                if (dist == rndf_dist_min)
                                {
                                    rndfNextPoint = LinePath.PointOnPath.Invalid;
                                }

                                break;
                            }

                            // check the angle made by the last segment of center line and the segment
                            // formed between the end point of the center line and this new point
                            double angle = Math.Acos(centerLineEndSegmentVec.Dot((rndfNextPoint.Location - laneModelEndLoc).Normalize()));

                            // check if the angle satisfies the threshold or we're increasing the angle
                            if (Math.Abs(angle) < rndf_angle_threshold || (!double.IsNaN(lastAngle) && angle > lastAngle))
                            {
                                // break out of the loop, we're done searching
                                break;
                            }

                            lastAngle = angle;
                        }
                    }

                    // tack on the rndf starting at next point going to the end
                    if (rndfNextPoint.Valid)
                    {
                        LinePath subPath = rndfPath.SubPath(rndfNextPoint, rndfPath.EndPoint);
                        centerLine.AddRange(subPath);

                        // insert the lane model end point into the sub path
                        subPath.Insert(0, laneModelEndLoc);

                        // get the angles
                        List <Pair <int, double> > angles = subPath.GetIntersectionAngles(0, subPath.Count - 1);

                        // add the width of the path inflated by the angles
                        for (int i = 0; i < angles.Count; i++)
                        {
                            // calculate the width expansion factor
                            // 90 deg, 3x width
                            // 45 deg, 1.5x width
                            // 0 deg, 1x width
                            double widthFactor = Math.Pow(angles[i].Right / (Math.PI / 2.0), 2) * 2 + 1;

                            // add the width value
                            widthValue.Add(widthFactor * laneModel.Width / 2);
                        }

                        // add the final width
                        widthValue.Add(laneModel.Width / 2);
                    }

                    // set the rndf path start point to be the point we used
                    rndfPathStartPoint = rndfNextPoint;
                }

                // for now, calculate the left and right bounds the same way we do for the path lane model
                // TODO: figure out if we want to do this more intelligently using knowledge of the lane model uncertainty
                LinePath leftBound = centerLine.ShiftLateral(widthValue.ToArray());
                // get the next shifts
                for (int i = 0; i < widthValue.Count; i++)
                {
                    widthValue[i] = -widthValue[i];
                }
                LinePath rightBound = centerLine.ShiftLateral(widthValue.ToArray());

                // build the final lane model
                finalLaneModel = new CombinedLaneModel(centerLine, leftBound, rightBound, laneModel.Width, rndfPathTimestamp);
            }

            SendLaneModelToUI(finalLaneModel, rndfPathTimestamp);

            // output the fit result
            return(finalLaneModel);
        }
예제 #20
0
        public void NavigationHitTest(Coordinates c, out INavigableNode node, out NavigableEdge edge)
        {
            if (this.zt.current != null)
            {
                // Determine size of bounding box
                float scaled_offset = 1 / this.rd.WorldTransform.Scale;

                // invert the scale
                float scaled_size = DrawingUtility.cp_large_size;

                // assume that the world transform is currently applied correctly to the graphics
                RectangleF rect = new RectangleF((float)c.X - scaled_size / 2, (float)c.Y - scaled_size / 2, scaled_size, scaled_size);

                foreach (ArbiterParkingSpotWaypoint apsw in this.zt.current.ParkingSpotWaypoints.Values)
                {
                    if (apsw.Position.DistanceTo(c) < 1.0)
                    {
                        node = apsw;
                        edge = null;
                        return;
                    }
                }

                foreach (ArbiterPerimeterWaypoint apw in this.zt.current.Perimeter.PerimeterPoints.Values)
                {
                    if ((apw.IsExit || apw.IsEntry) && rect.Contains(DrawingUtility.ToPointF(apw.Position)) &&
                        (this.PreviousNode == null || !this.PreviousNode.Equals(apw)) &&
                        apw.Position.DistanceTo(c) < 1.0)
                    {
                        node = apw;
                        edge = null;
                        return;
                    }
                }

                foreach (INavigableNode inn in this.zt.current.NavigationNodes)
                {
                    if (rect.Contains(DrawingUtility.ToPointF(inn.Position)) &&
                        (this.PreviousNode == null || !this.PreviousNode.Equals(inn)) &&
                        inn.Position.DistanceTo(c) < 1.0)
                    {
                        node = inn;
                        edge = null;
                        return;
                    }
                }

                NavigableEdge closest  = null;
                double        distance = double.MaxValue;
                foreach (NavigableEdge ne in this.zt.current.NavigableEdges)
                {
                    LinePath lp   = new LinePath(new Coordinates[] { ne.Start.Position, ne.End.Position });
                    double   dist = lp.GetClosestPoint(c).Location.DistanceTo(c);
                    if (dist < rect.Width && (dist < distance || closest == null) && dist < 1.0)
                    {
                        closest  = ne;
                        distance = dist;
                    }
                }

                if (closest != null)
                {
                    node = null;
                    edge = closest;
                    return;
                }
            }

            node = null;
            edge = null;
            return;
        }