class CurveRoadBuilder: def __init__(self, country=CountryCodes.US): self.STD_ROADMARK = pyodrx.RoadMark(pyodrx.RoadMarkType.solid, 0.2, rule=pyodrx.MarkRule.no_passing) self.STD_START_CLOTH = 1 / 1000000000 self.country = country self.laneBuilder = LaneBuilder() pass def getJunctionSelection(self, isJunction): if isJunction: return 1 return -1 def createPVForArcWithCloths(self, arcCurvature, arcAngle, clothAngle, clothCurvatureStart=None, clothCurvatureEnd=None): """[summary] Args: arcCurvature ([type]): [description] arcAngle ([type]): [description] clothAngle ([type]): [description] clothCurvatureStart ([type], optional): [description]. Defaults to None. clothCurvatureEnd ([type], optional): [description]. Defaults to None. Returns: [type]: [description] """ if clothCurvatureStart is None: clothCurvatureStart = self.STD_START_CLOTH if clothCurvatureEnd is None: clothCurvatureEnd = self.STD_START_CLOTH pv = extensions.ExtendedPlanview() spiral1 = extensions.ExtendedSpiral(clothCurvatureStart, arcCurvature, angle=clothAngle) arc = pyodrx.Arc(arcCurvature, angle=arcAngle) spiral2 = extensions.ExtendedSpiral(arcCurvature, clothCurvatureEnd, angle=clothAngle) pv.add_geometry(spiral1) pv.add_geometry(arc) pv.add_geometry(spiral2) return pv def create(self, roadId, angleBetweenEndpoints, isJunction=False, curvature=StandardCurvature.Medium.value, curveType=StandardCurveTypes.Simple, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): """[summary] Args: angleBetweenEndpoints (radian): (0, 180) range in radian curvature (float): (-inf, +inf) Raises: Exception: [description] Returns: [type]: [description] """ if angleBetweenEndpoints < 0: angleBetweenEndpoints *= -1 curvature *= -1 if curveType is StandardCurveTypes.Simple: return self.createSimple(roadId, angleBetweenEndpoints, isJunction, curvature, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) elif curveType is StandardCurveTypes.LongArc: return self.createSimpleCurveWithLongArc(roadId, angleBetweenEndpoints, isJunction, curvature) elif curveType is StandardCurveTypes.S: return self.createS(roadId, angleBetweenEndpoints, isJunction, curvature) else: error = f"Unkown curveType {curveType} or not supported for random creation" raise Exception(error) def createSimple(self, roadId, angleBetweenEndpoints, isJunction=False, curvature=StandardCurvature.Medium.value, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): junction = extensions.getJunctionSelection(isJunction) totalRotation = np.pi - angleBetweenEndpoints arcAngle = np.pi / 10000000 clothAngle = totalRotation / 2 return self.createCurveGeoAndLanes(roadId, isJunction, curvature, arcAngle, clothAngle, n_lanes, lane_offset, laneSides, isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane) def createCurveGeoAndLanes(self, roadId, isJunction, curvature, arcAngle, clothAngle, n_lanes, lane_offset, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): junction = extensions.getJunctionSelection(isJunction) pv = self.createPVForArcWithCloths(curvature, arcAngle, clothAngle) length = pv.getTotalLength() laneSections = self.laneBuilder.getStandardLanes( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction, curveType=StandardCurveTypes.LongArc) return road def createSimpleCurveWithLongArc(self, roadId, angleBetweenEndpoints, isJunction=False, curvature=StandardCurvature.Medium.value, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): junction = extensions.getJunctionSelection(isJunction) totalRotation = np.pi - angleBetweenEndpoints # most of the angleBetweenEndpoints should be assigned to the Arc arcAngle = totalRotation * 0.9 # main curve clothAngle = (totalRotation * 0.1) / 2 # curve more. return self.createCurveGeoAndLanes(roadId, isJunction, curvature, arcAngle, clothAngle, n_lanes, lane_offset, laneSides, isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane) def createS(self, roadId, angleBetweenEndpoints, isJunction=False, curvature=StandardCurvature.Medium.value, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): """Here the angleBetweenEndpoints are used for the first road and S mid point, and S mid point and Second road Args: roadId ([type]): [description] angleBetweenEndpoints ([type]): used for the first road and S mid point, and S mid point and Second road. Use negative angles for interesting Ss isJunction (bool, optional): [description]. Defaults to False. curvature ([type], optional): [description]. Defaults to StandardCurvature.Medium.value. Returns: [type]: [description] """ junction = extensions.getJunctionSelection(isJunction) totalRotation = np.pi - angleBetweenEndpoints # most of the angleBetweenEndpoints should be assigned to the Arc arcAngle = totalRotation * 0.9 clothAngle = (totalRotation * 0.1) / 2 # curve more. arc_curv = curvature arc_angle = arcAngle cloth_angle = clothAngle cloth_start = self.STD_START_CLOTH pv = ExtendedPlanview() # adjust sign if angle is negative if cloth_angle < 0 and arc_curv > 0: cloth_angle = -cloth_angle arc_curv = -arc_curv cloth_start = -cloth_start arc_angle = -arc_angle # we are changing the second half of the S to have different arc angle and curvature. multiplier = np.random.choice(9) / 10 arc_angle2 = arc_angle - arc_angle * multiplier arc_curv2 = -(arc_curv + arc_curv * multiplier ) # the angle needs to be opposite for the second half. # create geometries spiral1 = extensions.ExtendedSpiral(cloth_start, arc_curv, angle=cloth_angle) arc = pyodrx.Arc(arc_curv, angle=arc_angle) arc2 = pyodrx.Arc(arc_curv2, angle=-arc_angle2) spiral2 = extensions.ExtendedSpiral(-arc_curv, cloth_start, angle=-cloth_angle) pv.add_geometry(spiral1) pv.add_geometry(arc) pv.add_geometry(arc2) pv.add_geometry(spiral2) length = pv.getTotalLength() laneSections = self.laneBuilder.getStandardLanes( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction, curveType=StandardCurveTypes.S) return road def createCurveByLength(self, roadId, length, isJunction=False, curvature=StandardCurvature.Medium.value, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): junction = self.getJunctionSelection(isJunction) pv = ExtendedPlanview() arc = pyodrx.Arc(curvature, length=length) pv.add_geometry(arc) length = pv.getTotalLength() laneSections = self.laneBuilder.getStandardLanes( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction, curveType=StandardCurveTypes.LongArc) return road def createParamPoly3(self, roadId, isJunction=False, au=0, bu=20, cu=20, du=10, av=0, bv=2, cv=20, dv=10, prange='normalized', length=None, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): junction = self.getJunctionSelection(isJunction) pv = ExtendedPlanview() poly = pyodrx.ParamPoly3(au, bu, cu, du, av, bv, cv, dv, prange, length) # poly = extensions.IntertialParamPoly(au,bu,cu,du,av,bv,cv,dv,prange,length) pv.add_geometry(poly) length = pv.getTotalLength() laneSections = self.laneBuilder.getStandardLanes( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) # create lanes road = ExtendedRoad(roadId, pv, laneSections, road_type=junction, curveType=StandardCurveTypes.Poly) return road
class test_LaneBuilder(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.laneBuilder = LaneBuilder() def test_widths(self): roadLength = 10 #1 simple Lanes lanes = self.laneBuilder.getStandardLanes(1, 3) for laneSec in lanes.lanesections: widths = laneSec.widths(roadLength) assert widths[0] == 6 assert widths[1] == 6 #1 turn lanes lanes = self.laneBuilder.getStandardLanes(1, 3, roadLength=roadLength, isLeftTurnLane=True, isRightTurnLane=True) widths = lanes.lanesections[0].widths(roadLength, LaneOffset(s=0), LaneOffset(s=1)) print(widths) assert widths[0] == 6 assert widths[1] == 6 widths = lanes.lanesections[1].widths(roadLength, LaneOffset(s=1), LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 6 assert widths[1] == 12 widths = lanes.lanesections[2].widths(roadLength, LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 12 assert widths[1] == 12 #2 merge lanes lanes = self.laneBuilder.getStandardLanes(1, 3, roadLength=roadLength, isLeftMergeLane=True, isRightMergeLane=True) widths = lanes.lanesections[0].widths(roadLength, LaneOffset(s=0), LaneOffset(s=1)) print(widths) assert widths[0] == 12 assert widths[1] == 12 widths = lanes.lanesections[1].widths(roadLength, LaneOffset(s=1), LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 12 assert widths[1] == 6 widths = lanes.lanesections[2].widths(roadLength, LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 6 assert widths[1] == 6 #2 internal lanes lanes = self.laneBuilder.getStandardLanesWithInternalTurns( 1, 3, roadLength=roadLength, numberOfLeftTurnLanesOnRight=2) widths = lanes.lanesections[0].widths(roadLength, LaneOffset(s=0), LaneOffset(s=1)) print(widths) assert widths[0] == 12 assert widths[1] == 12 widths = lanes.lanesections[1].widths(roadLength, LaneOffset(s=1), LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 12 assert widths[1] == 12 widths = lanes.lanesections[2].widths(roadLength, LaneOffset(s=roadLength - 1)) print(widths) assert widths[0] == 12 assert widths[1] == 12