def __init__(self, roadBuilder=None, straightRoadLen=10, minAngle=np.pi / 6, maxAngle=None, country=CountryCodes.US, random_seed=39): self.config = Configuration() self.roadBuilder = roadBuilder if self.roadBuilder is None: self.roadBuilder = RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.laneBuilder = LaneBuilder() self.straightRoadLen = straightRoadLen self.minAngle = minAngle self.maxAngle = maxAngle if self.maxAngle == np.pi: self.maxAngle = 0.99 * np.pi self.laneWidth = self.config.get("default_lane_width") self.minConnectionLength = self.config.get("min_connection_length") self.maxConnectionLength = self.config.get("max_connection_length") self.curveBuilder = CurveRoadBuilder(country=country) self.connectionBuilder = ConnectionBuilder() self.countryCode = country np.random.seed(random_seed) pass
def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.configuration = Configuration() self.roadBuilder = RoadBuilder() self.junctionBuilder = JunctionBuilder() outputDir = os.path.join(os.getcwd(), 'output') lastId = 0 self.harvester = JunctionHarvester(outputDir=outputDir, outputPrefix='test_', lastId=lastId, minAngle=np.pi / 30, maxAngle=np.pi)
def __init__(self, outputDir, outputPrefix, lastId=0, minAngle = np.pi / 10, maxAngle = np.pi, straightRoadLen = 10, esminiPath = None, saveImage = True): """The angle between two connected roads are >= self.minAngle <= self.maxAngle Args: outputDir ([type]): [description] outputPrefix ([type]): [description] lastId (int, optional): [description]. Defaults to 0. minAngle ([type], optional): [description]. Defaults to np.pi/10. maxAngle ([type], optional): [description]. Defaults to np.pi. straightRoadLen : used both for normal and connection roads esminiPath: Path to esmini to generate images for roads. """ self.destinationPrefix = os.path.join(outputDir, outputPrefix) self.minAngle = minAngle self.maxAngle = maxAngle self.lastId = lastId self.straightRoadLen = straightRoadLen self.roadBuilder = RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.junctionBuilder = JunctionBuilder(self.roadBuilder) self.sequentialJunctionBuilder = SequentialJunctionBuilder(self.roadBuilder, self.straightRoadLen, minAngle=minAngle, maxAngle=maxAngle) self.junctionMerger = JunctionMerger(outputDir, outputPrefix, lastId) self.configuration = Configuration() if esminiPath is None: self.esminiPath = self.configuration.get("esminipath") else: self.esminiPath = esminiPath self.saveImage = saveImage if os.path.isdir(self.esminiPath) is False: logging.warn(f"Esmini path not found {self.esminiPath}. Will break if you try to save images using harvester.") pass
def __init__(self, outputDir, outputPrefix='R3_', lastId=0, esminiPath=None, saveImage=True): """ Args: outputDir ([type]): [description] outputPrefix ([type]): [description] lastId (int, optional): [description]. Defaults to 0. """ self.destinationPrefix = os.path.join(outputDir, outputPrefix) self.lastId = lastId self.roadBuilder = RoadBuilder() self.configuration = Configuration() if esminiPath is None: self.esminiPath = self.configuration.get("esminipath") else: self.esminiPath = esminiPath self.saveImage = saveImage if os.path.isdir(self.esminiPath) is False: logging.warn( f"Esmini path not found {self.esminiPath}. Will break if you try to save images using merger." ) pass
def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.roadBuilder = RoadBuilder() self.laneBuilder = LaneBuilder() self.laneLinker = LaneLinker() self.straightRoadBuilder = StraightRoadBuilder()
def __init__(self, outputDir, outputPrefix='R3_', lastId=0): """ Args: outputDir ([type]): [description] outputPrefix ([type]): [description] lastId (int, optional): [description]. Defaults to 0. """ self.destinationPrefix = os.path.join(outputDir, outputPrefix) self.lastId = lastId self.roadBuilder = RoadBuilder() pass
class test_RoadBuilder(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.configuration = Configuration() self.roadBuilder = RoadBuilder() self.junctionBuilder = JunctionBuilder() outputDir = os.path.join(os.getcwd(), 'output') lastId = 0 self.harvester = JunctionHarvester(outputDir=outputDir, outputPrefix='test_', lastId=lastId, minAngle=np.pi / 30, maxAngle=np.pi) def test_ParamPoly(self): tangentX = np.array([9.389829642616592, -7.596531772501544]) tangentY = np.array([0.0, 5.5192033616035365]) t = np.array([0, 1]) x = np.array([0, 17.8605173461395]) y = np.array([0, -5.803233839653106]) hermiteX = CubicHermiteSpline(t, x, tangentX) hermiteY = CubicHermiteSpline(t, y, tangentY) xCoeffs = hermiteX.c.flatten() yCoeffs = hermiteY.c.flatten() # scipy coefficient and open drive coefficents have opposite order. myRoad = self.roadBuilder.curveBuilder.createParamPoly3( 0, isJunction=False, au=xCoeffs[3], bu=xCoeffs[2], cu=xCoeffs[1], du=xCoeffs[0], av=yCoeffs[3], bv=yCoeffs[2], cv=yCoeffs[1], dv=yCoeffs[0]) odr = pyodrx.OpenDrive("test") odr.add_road(myRoad) odr.adjust_roads_and_lanes() extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) def test_getConnectionRoadBetween(self): # test scenario for connection road roads = [] roads.append(pyodrx.create_straight_road(0, 10)) roads.append( self.roadBuilder.curveBuilder.createSimple(1, np.pi / 4, True, curvature=0.2)) roads.append(pyodrx.create_straight_road(2, 10)) roads.append( self.roadBuilder.curveBuilder.createSimple(3, np.pi / 3, True, curvature=0.2)) roads.append(pyodrx.create_straight_road(4, 10)) roads.append( self.roadBuilder.curveBuilder.createSimple(5, np.pi / 2, True, curvature=0.2)) roads.append(pyodrx.create_straight_road(6, 10)) roads[0].add_successor(pyodrx.ElementType.junction, 1) # roads[1].add_predecessor(pyodrx.ElementType.road,0,pyodrx.ContactPoint.end) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.start) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.junction, 1) roads[2].add_successor(pyodrx.ElementType.junction, 3) roads[3].add_predecessor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[3].add_successor(pyodrx.ElementType.road, 4, pyodrx.ContactPoint.start) roads[4].add_predecessor(pyodrx.ElementType.junction, 3) roads[4].add_successor(pyodrx.ElementType.junction, 5) roads[5].add_predecessor(pyodrx.ElementType.road, 4, pyodrx.ContactPoint.start) roads[5].add_successor(pyodrx.ElementType.road, 6, pyodrx.ContactPoint.start) roads[6].add_predecessor(pyodrx.ElementType.junction, 5) junction = self.junctionBuilder.createJunctionForASeriesOfRoads(roads) odrName = "test_connectionRoad" odr = extensions.createOdr(odrName, roads, [junction]) lastConnection = self.harvester.junctionBuilder.createLastConnectionForLastAndFirstRoad( 7, roads, junction, cp1=pyodrx.ContactPoint.start) roads.append(lastConnection) odr.add_road(lastConnection) # randConnection = self.harvester.junctionBuilder.createConnectionFor2Roads(8, roads[0], roads[4], junction, cp1=pyodrx.ContactPoint.start) # roads.append(randConnection) # odr.add_road(randConnection) # randConnection2 = self.harvester.junctionBuilder.createConnectionFor2Roads(9, roads[2], roads[6], junction, cp1=pyodrx.ContactPoint.start) # roads.append(randConnection2) # odr.add_road(randConnection2) # odr.reset() # odr.add_road(lastConnection) # odr.adjust_roads_and_lanes() odr.resetAndReadjust() # pyodrx.prettyprint(odr.get_element()) odr.write_xml(f"output/test_connectionRoad.xodr") # extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) pass def test_createMShape(self): roads = [] roads.append(pyodrx.create_straight_road(0, 10)) roads.append(self.roadBuilder.createMShape(1, 1, np.pi / 1.5, 10)) roads.append(pyodrx.create_straight_road(2, 10)) roads[0].add_successor(pyodrx.ElementType.junction, 1) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.end) # roads[1].add_predecessor(pyodrx.ElementType.road,0,pyodrx.ContactPoint.start) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.junction, 1) junction = self.junctionBuilder.createJunctionForASeriesOfRoads(roads) odrName = "test_connectionRoad" odr = extensions.createOdr(odrName, roads, [junction]) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/m-shape.xodr" odr.write_xml(xmlPath) extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) def test_createMShapeLeftLanes(self): roads = [] roads.append(pyodrx.create_straight_road(0, 10)) roads.append( self.roadBuilder.createMShape(1, 1, np.pi / 1.5, 10, laneSides=junctions.LaneSides.LEFT)) roads.append(pyodrx.create_straight_road(2, 10)) roads[0].add_successor(pyodrx.ElementType.junction, 1) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.end) # roads[1].add_predecessor(pyodrx.ElementType.road,0,pyodrx.ContactPoint.start) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.junction, 1) junction = self.junctionBuilder.createJunctionForASeriesOfRoads(roads) odrName = "test_connectionRoad" odr = extensions.createOdr(odrName, roads, [junction]) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) def test_createMShapeRightLanes(self): roads = [] roads.append(pyodrx.create_straight_road(0, 10)) roads.append( self.roadBuilder.createMShape( 1, 1, -np.pi / 1.5, 10, laneSides=junctions.LaneSides.RIGHT, direction=CircularDirection.COUNTERCLOCK_WISE)) roads.append(pyodrx.create_straight_road(2, 10)) roads[0].add_successor(pyodrx.ElementType.junction, 1) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.end) # roads[1].add_predecessor(pyodrx.ElementType.road,0,pyodrx.ContactPoint.start) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.junction, 1) junction = self.junctionBuilder.createJunctionForASeriesOfRoads(roads) odrName = "test_connectionRoad" odr = extensions.createOdr(odrName, roads, [junction]) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")))
def setUp(self): self.roadBuilder = RoadBuilder() self.junctionBuilder = JunctionBuilder() self.configuration = Configuration() self.angleCurvatureMap = AngleCurvatureMap() self.esminiPath = self.configuration.get("esminipath")
class JunctionBuilder: def __init__(self, roadBuilder=None, straightRoadLen=10, minAngle=np.pi / 6, maxAngle=None, country=CountryCodes.US, random_seed=39): self.config = Configuration() self.roadBuilder = roadBuilder if self.roadBuilder is None: self.roadBuilder = RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.laneBuilder = LaneBuilder() self.straightRoadLen = straightRoadLen self.minAngle = minAngle self.maxAngle = maxAngle if self.maxAngle == np.pi: self.maxAngle = 0.99 * np.pi self.laneWidth = self.config.get("default_lane_width") self.minConnectionLength = self.config.get("min_connection_length") self.maxConnectionLength = self.config.get("max_connection_length") self.curveBuilder = CurveRoadBuilder(country=country) self.connectionBuilder = ConnectionBuilder() self.countryCode = country np.random.seed(random_seed) pass def createJunctionForASeriesOfRoads(self, roads, id=0): """[summary] Args: roads ([type]): even indices are roads, odd indices are connection roads of the junction Returns: [type]: [description] """ # TODO it does not support all lanes. # ID is wrong junction = pyodrx.Junction("spiderJunction", id) connectionId = 1 while (connectionId < len(roads)): print(f"connecting roads {connectionId-1} {connectionId}") connectionL = pyodrx.Connection(connectionId - 1, connectionId, pyodrx.ContactPoint.start) connectionL.add_lanelink(-1, -1) # if (connectionId + 1) < len(roads): # connectionR = pyodrx.Connection(connectionId+1, connectionId, pyodrx.ContactPoint.end) # else: # connectionR = pyodrx.Connection(0, connectionId, pyodrx.ContactPoint.end) # connectionR.add_lanelink(1,1) junction.add_connection(connectionL) # junction.add_connection(connectionR) connectionId += 2 return junction def createLastConnectionForLastAndFirstRoad(self, nextRoadId, roads, junction, cp1=pyodrx.ContactPoint.end, cp2=pyodrx.ContactPoint.start): lastConnectionId = nextRoadId lastConnection = self.roadBuilder.getConnectionRoadBetween( lastConnectionId, roads[-1], roads[0], cp2, cp1) RoadLinker.createExtendedPredSuc(predRoad=roads[-1], predCp=cp2, sucRoad=lastConnection, sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=lastConnection, predCp=pyodrx.ContactPoint.end, sucRoad=roads[0], sucCP=cp1) connectionL = pyodrx.Connection(roads[-1].id, lastConnectionId, pyodrx.ContactPoint.start) connectionL.add_lanelink(-1, -1) junction.add_connection(connectionL) # roads.append(lastConnection) # dangerous. do not add the road return lastConnection def createConnectionFor2Roads(self, nextRoadId, road1, road2, junction, cp1, cp2, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH): """Does not modify predecessor or successor of the given roads. Args: junction: the junction object to add links. Returns: [type]: connection road with first road as the predecessor and second road as the successor """ newConnectionId = nextRoadId newConnectionRoad = self.roadBuilder.getConnectionRoadBetween( newConnectionId, road1, road2, cp1, cp2, isJunction=True, n_lanes=n_lanes, lane_offset=lane_offset, laneSides=laneSides) RoadLinker.createExtendedPredSuc(predRoad=road1, predCp=cp1, sucRoad=newConnectionRoad, sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=newConnectionRoad, predCp=pyodrx.ContactPoint.end, sucRoad=road2, sucCP=cp2) if junction is not None: if laneSides == LaneSides.LEFT or laneSides == LaneSides.BOTH: connectionL = pyodrx.Connection(road2.id, newConnectionId, pyodrx.ContactPoint.end) connectionL.add_lanelink(-1, -1) junction.add_connection(connectionL) else: connectionL = pyodrx.Connection(road1.id, newConnectionId, pyodrx.ContactPoint.start) connectionL.add_lanelink(1, 1) junction.add_connection(connectionL) return newConnectionRoad def createInternalConnectionsForConnectionSeres(self, roads, connectionSeres, junction): """Assumes last road has the largest id. Used to create internal connections inside a junction. Assumes a connection series has middle roads which are connected by an internal connection road. Normally each series will have 3 roads. Args: roads ([type]): [description] connectionSeres ([type]): list of ConnectionSeries type """ # for each last road in a series, connect with the next first road length = len(connectionSeres) nextRoadId = connectionSeres[-1].getLast().id + 1 # last id so far. for i in range(length): currentConnectionS = connectionSeres[i] if (i + 1) < length: nextConnectionS = connectionSeres[i + 1] else: nextConnectionS = connectionSeres[0] # traffic will go from current to next fromRoad = currentConnectionS.getMiddle() toRoad = nextConnectionS.getMiddle() print( f"creating internal connection from {fromRoad.id} to {toRoad.id}" ) newConnection = self.createConnectionFor2Roads( nextRoadId, fromRoad, toRoad, cp1=pyodrx.ContactPoint.end, cp2=pyodrx.ContactPoint.start, junction=junction, laneSides=LaneSides.RIGHT) roads.append(newConnection) nextRoadId += 1 return nextRoadId def buildSimpleRoundAbout(self, odrId=0, numRoads=4, radius=10, cp1=pyodrx.ContactPoint.start, direction=CircularDirection.COUNTERCLOCK_WISE): """In a simple roundabout, there is a circle inside the junction, the connection roads reside in the circle. Args: numRoads (int, optional): [description]. Defaults to 4. radius : in meters. cp1: contact point on the first road. """ anglePerRoad = (np.pi * 2) / numRoads roads = [] roads.append( self.straightRoadBuilder.create(0, length=self.straightRoadLen)) nextRoadId = 1 roadsCreated = 1 connectionSeres = [ ] # holds all the connection road series so that we can create internal connections later. while roadsCreated < numRoads: previousRoadId = nextRoadId - 1 newConnectionId = nextRoadId # 1. create a new connection road series and increase nextRoadId newConnectionSeries = self.roadBuilder.createRoundAboutConnection( newConnectionId, anglePerRoad, radius) connectionSeres.append(newConnectionSeries) nextRoadId += newConnectionSeries.length() newRoadId = nextRoadId nextRoadId += 1 # 2. create a road newRoad = self.straightRoadBuilder.create( newRoadId, length=self.straightRoadLen) # 3 add new roads roads += newConnectionSeries.getAll() roads.append(newRoad) roads[previousRoadId].addExtendedSuccessor( newConnectionSeries.getFirst(), 0, pyodrx.ContactPoint.start) if newConnectionSeries.getFirst().id == 1: newConnectionSeries.getFirst().addExtendedPredecessor( roads[previousRoadId], 0, cp1) # TODO this is a hack. It will not eventually work because outgoing roads' ends will come to join other junctions. else: newConnectionSeries.getFirst().addExtendedPredecessor( roads[previousRoadId], 0, pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc( predRoad=newConnectionSeries.getLast(), predCp=pyodrx.ContactPoint.end, sucRoad=newRoad, sucCP=pyodrx.ContactPoint.start) # 6 get next action roadsCreated += 1 pass lastRoad = roads[-1] # 3. create connections and junction junction = self.createJunctionForASeriesOfRoads(roads) # print(f"number of roads created {len(roads)}") odrName = 'Simple-Roundabout-' + str(numRoads) + '_L2_' + str(odrId) odr = extensions.createOdrByPredecessor(odrName, roads, [junction]) # The last connection and resetting odr finalConnectionSeries = self.roadBuilder.createRoundAboutConnection( nextRoadId, anglePerRoad, radius) connectionSeres.append(finalConnectionSeries) roads += finalConnectionSeries.getAll() RoadLinker.createExtendedPredSuc( predRoad=lastRoad, predCp=pyodrx.ContactPoint.start, sucRoad=finalConnectionSeries.getFirst(), sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc( predRoad=finalConnectionSeries.getLast(), predCp=pyodrx.ContactPoint.end, sucRoad=roads[0], sucCP=cp1) odr.updateRoads(roads) odr.resetAndReadjust(byPredecessor=True) # Last step, link connection series by curves self.createInternalConnectionsForConnectionSeres( roads, connectionSeres, junction) odr.updateRoads(roads) odr.resetAndReadjust(byPredecessor=True) return odr def createConnectionRoads(self, roads, adjusted=False, areaType=JunctionAreaTypes.SQUARE): if adjusted is False: # set up x,y positions and headings for the roads around the boundary of the area if areaType == JunctionAreaTypes.SQUARE: # maximum roads to connect to a side maxRoadsPerSide = math.floor(len(roads) / 4) + 1 def createWithRandomLaneConfigurations(self, numRoads=3): raise NotImplementedError()
class JunctionHarvester: def __init__(self, outputDir, outputPrefix, lastId=0, minAngle = np.pi / 10, maxAngle = np.pi, straightRoadLen = 10, esminiPath = None, saveImage = True): """The angle between two connected roads are >= self.minAngle <= self.maxAngle Args: outputDir ([type]): [description] outputPrefix ([type]): [description] lastId (int, optional): [description]. Defaults to 0. minAngle ([type], optional): [description]. Defaults to np.pi/10. maxAngle ([type], optional): [description]. Defaults to np.pi. straightRoadLen : used both for normal and connection roads esminiPath: Path to esmini to generate images for roads. """ self.destinationPrefix = os.path.join(outputDir, outputPrefix) self.minAngle = minAngle self.maxAngle = maxAngle self.lastId = lastId self.straightRoadLen = straightRoadLen self.roadBuilder = RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.junctionBuilder = JunctionBuilder(self.roadBuilder) self.sequentialJunctionBuilder = SequentialJunctionBuilder(self.roadBuilder, self.straightRoadLen, minAngle=minAngle, maxAngle=maxAngle) self.junctionMerger = JunctionMerger(outputDir, outputPrefix, lastId) self.configuration = Configuration() if esminiPath is None: self.esminiPath = self.configuration.get("esminipath") else: self.esminiPath = esminiPath self.saveImage = saveImage if os.path.isdir(self.esminiPath) is False: logging.warn(f"Esmini path not found {self.esminiPath}. Will break if you try to save images using harvester.") pass def getOutputPath(self, fname): return self.destinationPrefix + fname + '.xodr' def createOdr(self, name, roads, junctions): return extensions.createOdr(name, roads, junctions) # odr = extensions.ExtendedOpenDrive(name) # for r in roads: # odr.add_road(r) # for junction in junctions: # odr.add_junction(junction) # print(f"starting adjustment. May freeze!!!!!!!!!!!!!") # odr.adjust_roads_and_lanes() # return odr def harvest2ways2Lanes(self, stepAngle=np.pi/20, maxTries = 100, seed=39): """We create junctions of two roads. Will create at least one road per angle. Args: stepAngle ([type], optional): used to generate angles between roads in conjunction with min and max angles. Defaults to np.pi/20. maxTries (int, optional): maximum number of junctions will be maxTries. Defaults to 1000. seed (int, optional): defaults to 39 """ np.random.seed(seed) # for each angle countCreated = 0 roadsPerAngle = self.getRoadsPerAngle(maxTries, stepAngle) angleBetweenRoads = self.minAngle odrObjectsPerAngle = {} # we will save the odrs keyed by angles while (countCreated < maxTries and angleBetweenRoads < self.maxAngle ): odrObjects = self.randomSome2ways2Lanes(angleBetweenRoads, roadsPerAngle) odrObjectsPerAngle[str(angleBetweenRoads)] = odrObjects countCreated += roadsPerAngle angleBetweenRoads += stepAngle print(f"created {countCreated} roads") # Save the odrs with(open(self.destinationPrefix + "harvested2R2LOrds.dill", "wb")) as f: dill.dump(odrObjectsPerAngle, f) print("Odr objects saved to " + self.destinationPrefix + "harvested2R2LOrds.dill" ) pass def getRoadsPerAngle(self, maxTries, stepAngle): roadsPerAngle = round((maxTries * stepAngle)/(self.maxAngle - self.minAngle)) if roadsPerAngle == 0: roadsPerAngle = 1 return roadsPerAngle def randomSome2ways2Lanes(self, angleBetweenRoads, numberOfRoads): """Creates 2way junctions where the connected roads have fixed angle Args: angleBetweenRoads ([type]): The angle between the roads. The connecting road is a curve that ensures the angle is preserved. numberOfRoads ([type]): number of roads to be generated Returns: [type]: [description] """ print( f"randomSome2ways2Lanes: creating {numberOfRoads} for angle: {math.degrees(angleBetweenRoads)}") odrObjects = [] for i in range( numberOfRoads): odr = self.random2ways2Lanes(angleBetweenRoads) odrObjects.append(odr) # 1. save the xml file fname = "2R2L_" + str(round(math.degrees(angleBetweenRoads))) + "_no" + str(i) xmlPath = self.getOutputPath(fname) odr.write_xml(xmlPath) # 2. save image if self.saveImage is True: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) return odrObjects def random2ways2Lanes(self, angleBetweenRoads): # print( f"random2ways2Lanes: creating a road network for angle: {math.degrees(angleBetweenRoads)}") roads = [] roads.append(pyodrx.create_straight_road(0, length=self.straightRoadLen)) # cannot reuse roads due to some references to links cannot be reinitialized with pyodrx lib. roads.append(self.createRandomConnectionConnectionRoad(1, angleBetweenRoads)) roads.append(pyodrx.create_straight_road(2, length=self.straightRoadLen)) self.link3RoadsWithMidAsJunction(roads) junction = self.create2RoadJunction(roads) self.lastId += 1 odrName = 'R2_L2_' + str(self.lastId) odr = self.createOdr(odrName, roads, [junction]) return odr def createRandomConnectionConnectionRoad(self, connectionRoadId, angleBetweenRoads): """The magic connectionRoad Args: angleBetweenRoads ([type]): The angle between the roads which this connectionRoad is suppose to connect together connectionRoadId ([type]): id to be assigned to the new connection road. """ if angleBetweenRoads > 3.05: # when it's greater than 174 degrees, create a straight road return pyodrx.create_straight_road(connectionRoadId, length=self.straightRoadLen, junction=1) connectionRoad = self.roadBuilder.createRandomCurve(connectionRoadId, angleBetweenRoads, isJunction=True) return connectionRoad def link3RoadsWithMidAsJunction(self, roads): roads[0].add_successor(pyodrx.ElementType.junction,1, pyodrx.ContactPoint.start) self.linkInsideRoad(roads[1], 0, 2) roads[2].add_predecessor(pyodrx.ElementType.junction,1, pyodrx.ContactPoint.end) def linkInsideRoad(self, connectionRoad, predecessorId, successorId): connectionRoad.add_predecessor(pyodrx.ElementType.road, predecessorId, pyodrx.ContactPoint.end) connectionRoad.add_successor(pyodrx.ElementType.road, successorId, pyodrx.ContactPoint.start) pass def create2RoadJunction(self, roads): """[summary] Args: roads ([type]): 3 roads with mid as the junction type. """ connection = self.connect2LaneRoads(0, 1) junction = pyodrx.Junction('test',1) junction.add_connection(connection) return junction def connect2LaneRoads(self, incomingRoadId, connectionRoadId,): """Assumes no center lane offset. Args: incomingRoadId ([type]): [description] connectionRoadId ([type]): [description] """ connection = pyodrx.Connection(incomingRoadId, connectionRoadId, pyodrx.ContactPoint.start) connection.add_lanelink(-1,-1) return connection def harvest3WayJunctionsFrom2Ways(self, ingredientsFile, maxTries = 100, randomizeAngleSelection = True): """Merges 2 way junctions into 3 ways. Args: ingredientsFile ([type]): [description] maxTries (int, optional): [description]. Defaults to 100. randomizeAngleSelection (bool, optional): If True it will pick 2R roads randomly without any angle filtering. If false, it will only connect 2R roads which have different angles. Defaults to True. Raises: NotImplementedError: [description] """ with(open(ingredientsFile, 'rb')) as f: odrDic = dill.load(f) selectedOdrs = None generatedOdrs = [] if randomizeAngleSelection: odrList = [] for angleOdrList in odrDic.values(): odrList += angleOdrList numberOfOds = len(odrList) for _ in range(maxTries): try: selectedOdrs = [odrList[np.random.choice(numberOfOds)], odrList[np.random.choice(numberOfOds)]] newOdr = self.junctionMerger.merge2R2L(selectedOdrs) generatedOdrs.append(newOdr) self.lastId += 1 except IncompatibleRoadsException: continue else: # TODO angle permutation for merging 2ways raise NotImplementedError("Angle permutation is not implemented yet") with(open(self.destinationPrefix + "harvested3R2LOrds.dill", "wb")) as f: dill.dump(generatedOdrs, f) print("Odr objects saved to " + self.destinationPrefix + "_harvested3R2LOrds.dill" ) pass def harvestByPainting2L(self, maxNumberOfRoadsPerJunction, triesPerRoadCount, show=False): """[summary] Args: maxNumberOfRoadsPerJunction ([type]): [description] triesPerRoadCount ([type]): number of junctions to be created for each set of roads. for 3 roads, we will try triesPerRoadCount, for 4 roads the same. save (bool, optional): [description]. Defaults to True. """ odrs = [] for numRoads in range(3, maxNumberOfRoadsPerJunction + 1): for _ in range(triesPerRoadCount): odr = self.sequentialJunctionBuilder.drawLikeAPainter2L(self.lastId, numRoads) self.lastId += 1 if show: extensions.view_road(odr, os.path.join('..', self.esminiPath)) xmlPath = self.getOutputPath(odr.name) odr.write_xml(xmlPath) # 2. save image if self.saveImage is True: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) odrs.append(odr) with(open(self.destinationPrefix + "harvestedByPainting2L.dill", "wb")) as f: dill.dump(odrs, f) print("Odr objects saved to " + self.destinationPrefix + "harvestedByPainting2L.dill" ) pass