class test_moreHelper(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.rootPath = self.configuration.get('rootPath') def test_saveRoadImageFromFile(self): xodrPath = self.rootPath + "\\output\\test-RightLane.xodr" outputFile = extensions.saveRoadImageFromFile( xodrPath, self.configuration.get("esminipath")) assert os.path.isfile(outputFile)
class test_Intersection(unittest.TestCase): def setUp(self): self.configuration = Configuration() outputDir = os.path.join(os.getcwd(), 'output') lastId = 0 self.seed = 2 self.builder = SequentialJunctionBuilder(minAngle=np.pi / 4, maxAngle=np.pi * .75, straightRoadLen=10, probLongConnection=0.5, probMinAngle=0.5, probRestrictedLane=0.2, maxConnectionLength=30, minConnectionLength=12, random_seed=self.seed) self.randomState = self.configuration.get("random_state") pass def test_getIncidentPoints(self): maxNumberOfRoadsPerJunction = 4 path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_FIRST, getAsOdr=False) odr = intersection.odr xmlPath = f"output/test_getIncidentPoints-split-first-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) incidentPoints = intersection.getIncidentPoints() translatedPoints = intersection.getIncidentPointsTranslatedToCenter() print(f"width and height: {intersection.getWH()}") print(incidentPoints) print(translatedPoints) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")))
class test_RotateOpenDrive(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.threeWayJunctionBuilder = ThreeWayJunctionBuilder( minAngle=np.pi / 9, maxAngle=np.pi * .25, straightRoadLen=20) def test_RotateOpenDrive(self): angleBetweenRoads = np.pi / 4 odr = self.threeWayJunctionBuilder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) # extensions.view_road(odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.printRoadPositions(odr) RotatedODR = extensions.rotateOpenDrive(odr=odr, startX=0.0, startY=0.0, heading=np.pi) extensions.printRoadPositions(RotatedODR) extensions.view_road( RotatedODR, os.path.join('..', self.configuration.get("esminipath")))
class test_JunctionMerger(unittest.TestCase): def setUp(self): self.configuration = Configuration() with(open('F:\\myProjects\\av\\junction-art\\output\\harvested2R2LOrds.dill', 'rb')) as f: self.odrDic = dill.load(f) outputDir = os.path.join(os.getcwd(), 'output') self.merger = JunctionMerger(outputDir=outputDir, outputPrefix="test_") def test_merge2R2L(self): odrs = self.odrDic['0.3141592653589793'] odrs2 = [odrs[0], odrs[2]] newOdr = self.merger.merge2R2L(odrs2) extensions.view_road(newOdr,os.path.join('..',self.configuration.get("esminipath"))) # odrs = self.odrDic['0.3141592653589793'] # odrs2 = [odrs[1], odrs[2]] # newOdr = self.merger.merge2R2L(odrs2) # extensions.view_road(newOdr,os.path.join('..',self.configuration.get("esminipath"))) # odrs = self.odrDic['0.3141592653589793'] # odrs2 = [odrs[3], odrs[4]] # newOdr = self.merger.merge2R2L(odrs2) # extensions.view_road(newOdr,os.path.join('..',self.configuration.get("esminipath"))) def test_merge2R2L2(self): odrList = [] for angleOdrList in self.odrDic.values(): odrList += angleOdrList numberOfOds = len(odrList) for _ in range(5): try: selectedOdrs = [odrList[np.random.choice(numberOfOds)], odrList[np.random.choice(numberOfOds)]] newOdr = self.merger.merge2R2L(selectedOdrs) extensions.view_road(newOdr,os.path.join('..',self.configuration.get("esminipath"))) except: pass # extensions.save_road_image(newOdr,os.path.join('..',self.configuration.get("esminipath")))
class test_CurveRoadBuilder(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.curveRoadBuilder = CurveRoadBuilder() self.roadLinker = RoadLinker() def test_SimpleCurve(self): roads = [] roads.append( self.curveRoadBuilder.create(0, angleBetweenEndpoints=np.pi / 2, curvature=0.05, isLeftTurnLane=True)) odrName = "test_LeftTurnLaneCurve" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_LeftTurnLaneCurve.xodr" odr.write_xml(xmlPath) roads = [] roads.append( self.curveRoadBuilder.create(0, angleBetweenEndpoints=np.pi / 2, curvature=0.05, isRightTurnLane=True)) odrName = "test_RightTurnLaneCurve" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_RightTurnLaneCurve.xodr" odr.write_xml(xmlPath)
class test_JunctionHarvester(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.harvester = JunctionHarvester(outputDir="./output", outputPrefix="test-harvest-", lastId=0, esminiPath=self.esminiPath) def test_harvestByPainting2L(self): self.harvester.harvestByPainting2L(maxNumberOfRoadsPerJunction=4, triesPerRoadCount=3, show=True)
class test_HDMapBuilder(unittest.TestCase): def setUp(self) -> None: self.configuration = Configuration() self.hdMapBuilder = HDMapBuilder(30, mapSize=(300, 300), cellSize=(60, 60)) with open(logfile, 'w') as f: f.truncate() pass def test_buildMap(self): name = 'first_hd_map' odr = self.hdMapBuilder.buildMap(name, plot=True) xmlPath = f"output/test_HDMapBuilder-{name}.xodr" odr.write_xml(xmlPath) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) pass
class test_JunctionBuilder(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.junctionBuilder = JunctionBuilder() self.esminiPath = self.configuration.get("esminipath") def test_buildSimpleRoundAbout(self): numRoads = 6 odr = self.junctionBuilder.buildSimpleRoundAbout( odrId=0, numRoads=numRoads, radius=10, cp1=pyodrx.ContactPoint.end) xmlPath = f"output/test-SimpleRoundAbout-{numRoads}.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) extensions.view_road(odr, os.path.join('..', self.esminiPath)) def test_buildSimpleRoundAboutAndCheckInternalConnection(self): numRoads = 3 odr = self.junctionBuilder.buildSimpleRoundAbout( odrId=0, numRoads=numRoads, radius=10, cp1=pyodrx.ContactPoint.end) xmlPath = f"output/test-SimpleRoundAbout-{numRoads}.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) extensions.view_road(odr, os.path.join('..', self.esminiPath)) # what's the problem with internal connections? internalConnection = odr.roads['12'].shallowCopy() internalConnection.clearRoadLinks() odr = extensions.createOdr("12 only", [internalConnection], []) xmlPath = f"output/test-SimpleRoundAbout-{numRoads}-road-12.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) extensions.view_road(odr, os.path.join('..', self.esminiPath))
class test_SequentialJunctionBuilder(unittest.TestCase): def setUp(self): self.configuration = Configuration() outputDir = os.path.join(os.getcwd(), 'output') lastId = 0 self.seed = 2 self.builder = SequentialJunctionBuilder(minAngle=np.pi / 10, maxAngle=np.pi * .75, straightRoadLen=5, probLongConnection=0.5, probMinAngle=0.5, probRestrictedLane=0.2, maxConnectionLength=50, minConnectionLength=20, random_seed=self.seed) self.randomState = self.configuration.get("random_state") self.validator = IntersectionValidator() pass def test_drawLikeAPainter2L(self): maxNumberOfRoadsPerJunction = 3 odr = self.builder.drawLikeAPainter2L( odrId=0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_drawLikeAPainter2L-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 4 odr = self.builder.drawLikeAPainter2L( odrId=0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_drawLikeAPainter2L-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 5 odr = self.builder.drawLikeAPainter2L( odrId=0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_drawLikeAPainter2L-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 6 odr = self.builder.drawLikeAPainter2L( odrId=0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_drawLikeAPainter2L-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) def test_drawLikeAPainter2LWihtoutInternalConnections(self): maxNumberOfRoadsPerJunction = 5 odr = self.builder.drawLikeAPainter2L( odrId=0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, internalConnections=False) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_drawLikeAPainter2LWihtoutInternalConnections.xodr" odr.write_xml(xmlPath) def test_createWithRandomLaneConfigurations(self): maxNumberOfRoadsPerJunction = 4 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_FIRST, restrictedLanes=True) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-first-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) maxNumberOfRoadsPerJunction = 3 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_LAST, uTurnLanes=1, restrictedLanes=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-last-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 5 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, restrictedLanes=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 6 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) def test_equalAngles(self): maxNumberOfRoadsPerJunction = 4 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 4 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 3 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 5 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 6 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) def test_6PlusLanes(self): maxNumberOfRoadsPerJunction = 7 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 9 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}.xodr" odr.write_xml(xmlPath) def test_TJunctions(self): maxNumberOfRoadsPerJunction = 3 maxLanePerSide = 1 minLanePerSide = 1 for sl in range(5): path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, uTurnLanes=0) # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_4WayJunctions(self): maxNumberOfRoadsPerJunction = 4 maxLanePerSide = 1 minLanePerSide = 1 for sl in range(5): path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, sl, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, getAsOdr=False) odr = intersection.odr # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) isValid = self.validator.validateIncidentPoints( intersection, self.builder.minConnectionLength) # if isValid == False: # print(f"{sl} is an invalid intersection") plt = extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")), returnPlt=True) if isValid == False: plt.title("Invalid") else: plt.title("Valid") plt.show() extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_5WayJunctions(self): maxNumberOfRoadsPerJunction = 5 maxLanePerSide = 3 minLanePerSide = 0 for sl in range(5): path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, sl, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, getAsOdr=False) odr = intersection.odr # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) isValid = self.validator.validateIncidentPoints( intersection, self.builder.minConnectionLength) # if isValid == False: # print(f"{sl} is an invalid intersection") plt = extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")), returnPlt=True) if isValid == False: plt.title("Invalid") else: plt.title("Valid") plt.show() extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_6WayJunctions(self): maxNumberOfRoadsPerJunction = 6 maxLanePerSide = 2 minLanePerSide = 0 for sl in range(5): path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, sl, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, getAsOdr=False) odr = intersection.odr # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) isValid = self.validator.validateIncidentPoints( intersection, self.builder.minConnectionLength) # if isValid == False: # print(f"{sl} is an invalid intersection") plt = extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")), returnPlt=True) if isValid == False: plt.title(f"Invalid - {xmlPath}") else: plt.title(f"Valid - {xmlPath}") plt.show() extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_7WayJunctions(self): maxNumberOfRoadsPerJunction = 7 maxLanePerSide = 2 minLanePerSide = 0 for sl in range(5): path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, sl, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, getAsOdr=False) odr = intersection.odr # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) isValid = self.validator.validateIncidentPoints( intersection, self.builder.minConnectionLength) # if isValid == False: # print(f"{sl} is an invalid intersection") plt = extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")), returnPlt=True) if isValid == False: plt.title(f"Invalid - {xmlPath}") else: plt.title(f"Valid - {xmlPath}") plt.show() extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_7WayJunctionsEQA(self): maxNumberOfRoadsPerJunction = 7 maxLanePerSide = 2 minLanePerSide = 0 for sl in range(5): path = self.configuration.get("harvested_straight_roads") intersection = self.builder.createWithRandomLaneConfigurations( path, sl, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True, getAsOdr=False) odr = intersection.odr # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) extensions.printRoadPositions(odr) isValid = self.validator.validateIncidentPoints( intersection, self.builder.minConnectionLength) # if isValid == False: # print(f"{sl} is an invalid intersection") plt = extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")), returnPlt=True) if isValid == False: plt.title(f"Invalid - {xmlPath}") else: plt.title(f"Valid - {xmlPath}") plt.show() extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_8PlusWayJunctions(self): maxNumberOfRoadsPerJunction = 20 maxLanePerSide = 2 minLanePerSide = 0 for sl in range(5): path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY) # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-{sl}.xodr" odr.write_xml(xmlPath) # extensions.view_road(odr,os.path.join('..',self.configuration.get("esminipath"))) extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) maxNumberOfRoadsPerJunction += 1 def test_4WayJunctionsEqualAngles(self): maxNumberOfRoadsPerJunction = 4 maxLanePerSide = 1 minLanePerSide = 1 for sl in range(5): path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, equalAngles=True) # xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" xmlPath = f"output/seed-{self.seed}-{maxNumberOfRoadsPerJunction}-way-eq-{sl}.xodr" odr.write_xml(xmlPath) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) extensions.saveRoadImageFromFile( xmlPath, self.configuration.get("esminipath")) def test_SafeMinConnectionLengthForEqualAngle(self): sl = 0 maxNumberOfRoadsPerJunction = 8 sl += 1 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=3, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, restrictedLanes=1, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 sl += 1 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, restrictedLanes=1, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 sl += 1 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, restrictedLanes=1, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" odr.write_xml(xmlPath) maxNumberOfRoadsPerJunction = 8 sl += 1 path = self.configuration.get("harvested_straight_roads") odr = self.builder.createWithRandomLaneConfigurations( path, 0, maxNumberOfRoadsPerJunction=maxNumberOfRoadsPerJunction, maxLanePerSide=2, minLanePerSide=0, internalConnections=True, cp1=pyodrx.ContactPoint.end, internalLinkStrategy=LaneConfigurationStrategies.SPLIT_ANY, restrictedLanes=1, equalAngles=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_createWithRandomLaneConfigurations-split-any-{maxNumberOfRoadsPerJunction}-{sl}.xodr" odr.write_xml(xmlPath)
class test_LaneLinker(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.roadBuilder = RoadBuilder() self.laneBuilder = LaneBuilder() self.laneLinker = LaneLinker() self.straightRoadBuilder = StraightRoadBuilder() self.roadLinker = RoadLinker() def test_normalRoadLinks(self): roads = [] roads.append(self.straightRoadBuilder.create(0)) roads.append( self.roadBuilder.curveBuilder.createSimple(1, np.pi / 4, False, curvature=0.2)) roads.append(self.straightRoadBuilder.create(2)) RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_connectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) # self.laneBuilder.addRightTurnLaneUS(roads[0], 3) # self.laneBuilder.addRightLaneUS(roads[1]) # odr.resetAndReadjust(byPredecessor=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) def test_DifferentCPs(self): # same cps roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=20, junction=-1, n_lanes_left=2, n_lanes_right=1)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(1, length=10, junction=-1, n_lanes_left=1, n_lanes_right=2)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.start) odrName = "test_DifferentCPs1" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_DifferentCPs1.xodr" odr.write_xml(xmlPath) # same cps 2 roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=20, junction=-1, n_lanes_left=2, n_lanes_right=1)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(1, length=10, junction=-1, n_lanes_left=2, n_lanes_right=1)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.end) odrName = "test_DifferentCPs2" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_DifferentCPs2.xodr" odr.write_xml(xmlPath)
class test_ExtendedRoad(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminipath = self.configuration.get("esminipath") self.roadBuilder = junctions.RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.roadLinker = RoadLinker() self.connectionBuilder = ConnectionBuilder() self.curveRoadBuilder = CurveRoadBuilder() def test_createSingleLaneConnectionRoad(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=10, junction=-1, n_lanes_left=2, n_lanes_right=2)) roads.append( self.curveRoadBuilder.createSimple(1, np.pi / 3, isJunction=True, n_lanes=2)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, length=10, junction=-1, n_lanes_left=2, n_lanes_right=2)) RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_createSingleLaneConnectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) # extensions.view_road(odr, os.path.join('..', self.configuration.get("esminipath"))) newConnection = self.connectionBuilder.createSingleLaneConnectionRoad( 3, roads[0], roads[2], -2, -2, pyodrx.ContactPoint.end, pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=newConnection, sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=newConnection, predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) newConnection.updatePredecessorOffset(-1) roads.append(newConnection) odr.add_road(newConnection) newConnection = self.connectionBuilder.createSingleLaneConnectionRoad( 4, roads[2], roads[0], 2, 2, pyodrx.ContactPoint.start, pyodrx.ContactPoint.end) RoadLinker.createExtendedPredSuc(predRoad=roads[2], predCp=pyodrx.ContactPoint.start, sucRoad=newConnection, sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=newConnection, predCp=pyodrx.ContactPoint.end, sucRoad=roads[0], sucCP=pyodrx.ContactPoint.end) newConnection.updatePredecessorOffset(1) roads.append(newConnection) odr.add_road(newConnection) odr.resetAndReadjust(byPredecessor=True) extensions.printRoadPositions(odr) extensions.view_road(odr, os.path.join('..', self.esminipath)) xmlPath = f"output/test_createSingleLaneConnectionRoad.xodr" odr.write_xml(xmlPath)
from classifiers.ClassifierManager import Classifiermanager from flask import request import numpy as np import pprint from library.Configuration import Configuration from dataProcessors.DoodleDataStats import DoodleDataStats import cv2 import numpy as np import math from gpt_run import RunGPT from random import randint configuration = Configuration() classifierManager = Classifiermanager(configuration) dataStats = DoodleDataStats() dataStats.load(configuration.get('doodle.stats')) classes = list(dataStats.stats['classes'].keys()) printer = pprint.PrettyPrinter(indent=4) routesBluePrint = Blueprint('routes', __name__, template_folder=None) responseProcessor = ResponseProcessor(debug=True) @routesBluePrint.route('/') def hello_world(): data = 'Hello World' return responseProcessor.makeResponse(data) @routesBluePrint.route("/api/doodle2story") def doodle2Story():
class test_ExtendedOpenDrive(unittest.TestCase): def setUp(self): 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_AdjustStartPointsForAnyRoadCombinations(self): # test scenario for connection road roads = [] roads.append(pyodrx.create_straight_road(0, 10)) roads.append( self.roadBuilder.curveBuilder.createSimpleCurveWithLongArc( 1, np.pi / 4, False, curvature=0.2)) roads.append(pyodrx.create_straight_road(2, 10)) roads.append( self.roadBuilder.curveBuilder.createSimpleCurveWithLongArc( 3, np.pi / 3, True, curvature=0.2)) roads.append(pyodrx.create_straight_road(4, 10)) roads.append( self.roadBuilder.curveBuilder.createSimpleCurveWithLongArc( 5, np.pi / 2, True, curvature=0.2)) roads.append(pyodrx.create_straight_road(6, 10)) roads[0].add_successor(pyodrx.ElementType.road, 1, pyodrx.ContactPoint.start) # 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.road, 1, pyodrx.ContactPoint.end) 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.resetAndReadjust() extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) pass
class LaneBuilder: def __init__(self): self.config = Configuration() self.defaultLaneWidth = self.config.get("default_lane_width") self.name = 'LaneBuilder' def getStandardLanes(self, n_lanes, lane_offset, laneSides=LaneSides.BOTH, roadLength = None, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False): """[summary] Don't allow both merge lanes and turn lanes in a road. Better split to two roads TODO allow merge and turn in opposite sides of a road Args: n_lanes ([type]): [description] lane_offset ([type]): width laneSides ([type], optional): where to put lanes wrt center lane. Defaults to LaneSides.BOTH. Returns: ExtenndedLanes: Road with one lane section if there is no merge or turn lanes. 3 sections otherwise. In case of turns, the first section will have no turn lanes. In case of merges, the last section will have no merge lanes. In case of a single side, all the turns or merge will be added on the single side only. Otherside will have no lanes """ if laneSides != LaneSides.BOTH: return self.getStandardTurnsOnSingleSide(n_lanes, lane_offset, laneSides, roadLength=roadLength, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane ) elif self.anyTurnOrMerge(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane): return self.getStandardLanesWithInternalTurns(n_lanes, lane_offset, laneSides, roadLength=roadLength, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane, ) else: return self.getStandardLanesWithDifferentLeftAndRight(n_lanes, n_lanes, lane_offset) def anyTurnOrMerge(self, isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane): return isLeftTurnLane or isRightTurnLane or isLeftMergeLane or isRightMergeLane def getStandardLanesWithDifferentLeftAndRight(self, n_lanes_left, n_lanes_right, lane_offset, singleSide=False ): """[summary] Args: n_lanes_left ([type]): [description] n_lanes_right ([type]): [description] lane_offset ([type]): [description] Returns: ExtendedLanes : An extended lanes object with 3 lane sections. """ firstSec = self.getStandardLaneSection(0, n_lanes_left, n_lanes_right, lane_offset, singleSide=singleSide) laneSections = extensions.ExtendedLanes() laneSections.add_lanesection(firstSec) return laneSections def getStandardTurnsOnSingleSide(self, n_lanes, lane_offset, laneSide=LaneSides.RIGHT, roadLength = None, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False, laneOffset = 0): """[summary] Don't allow both merge lanes and turn lanes in a road. Better split to two roads TODO allow merge and turn in opposite sides of a road Args: n_lanes ([type]): [description] lane_offset ([type]): width laneSides ([type], optional): where to put lanes wrt center lane. Defaults to LaneSides.RIGHT. Returns: [type]: Road with one lane section if there is no merge or turn lanes. 3 sections otherwise. In case of turns, the first section will have no turn lanes. In case of merges, the last section will have no merge lanes. One side will have no lanes. """ if laneSide == LaneSides.BOTH: raise Exception(f"Lanes side can be left or right only.") self.checkTurnAndMergeConflict(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane) self.checkRoadLengthRequirement(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane, roadLength) n_lanes_left = 0 n_lanes_right = 0 if laneSide == LaneSides.RIGHT: n_lanes_right = n_lanes else: n_lanes_left = n_lanes firstSec = self.getStandardLaneSection(0, n_lanes_left, n_lanes_right, lane_offset=lane_offset, singleSide=True) extendedLanes = extensions.ExtendedLanes() extendedLanes.add_lanesection(firstSec) if self.anyTurnOrMerge(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane): turnOffSet, finalOffset, curveLaneLength = self.getOffsetsAndTurnLaneCurveLength(roadLength) midSecWithTurns = self.getStandardLaneSection(turnOffSet, n_lanes_left, n_lanes_right, lane_offset=lane_offset, singleSide=True) finalSection = self.getStandardLaneSection(finalOffset, n_lanes_left, n_lanes_right, lane_offset=lane_offset, singleSide=True) # 1 add the turn Section # 2 final section should have no turns. turnLaneOffset = None finalLaneOffset = None if laneSide == LaneSides.RIGHT: if isLeftTurnLane: # we need to change the center lane offset for mid and final lane = self.createLinearSplitLane(TurnTypes.LEFT, lane_offset, curveLaneLength) midSecWithTurns.prependLaneToRightLanes(lane) finalSection.prependLaneToRightLanes(self.createStandardDrivingLane(lane_offset)) # now the offsets # 1. one for turnOffset # 2. one for finalOffset turnLaneOffset = LaneOffset.createLinear(turnOffSet, maxWidth=lane_offset, laneLength=curveLaneLength) finalLaneOffset = LaneOffset.createParallel(finalOffset, a=lane_offset) if isRightTurnLane: lane = self.createLinearSplitLane(TurnTypes.RIGHT, lane_offset, curveLaneLength) midSecWithTurns.add_right_lane(lane) finalSection.add_right_lane(self.createStandardDrivingLane(lane_offset)) if laneSide == LaneSides.LEFT: if isLeftTurnLane: lane = self.createLinearSplitLane(TurnTypes.LEFT, lane_offset, curveLaneLength) midSecWithTurns.add_left_lane(lane) finalSection.add_left_lane(self.createStandardDrivingLane(lane_offset)) if isRightTurnLane: # we need to change the center lane offset for mid and final lane = self.createLinearSplitLane(TurnTypes.RIGHT, lane_offset, curveLaneLength) midSecWithTurns.prependLaneToLeftLanes(lane) finalSection.prependLaneToLeftLanes(self.createStandardDrivingLane(lane_offset)) # now the offsets # 1. one for turnOffset # 2. one for finalOffset turnLaneOffset = LaneOffset.createLinear(turnOffSet, maxWidth=-lane_offset, laneLength=curveLaneLength) finalLaneOffset = LaneOffset.createParallel(finalOffset, a=-lane_offset) extendedLanes.add_lanesection(midSecWithTurns) extendedLanes.add_lanesection(finalSection) self.addLaneOffsets(extendedLanes, turnLaneOffset, finalLaneOffset) return extendedLanes def getStandardLanesWithInternalTurns(self, n_lanes, lane_offset, laneSides=LaneSides.BOTH, roadLength=None, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False, numberOfLeftTurnLanesOnRight=0, numberOfRightTurnLanesOnLeft=0, mergeLaneOnTheOppositeSideForInternalTurn=True): """Will create numberOfRightTurnLanesOnLeft right turn lanes on the left side of the center line. Equal number of mergelanes will be created on the right side of the center lane, too. Will create numberOfLeftTurnLanesOnRight left turn lanes on the right side of the center line. Equal number of mergelanes will be created on the left side of the center lane, too Args: n_lanes ([type]): [description] lane_offset ([type]): [description] laneSides ([type], optional): [description]. Defaults to LaneSides.BOTH. roadLength ([type], optional): [description]. Defaults to None. isLeftTurnLane (bool, optional): [description]. Defaults to False. isRightTurnLane (bool, optional): [description]. Defaults to False. isLeftMergeLane (bool, optional): [description]. Defaults to False. isRightMergeLane (bool, optional): [description]. Defaults to False. numberOfLeftTurnLanesOnRight (int, optional): [description]. Defaults to 1. numberOfRightTurnLanesOnLeft (int, optional): [description]. Defaults to 1. """ if numberOfLeftTurnLanesOnRight > 0 and numberOfRightTurnLanesOnLeft > 0: raise Exception(f"Cannot add internal turn lanes on both sides.") self.checkTurnAndMergeConflict(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane) self.checkRoadLengthRequirement(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane, roadLength) turnOffSet, finalOffset, curveLaneLength = self.getOffsetsAndTurnLaneCurveLength(roadLength) extendedLanes = self.get3SectionLanes(roadLength, turnOffSet, finalOffset, n_lanes_left=n_lanes, n_lanes_right=n_lanes, lane_offset=lane_offset) firstSection = extendedLanes.lanesections[0] midSection = extendedLanes.lanesections[1] finalSection = extendedLanes.lanesections[-1] # Add turn lanes section if necessary self.createTurnLanesAtTheEdges(isLeftTurnLane, isRightTurnLane, lane_offset, curveLaneLength, midSection, finalSection) self.createMergeLanesAtTheEdges(isLeftMergeLane, isRightMergeLane, lane_offset, curveLaneLength, midSection, firstSection) if numberOfRightTurnLanesOnLeft > 0: self.createRightTurnLanesOnLeft(numberOfRightTurnLanesOnLeft, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset) elif numberOfLeftTurnLanesOnRight > 0: self.createLeftTurnLanesOnRight(numberOfLeftTurnLanesOnRight, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset) return extendedLanes def getLanes(self, n_lanes_left, n_lanes_right, lane_offset = None, singleSide=False, roadLength=None, numLeftTurnsOnLeft=0, numRightTurnsOnRight=0, numLeftMergeOnLeft=0, numRightMergeOnRight=0, numberOfLeftTurnLanesOnRight=0, numberOfRightTurnLanesOnLeft=0, mergeLaneOnTheOppositeSideForInternalTurn=True, force3Section=False): """Always returns 3 lane sections if there is a turn or merge. """ if lane_offset is None: lane_offset = self.defaultLaneWidth if (force3Section is False and numLeftTurnsOnLeft == 0 and numRightTurnsOnRight == 0 and numLeftMergeOnLeft == 0 and numRightMergeOnRight == 0 and numberOfLeftTurnLanesOnRight == 0 and numberOfRightTurnLanesOnLeft == 0): return self.getStandardLanesWithDifferentLeftAndRight(n_lanes_left, n_lanes_right, lane_offset, singleSide) if numberOfLeftTurnLanesOnRight > 0 and numberOfRightTurnLanesOnLeft > 0: raise Exception(f"Cannot add internal turn lanes on both sides.") if roadLength is None: raise Exception("road length require for getLanes") # self.checkTurnAndMergeConflict(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane) # self.checkRoadLengthRequirement(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane, roadLength) turnOffSet, finalOffset, curveLaneLength = self.getOffsetsAndTurnLaneCurveLength(roadLength) extendedLanes = self.get3SectionLanes(roadLength, turnOffSet, finalOffset, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right, lane_offset=lane_offset) firstSection = extendedLanes.lanesections[0] midSection = extendedLanes.lanesections[1] finalSection = extendedLanes.lanesections[-1] # TODO refactor to input number of turns self.createLeftTurnLanesOnLeftEdge(numLeftTurnsOnLeft, lane_offset, curveLaneLength, midSection, finalSection) self.createRightTurnLanesOnRightEdge(numRightTurnsOnRight, lane_offset, curveLaneLength, midSection, finalSection) self.createLeftMergesOnLeftEdge(numLeftMergeOnLeft, lane_offset, curveLaneLength, midSection, firstSection) self.createRightMergesOnRightEdge(numRightMergeOnRight, lane_offset, curveLaneLength, midSection, firstSection) if numberOfRightTurnLanesOnLeft > 0: self.createRightTurnLanesOnLeft(numberOfRightTurnLanesOnLeft, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset) elif numberOfLeftTurnLanesOnRight > 0: self.createLeftTurnLanesOnRight(numberOfLeftTurnLanesOnRight, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset) return extendedLanes def createLeftTurnLanesOnRight(self, numberOfLeftTurnLanesOnRight, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset): for _ in range(numberOfLeftTurnLanesOnRight): # 1. we need to change the center lane offset for mid and final lane = self.createLinearSplitLane(TurnTypes.LEFT, lane_offset, curveLaneLength) midSection.prependLaneToRightLanes(lane) finalSection.prependLaneToRightLanes(self.createStandardDrivingLane(lane_offset)) if mergeLaneOnTheOppositeSideForInternalTurn: # 2. add a merge lane to left lanes lane = self.createLinearMergeLane(lane_offset, curveLaneLength) midSection.prependLaneToLeftLanes(lane) firstSection.prependLaneToLeftLanes(self.createStandardDrivingLane(lane_offset)) # now the offsets # 1. one for turnOffset # 2. one for finalOffset # we need to shift center lane up. turnLaneOffset = LaneOffset.createLinear(turnOffSet, maxWidth=lane_offset * numberOfLeftTurnLanesOnRight, laneLength=curveLaneLength) finalLaneOffset = LaneOffset.createParallel(finalOffset, a=lane_offset * numberOfLeftTurnLanesOnRight) self.addLaneOffsets(extendedLanes, turnLaneOffset, finalLaneOffset) pass def createRightTurnLanesOnLeft(self, numberOfRightTurnLanesOnLeft, extendedLanes, lane_offset, curveLaneLength, firstSection, midSection, finalSection, mergeLaneOnTheOppositeSideForInternalTurn, turnOffSet, finalOffset): for _ in range(numberOfRightTurnLanesOnLeft): # 1. we need to change the center lane offset for mid and final lane = self.createLinearSplitLane(TurnTypes.RIGHT, lane_offset, curveLaneLength) midSection.prependLaneToLeftLanes(lane) finalSection.prependLaneToLeftLanes(self.createStandardDrivingLane(lane_offset)) if mergeLaneOnTheOppositeSideForInternalTurn: # 2. add a merge lane to left lanes lane = self.createLinearMergeLane(lane_offset, curveLaneLength) midSection.prependLaneToRightLanes(lane) firstSection.prependLaneToRightLanes(self.createStandardDrivingLane(lane_offset)) # now the offsets # 1. one for turnOffset # 2. one for finalOffset # we need to shift center lane down. turnLaneOffset = LaneOffset.createLinear(turnOffSet, maxWidth=-lane_offset * numberOfRightTurnLanesOnLeft, laneLength=curveLaneLength) finalLaneOffset = LaneOffset.createParallel(finalOffset, a=-lane_offset * numberOfRightTurnLanesOnLeft) self.addLaneOffsets(extendedLanes, turnLaneOffset, finalLaneOffset) pass def checkRoadLengthRequirement(self, isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane, roadLength): if self.anyTurnOrMerge(isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane): if roadLength is None: raise Exception("Road length cannot be None for turn lanes") def checkTurnAndMergeConflict(self, isLeftTurnLane, isRightTurnLane, isLeftMergeLane, isRightMergeLane): if (isLeftTurnLane or isRightTurnLane) and (isLeftMergeLane or isRightMergeLane): raise Exception("merge lane and turn lanes cannot appear in the same road. Please split the road into two for simpler calculations.") def getOffsetsAndTurnLaneCurveLength(self, roadLength): """returns soffsets for turn slope start and end Args: roadLength ([type]): [description] Returns: [type]: [description] """ turnOffSet = 1 finalOffset = roadLength - 1 # where the final lanesection resides. laneLength = finalOffset - turnOffSet return turnOffSet, finalOffset, laneLength def addLaneOffsets(self, extendedLanes, turnLaneOffset, finalLaneOffset): if turnLaneOffset is not None: extendedLanes.addLaneOffset(turnLaneOffset) if finalLaneOffset is not None: extendedLanes.addLaneOffset(finalLaneOffset) pass def createLeftTurnLanesOnLeftEdge(self, numLeftTurnsOnLeft, maxWidth, laneLength, midSection, finalSection): for _ in range(numLeftTurnsOnLeft): lane = self.createLinearSplitLane(TurnTypes.LEFT, maxWidth, laneLength) midSection.add_left_lane(lane) finalSection.add_left_lane(self.createStandardDrivingLane(maxWidth)) pass def createRightTurnLanesOnRightEdge(self, numRightTurnsOnRight, maxWidth, laneLength, midSection, finalSection): for _ in range(numRightTurnsOnRight): lane = self.createLinearSplitLane(TurnTypes.RIGHT, maxWidth, laneLength) midSection.add_right_lane(lane) finalSection.add_right_lane(self.createStandardDrivingLane(maxWidth)) pass def createTurnLanesAtTheEdges(self, isLeftTurnLane, isRightTurnLane, maxWidth, laneLength, midSection, finalSection): """curve to the midSection, and parallel to the final section. Args: isLeftTurnLane (bool): [description] isRightTurnLane (bool): [description] maxWidth ([type]): [description] laneLength ([type]): [description] midSection ([type]): [description] finalSection ([type]): [description] """ if isLeftTurnLane: lane = self.createLinearSplitLane(TurnTypes.LEFT, maxWidth, laneLength) midSection.add_left_lane(lane) finalSection.add_left_lane(self.createStandardDrivingLane(maxWidth)) if isRightTurnLane: lane = self.createLinearSplitLane(TurnTypes.RIGHT, maxWidth, laneLength) midSection.add_right_lane(lane) finalSection.add_right_lane(self.createStandardDrivingLane(maxWidth)) pass def createLeftMergesOnLeftEdge(self, numLeftMergeOnLeft, maxWidth, laneLength, midSection, firstSection): for _ in range(numLeftMergeOnLeft): lane = self.createLinearMergeLane(maxWidth, laneLength) midSection.add_left_lane(lane) firstSection.add_left_lane(self.createStandardDrivingLane(maxWidth)) pass def createRightMergesOnRightEdge(self, numRightMergeOnRight, maxWidth, laneLength, midSection, firstSection): for _ in range(numRightMergeOnRight): lane = self.createLinearMergeLane(maxWidth, laneLength) midSection.add_right_lane(lane) firstSection.add_right_lane(self.createStandardDrivingLane(maxWidth)) pass def createMergeLanesAtTheEdges(self, isLeftMergeLane, isRightMergeLane, maxWidth, laneLength, midSection, firstSection): if isLeftMergeLane: lane = self.createLinearMergeLane(maxWidth, laneLength) midSection.add_left_lane(lane) firstSection.add_left_lane(self.createStandardDrivingLane(maxWidth)) if isRightMergeLane: lane = self.createLinearMergeLane(maxWidth, laneLength) midSection.add_right_lane(lane) firstSection.add_right_lane(self.createStandardDrivingLane(maxWidth)) pass def getStandardLaneSection(self, soffset, n_lanes_left, n_lanes_right, lane_offset, singleSide=False ): lsec = extensions.ExtendedLaneSection(soffset, self.createStandardDrivingLane(lane_offset), singleSide=singleSide) for _ in range(n_lanes_left): lsec.add_left_lane(self.createStandardDrivingLane(lane_offset)) for _ in range(n_lanes_right): lsec.add_right_lane(self.createStandardDrivingLane(lane_offset)) return lsec def createStandardDrivingLane(self, laneWidth): lane = ExtendedLane(a=laneWidth) lane.add_roadmark(STD_ROADMARK) return lane def createLane(self, laneType, a, b=0, c=0, d=0): lane = ExtendedLane(lane_type=laneType, a=a, b=b, c=c, d=d) lane.add_roadmark(STD_ROADMARK) return lane def get3SectionLanes(self, roadLength, turnOffSet, finalOffset, n_lanes_left=1, n_lanes_right=1, lane_offset=3): firstSec = self.getStandardLaneSection(0, n_lanes_left, n_lanes_right, lane_offset=lane_offset) midSection = self.getStandardLaneSection(turnOffSet, n_lanes_left, n_lanes_right, lane_offset=lane_offset) finalSection = self.getStandardLaneSection(finalOffset, n_lanes_left, n_lanes_right, lane_offset=lane_offset) extendedLanes = extensions.ExtendedLanes() extendedLanes.add_lanesection(firstSec) extendedLanes.add_lanesection(midSection) extendedLanes.add_lanesection(finalSection) return extendedLanes ### Section : Adding Lanes to existing roads def addOutgoingLanes(self, road, cp, num, countryCode, laneWidth=3): if countryCode == extensions.CountryCodes.US: if cp == pyodrx.ContactPoint.start: for _ in range(num): self.addRightLaneUS(road, laneWidth=laneWidth) else: for _ in range(num): self.addLefLaneUS(road, laneWidth=laneWidth) return raise NotImplementedError("Only us are implemented") def addIncomingLanes(self, road, cp, num, countryCode, laneWidth=3): if countryCode == extensions.CountryCodes.US: if cp == pyodrx.ContactPoint.start: for _ in range(num): self.addLefLaneUS(road, laneWidth=laneWidth) else: for _ in range(num): self.addRightLaneUS(road, laneWidth=laneWidth) return raise NotImplementedError("Only us are implemented") def createLinearSplitLane(self, turnType, maxWidth, laneLength, soffset=0, laneOffset = 0, laneType=pyodrx.LaneType.driving): if laneLength is None: raise Exception("Lane length cannot be None for turn lanes") if maxWidth is None: raise Exception("maxWidth cannot be None for turn lanes") a = laneOffset b = (maxWidth / laneLength) # c = 0 # c = .1 * (maxWidth / laneLength) lane = ExtendedLane(lane_type=laneType, soffset=soffset, a=a, b=b, turnType=turnType) return lane def addLeftLane(self, road, laneWidth = 3, soffset=0, countryCode=extensions.CountryCodes.US): if countryCode == extensions.CountryCodes.US: return self.addLefLaneUS(road, laneWidth, soffset=soffset) raise NotImplementedError("Only us turns are implemented") def addLeftTurnLane(self, road, maxWidth, laneLength = None, countryCode=extensions.CountryCodes.US): """Assumes that the last lane section is longer than laneLength """ if countryCode == extensions.CountryCodes.US: return self.addLeftTurnLaneUS(road, maxWidth, laneLength) raise NotImplementedError("Only us turns are implemented") def addLefLaneUS(self, road, laneWidth = 3, soffset=0): """Assumes that the last lane section is longer than laneLength """ laneSections = road.getLaneSections() for laneSection in laneSections: lane = ExtendedLane(soffset=soffset, a=laneWidth) laneSection.add_left_lane(lane) pass def addLeftTurnLaneUS(self, road, maxWidth, laneLength = None): """Assumes that the last lane section is longer than laneLength """ soffset = 0 if laneLength is not None: soffset = road.length() - laneLength else: laneLength = road.length() lane = self.createLinearSplitLane(TurnTypes.LEFT, maxWidth, laneLength, soffset) # 2. add lane laneSection = road.getLastLaneSection() laneSection.add_left_lane(lane) raise NotImplementedError("addLeftTurnLaneForUS not implemented") def addRightLane(self, road, laneWidth = 3, soffset=0, countryCode=extensions.CountryCodes.US): if countryCode == extensions.CountryCodes.US: return self.addRightLaneUS(road, laneWidth, soffset=soffset) raise NotImplementedError("Only us turns are implemented") def addRightTurnLane(self, road, maxWidth, laneLength = None, countryCode=extensions.CountryCodes.US): """Assumes that the last lane section is longer than laneLength """ if countryCode == extensions.CountryCodes.US: return self.addRightTurnLaneUS(road, maxWidth, laneLength) raise NotImplementedError("Only us turns are implemented") def addRightTurnLaneUS(self, road, maxWidth, laneLength = None): """Assumes that the last lane section is longer than laneLength Will not work for 3 lane section plan """ # 1. define lane equation params soffset = 0 if laneLength is not None: soffset = road.length() - laneLength else: laneLength = road.length() lane = self.createLinearSplitLane(TurnTypes.RIGHT, maxWidth, laneLength, soffset) # 2. add lane laneSection = road.getLastLaneSection() laneSection.add_right_lane(lane) pass def addRightLaneUS(self, road, laneWidth = 3, soffset=0): """Assumes that the last lane section is longer than laneLength """ laneSections = road.getLaneSections() for laneSection in laneSections: lane = ExtendedLane(soffset=soffset, a=laneWidth) laneSection.add_right_lane(lane) pass ### Section: Merge Lanes def createLinearMergeLane(self, maxWidth, laneLength, soffset=0, laneType=pyodrx.LaneType.driving): if laneLength is None: raise Exception("Lane length cannot be None for turn lanes") if maxWidth is None: raise Exception("maxWidth cannot be None for turn lanes") a = maxWidth b = -(maxWidth / laneLength) # c = 0 # c = .1 * (maxWidth / laneLength) lane = ExtendedLane(lane_type=laneType, soffset=soffset, a=a, b=b) return lane ### Section: Connecting roads def createLanesForConnectionRoad(self, connectionRoad: ExtendedRoad, predRoad: ExtendedRoad, sucRoad: ExtendedRoad, strategy = LaneConfigurationStrategies.MERGE_EDGE, countryCode=extensions.CountryCodes.US): """Assumes start of connection road is connected to predRoad and end to sucRoad and connection road's lanes are not connected to either of the roads. It can connect roads with two different lane configurations. Args: connectionRoad (ExtendedRoad): predRoad (ExtendedRoad): Extended predecessor road of connectionRoad. That means connection road's start is connected to predRoad sucRoad (ExtendedRoad): Extended successor road of connectionRoad. That means connection road's end is connected to sucRoad strategy ([type], optional): [description]. Defaults to LaneConfigurationStrategies.MERGE_EDGE. """ try: cp1, cp1Con = RoadLinker.getContactPoints(predRoad, connectionRoad) cp2, cp2Con = RoadLinker.getContactPoints(sucRoad, connectionRoad) laneSection1 = predRoad.getLaneSectionByCP(cp1) laneSection2 = sucRoad.getLaneSectionByCP(cp2) connectionRoad.clearLanes() leftConnections, rightConnections = LaneConfiguration.getLaneLinks(laneSection1, laneSection2, (cp1 == cp2), strategy) # now we need to workout the number of straight lanes, merge lanes, and turn lanes on each side. # switch lane sides if cp1 and cp1Con are the same, because the lane orientation is reversed if cp1 == cp1Con: leftNumStandard, leftNumMerge, leftNumTurn = LaneConfiguration.getNumberDifferentLanes(rightConnections) rightNumStandard, rightNumMerge, rightNumTurn = LaneConfiguration.getNumberDifferentLanes(leftConnections) else: leftNumStandard, leftNumMerge, leftNumTurn = LaneConfiguration.getNumberDifferentLanes(leftConnections) rightNumStandard, rightNumMerge, rightNumTurn = LaneConfiguration.getNumberDifferentLanes(rightConnections) connectionRoad.lanes = self.getLanes(n_lanes_left=leftNumStandard, n_lanes_right=rightNumStandard, roadLength=connectionRoad.length(), numLeftTurnsOnLeft=leftNumTurn, numLeftMergeOnLeft=leftNumMerge, numRightTurnsOnRight= rightNumTurn, numRightMergeOnRight=rightNumMerge) except Exception as e: raise e # Section Non-driving lanes. def addMedianIslandsToAllSections(self, road, width, laneType=pyodrx.LaneType.restricted): # 1. check if islands exists on left and right side. lanes = road.lanes for ls in lanes.lanesections: self.addMedianIslandsToSection(road, ls, width, laneType) pass def addMedianIslandsToSection(self, road, ls, width, laneType=pyodrx.LaneType.restricted): # lane = self.createLinearTurnLane(TurnTypes.RIGHT, lane_offset, curveLaneLength) # midSection.prependLaneToLeftLanes(lane) # finalSection.prependLaneToLeftLanes(self.createStandardDrivingLane(lane_offset)) if len(ls.leftlanes) > 0: ls.prependLaneToLeftLanes(self.createLane(laneType, a=width/2)) if len(ls.rightlanes) > 0: ls.prependLaneToRightLanes(self.createLane(laneType, a=width/2)) pass def addMedianIslandsTo2Of3Sections(self, road, roadLength, skipEndpoint, width, laneType=pyodrx.LaneType.restricted): """[summary] Args: road ([type]): [description] roadLength ([type]): [description] skipSection ([type]): valid values are: pyodrx ContactPoints. No new lane will be added to this end point. width ([type]): [description] laneType ([type], optional): [description]. Defaults to pyodrx.LaneType.restricted. Raises: Exception: [description] """ # 1. check if islands exists on left and right side. extendedLanes = road.lanes if len(extendedLanes.lanesections) != 3: raise Exception(f"{self.name}: addMedianIslandsTo3Sections only works on 3 lane section roads") if roadLength is None or roadLength < 3: raise Exception(f"{self.name}: addMedianIslandsTo3Sections requires roadLength >= 3") firstSection = extendedLanes.lanesections[0] midSection = extendedLanes.lanesections[1] finalSection = extendedLanes.lanesections[-1] turnOffSet, finalOffset, curveLaneLength = self.getOffsetsAndTurnLaneCurveLength(roadLength) if skipEndpoint == pyodrx.ContactPoint.start: if len(midSection.leftlanes) > 0: increasingLaneL = self.createLinearSplitLane(None, maxWidth=width/2, laneLength=curveLaneLength, laneType=laneType) midSection.prependLaneToLeftLanes(increasingLaneL) if len(midSection.rightlanes) > 0: increasingLaneR = self.createLinearSplitLane(None, maxWidth=width/2, laneLength=curveLaneLength, laneType=laneType) midSection.prependLaneToRightLanes(increasingLaneR) self.addMedianIslandsToSection(road, finalSection, width, laneType=laneType) else: if len(midSection.leftlanes) > 0: decreasingLaneL = self.createLinearMergeLane(maxWidth=width/2, laneLength=curveLaneLength, laneType=laneType) midSection.prependLaneToLeftLanes(decreasingLaneL) if len(midSection.rightlanes) > 0: decreasingLaneR = self.createLinearMergeLane(maxWidth=width/2, laneLength=curveLaneLength, laneType=laneType) midSection.prependLaneToRightLanes(decreasingLaneR) self.addMedianIslandsToSection(road, firstSection, width, laneType=laneType) pass def getClockwiseAdjacentLanes(self, firstRoad, firstCp, secondRoad, secondCP): firstLanes = firstRoad.getLaneSectionByCP(firstCp).rightlanes if firstCp == pyodrx.ContactPoint.end: firstLanes = firstRoad.getLaneSectionByCP(firstCp).leftlanes secondLanes = secondRoad.getLaneSectionByCP(secondCP).leftlanes if secondCP == pyodrx.ContactPoint.end: secondLanes = secondRoad.getLaneSectionByCP(secondCP).rightlanes return firstLanes, secondLanes
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")))
class test_StraightRoadHarvester(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.harvester = StraightRoadHarvester(outputDir="./output", outputPrefix="harvest-straight-", lastId=0, straightRoadLen=10, esminiPath=self.esminiPath) self.straightRoadBuilder = StraightRoadBuilder() self.pp = pprint.PrettyPrinter(indent=4) def test_getPossibleTurnsWRTLaneOnRight(self): possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.RIGHT) assert len(possibleTurns) == 6 possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.STRAIGHT_RIGHT) assert len(possibleTurns) == 3 possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.STRAIGHT) print(possibleTurns) assert len(possibleTurns) == 3 possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.STRAIGHT_LEFT) print(possibleTurns) assert len(possibleTurns) == 1 possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.LEFT) print(possibleTurns) assert len(possibleTurns) == 1 possibleTurns = self.harvester.getPossibleTurnsWRTLaneOnRight(TurnTypes.ALL) print(possibleTurns) assert len(possibleTurns) == 1 def test_getLaneTurnCombinations(self): combinations = self.harvester.getLaneTurnCombinations(1) # self.pp.pprint(combinations) assert len(combinations) == 6 combinations = self.harvester.getLaneTurnCombinations(2) self.pp.pprint(combinations) print(len(combinations)) assert len(combinations) == 15 def test_applyTurnCombinationOnLanes(self): road = self.straightRoadBuilder.createWithDifferentLanes(0, length=10, n_lanes_left=2, n_lanes_right=2) leftCombinations = self.harvester.getLaneTurnCombinations(2) rightCombinations = leftCombinations laneSectionForLeft = road.getFirstLaneSection() laneSectionForRight = road.getLastLaneSection() self.harvester.applyTurnCombinationOnLanes(laneSectionForLeft.leftlanes, leftCombinations[0]) self.harvester.applyTurnCombinationOnLanes(laneSectionForRight.rightlanes, rightCombinations[0]) assert laneSectionForLeft.leftlanes[0].turnType == leftCombinations[0][0] assert laneSectionForLeft.leftlanes[0].turnType == rightCombinations[0][0] def test_harvestUS(self): odrs = self.harvester.harvestUS(2, 2, False) print(len(odrs)) def test_harvest(self): self.harvester.harvest(maxLeftLanes=2, maxRightLanes=2, countryCode=CountryCodes.US)
class StraightRoadBuilder: 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() self.configuration = Configuration() self.name = 'StraightRoadBuilder' pass def createPVForLine(self, length): line1 = pyodrx.Line(length) # create planviews pv = extensions.ExtendedPlanview() pv.add_geometry(line1) return pv def createRandom(self, roadId, randomState=None, length=20, junction=-1, lane_offset=None, maxLanePerSide=2, minLanePerSide=0, turns=False, merges=False, medianType=None, medianWidth=3, skipEndpoint=None, force3Section=False): if randomState is not None: np.random.set_state(randomState) if lane_offset is None: lane_offset = self.configuration.get("default_lane_width") if maxLanePerSide < 1: raise Exception( f"{self.name}: createRandom: maxLanePerSide cannot be less than 1" ) laneRange = np.arange(minLanePerSide, maxLanePerSide + 1) n_lanes_left = np.random.choice(laneRange) n_lanes_right = np.random.choice(laneRange) if (n_lanes_left == 0) and (n_lanes_right == 0): return self.createRandom(roadId, randomState=randomState, length=length, junction=junction, lane_offset=lane_offset, maxLanePerSide=maxLanePerSide, minLanePerSide=minLanePerSide, turns=turns, merges=merges, medianType=medianType, medianWidth=3, skipEndpoint=skipEndpoint, force3Section=force3Section) numLeftTurnsOnLeft = 0 numRightTurnsOnRight = 0 numLeftMergeOnLeft = 0 numRightMergeOnRight = 0 numberOfLeftTurnLanesOnRight = 0 numberOfRightTurnLanesOnLeft = 0 mergeLaneOnTheOppositeSideForInternalTurn = np.random.choice( [True, False]) if turns: numLeftTurnsOnLeft = np.random.choice([0, 1]) numRightTurnsOnRight = np.random.choice([0, 1]) numberOfLeftTurnLanesOnRight = np.random.choice([0, 1]) numberOfRightTurnLanesOnLeft = np.random.choice([0, 1]) elif merges: numLeftMergeOnLeft = np.random.choice([0, 1]) numRightMergeOnRight = np.random.choice([0, 1]) if medianType is None: return self.create( roadId, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right, length=length, junction=junction, lane_offset=lane_offset, laneSides=LaneSides.BOTH, numLeftTurnsOnLeft=numLeftTurnsOnLeft, numRightTurnsOnRight=numRightTurnsOnRight, numLeftMergeOnLeft=numLeftMergeOnLeft, numRightMergeOnRight=numRightMergeOnRight, numberOfLeftTurnLanesOnRight=numberOfLeftTurnLanesOnRight, numberOfRightTurnLanesOnLeft=numberOfRightTurnLanesOnLeft, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn, force3Section=force3Section) else: return self.createWithMedianRestrictedLane( roadId, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right, length=length, junction=junction, lane_offset=lane_offset, laneSides=LaneSides.BOTH, numLeftTurnsOnLeft=numLeftTurnsOnLeft, numRightTurnsOnRight=numRightTurnsOnRight, numLeftMergeOnLeft=numLeftMergeOnLeft, numRightMergeOnRight=numRightMergeOnRight, numberOfLeftTurnLanesOnRight=numberOfLeftTurnLanesOnRight, numberOfRightTurnLanesOnLeft=numberOfRightTurnLanesOnLeft, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn, medianType=medianType, medianWidth=medianWidth, skipEndpoint=skipEndpoint) pass def create(self, roadId, n_lanes_left=1, n_lanes_right=1, length=20, junction=-1, lane_offset=3, laneSides=LaneSides.BOTH, numLeftTurnsOnLeft=0, numRightTurnsOnRight=0, numLeftMergeOnLeft=0, numRightMergeOnRight=0, numberOfLeftTurnLanesOnRight=0, numberOfRightTurnLanesOnLeft=0, mergeLaneOnTheOppositeSideForInternalTurn=True, force3Section=False): # create geometry pv = self.createPVForLine(length) # laneSections = self.laneBuilder.getStandardLanes(n_lanes, lane_offset, laneSides, # roadLength=length, # isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, # isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane) singleSide = False if laneSides != LaneSides.BOTH: singleSide = True laneSections = self.laneBuilder.getLanes( n_lanes_left, n_lanes_right, lane_offset=lane_offset, singleSide=singleSide, roadLength=length, numLeftTurnsOnLeft=numLeftTurnsOnLeft, numRightTurnsOnRight=numRightTurnsOnRight, numLeftMergeOnLeft=numLeftMergeOnLeft, numRightMergeOnRight=numRightMergeOnRight, numberOfLeftTurnLanesOnRight=numberOfLeftTurnLanesOnRight, numberOfRightTurnLanesOnLeft=numberOfRightTurnLanesOnLeft, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn, force3Section=force3Section) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction) return road def createWithMedianRestrictedLane( self, roadId, n_lanes_left=1, n_lanes_right=1, length=20, junction=-1, lane_offset=3, laneSides=LaneSides.BOTH, numLeftTurnsOnLeft=0, numRightTurnsOnRight=0, numLeftMergeOnLeft=0, numRightMergeOnRight=0, numberOfLeftTurnLanesOnRight=0, numberOfRightTurnLanesOnLeft=0, mergeLaneOnTheOppositeSideForInternalTurn=True, medianType='partial', medianWidth=3, skipEndpoint=None): road = self.create( roadId, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right, length=length, junction=junction, lane_offset=lane_offset, laneSides=laneSides, numLeftTurnsOnLeft=numLeftTurnsOnLeft, numRightTurnsOnRight=numRightTurnsOnRight, numLeftMergeOnLeft=numLeftMergeOnLeft, numRightMergeOnRight=numRightMergeOnRight, numberOfLeftTurnLanesOnRight=numberOfLeftTurnLanesOnRight, numberOfRightTurnLanesOnLeft=numberOfRightTurnLanesOnLeft, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn, force3Section=True) if medianType == 'partial': if skipEndpoint is None: raise Exception( f"{self.name}: createWithMedianRestrictedLane skipEndpoint cannot be None for partial median lanes." ) self.laneBuilder.addMedianIslandsTo2Of3Sections( road, roadLength=length, skipEndpoint=skipEndpoint, width=medianWidth) else: self.laneBuilder.addMedianIslandsToAllSections(road, width=medianWidth) return road def createWithRightTurnLanesOnLeft( self, roadId, length=100, junction=-1, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False, numberOfRightTurnLanesOnLeft=1, mergeLaneOnTheOppositeSideForInternalTurn=True): # create geometry pv = self.createPVForLine(length) laneSections = self.laneBuilder.getStandardLanesWithInternalTurns( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane, numberOfRightTurnLanesOnLeft=numberOfRightTurnLanesOnLeft, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction) return road def createWithLeftTurnLanesOnRight( self, roadId, length=100, junction=-1, n_lanes=1, lane_offset=3, laneSides=LaneSides.BOTH, isLeftTurnLane=False, isRightTurnLane=False, isLeftMergeLane=False, isRightMergeLane=False, numberOfLeftTurnLanesOnRight=1, mergeLaneOnTheOppositeSideForInternalTurn=True): """Will create numberOfLeftTurnLanesOnRight left turn lanes on the right side of the center line. Equal number of mergelanes will be created on the left side of the center lane, too. Args: roadId ([type]): [description] length (int, optional): [description]. Defaults to 100. junction (int, optional): [description]. Defaults to -1. n_lanes (int, optional): [description]. Defaults to 1. lane_offset (int, optional): [description]. Defaults to 3. laneSides ([type], optional): [description]. Defaults to LaneSides.BOTH. isLeftTurnLane (bool, optional): [description]. Defaults to False. isRightTurnLane (bool, optional): [description]. Defaults to False. isLeftMergeLane (bool, optional): [description]. Defaults to False. isRightMergeLane (bool, optional): [description]. Defaults to False. numberOfLeftTurnLanesOnRight (int, optional): [description]. Defaults to 1. Returns: [type]: [description] """ # create geometry pv = self.createPVForLine(length) laneSections = self.laneBuilder.getStandardLanesWithInternalTurns( n_lanes, lane_offset, laneSides, roadLength=length, isLeftTurnLane=isLeftTurnLane, isRightTurnLane=isRightTurnLane, isLeftMergeLane=isLeftMergeLane, isRightMergeLane=isRightMergeLane, numberOfLeftTurnLanesOnRight=numberOfLeftTurnLanesOnRight, mergeLaneOnTheOppositeSideForInternalTurn= mergeLaneOnTheOppositeSideForInternalTurn) road = ExtendedRoad(roadId, pv, laneSections, road_type=junction) return road def createWithDifferentLanes(self, roadId, length=100, junction=-1, n_lanes_left=1, n_lanes_right=1, lane_offset=3, force3Section=False): return self.create(roadId, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right, length=length, junction=junction, lane_offset=lane_offset, force3Section=force3Section)
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 ConnectionBuilder: def __init__(self): self.config = Configuration() self.countryCode = CountryCodes.getByStr( self.config.get("countryCode")) self.curveBuilder = CurveRoadBuilder() self.name = "ConnectionBuilder" self.uTurnFirstLaneShift = self.config.get("uturn_firstlane_shift") def createSingleLaneConnectionRoad(self, newRoadId, incomingRoad, outgoingRoad, incomingLaneId, outgoingLaneId, incomingCp, outgoingCp): """Warining: uses default lane width. Works only after roads has been adjusted. Args: incomingRoad ([type]): [description] outgoingRoad ([type]): [description] incomingLaneId ([type]): [description] outgoingLaneId ([type]): [description] incomingCp ([type]): [description] outgoingCp ([type]): [description] """ laneSides = None if self.countryCode == CountryCodes.US: laneSides = LaneSides.RIGHT if self.countryCode == CountryCodes.UK: laneSides = LaneSides.LEFT incomingBoundaryId = incomingLaneId - 1 if incomingLaneId < 0: incomingBoundaryId = incomingLaneId + 1 outgoingBoundaryId = outgoingLaneId - 1 if outgoingLaneId < 0: outgoingBoundaryId = outgoingLaneId + 1 # TODO, get lane widths from road and create an equation. width = self.config.get("default_lane_width") x1, y1, h1 = incomingRoad.getLanePosition(incomingBoundaryId, incomingCp) x2, y2, h2 = outgoingRoad.getLanePosition(outgoingBoundaryId, outgoingCp) # special case for U turns from -1 to 1 or 1 to -1 if x1 == x2 and y1 == y2: # x1 = 0.9 * x1 # y1 = 0.9 * y1 width -= self.uTurnFirstLaneShift logging.debug(f"{self.name}: createSingleLaneConnectionRoad: start: ", x1, y1, h1) logging.debug(f"{self.name}: createSingleLaneConnectionRoad: end: ", x2, y2, h2) xCoeffs, yCoeffs = Geometry.getCoeffsForParamPoly( x1, y1, h1, x2, y2, h2, incomingCp, outgoingCp, vShiftForSamePoint=self.uTurnFirstLaneShift) # scipy coefficient and open drive coefficents have opposite order. newConnection = self.curveBuilder.createParamPoly3(newRoadId, isJunction=True, au=xCoeffs[3], bu=xCoeffs[2], cu=xCoeffs[1], du=xCoeffs[0], av=yCoeffs[3], bv=yCoeffs[2], cv=yCoeffs[1], dv=yCoeffs[0], n_lanes=1, lane_offset=width, laneSides=laneSides) newConnection.predecessorOffset = incomingBoundaryId newConnection.isSingleLaneConnection = True RoadLinker.createExtendedPredSuc(predRoad=incomingRoad, predCp=incomingCp, sucRoad=newConnection, sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=newConnection, predCp=pyodrx.ContactPoint.end, sucRoad=outgoingRoad, sucCP=outgoingCp) return newConnection def createSingleLaneConnectionRoads(self, nextRoadId, outsideRoads, cp1, strategy): """Assumes all roads are connected by start point except for the first one Args: outsideRoads ([type]): [description] cp1 ([type]): [description] Returns: [type]: [description] """ # return [] roadDic = {} for road in outsideRoads: roadDic[road.id] = road newConnectionRoads = [] firstRoadId = outsideRoads[0].id countOldRoads = len(outsideRoads) # count = 0 for incomingRoad in outsideRoads: # count += 1 # if count == 1: # continue incomingLaneIds = [] if firstRoadId == incomingRoad.id: incomingLaneIds = LaneConfiguration.getIncomingLaneIdsOnARoad( incomingRoad, cp1, self.countryCode) else: incomingLaneIds = LaneConfiguration.getIncomingLaneIdsOnARoad( incomingRoad, pyodrx.ContactPoint.start, self.countryCode) outgoingLaneIds = LaneConfiguration.getOutgoingLanesIdsFromARoad( incomingRoad, outsideRoads, cp1=cp1, countryCode=self.countryCode) try: linkConfig = LaneConfiguration.getIntersectionLinks1ToMany( incomingLaneIds, outgoingLaneIds, strategy=strategy) # for each link, create a new connection road connectionRoadsForConfig = self.createRoadsForLinkConfig( nextRoadId, roadDic, firstRoadId, incomingRoad, cp1, linkConfig) nextRoadId += len(connectionRoadsForConfig) newConnectionRoads += connectionRoadsForConfig except Exception as e: logging.warn(f"{self.name}: {e}") raise e # break return newConnectionRoads def createUTurnConnectionRoads( self, nextRoadId, outsideRoads, cp1, strategy=LaneConfigurationStrategies.SPLIT_FIRST): # return [] roadDic = {} for road in outsideRoads: roadDic[road.id] = road newConnectionRoads = [] firstRoadId = outsideRoads[0].id incomingLaneIds = [] cp = None for incomingRoad in outsideRoads: if firstRoadId == incomingRoad.id: cp = cp1 else: cp = pyodrx.ContactPoint.start incomingLaneIds = LaneConfiguration.getIncomingLaneIdsOnARoad( incomingRoad, cp, self.countryCode) outgoingLaneIds = LaneConfiguration.getOutgoingLaneIdsOnARoad( incomingRoad, cp, self.countryCode) if len(incomingLaneIds) == 0 or len(outgoingLaneIds) == 0: continue incomingLaneIds = [incomingLaneIds[0]] # only the median lane try: linkConfig = LaneConfiguration.getIntersectionLinks1ToMany( incomingLaneIds, outgoingLaneIds, strategy=strategy) connectionRoadsForConfig = self.createRoadsForLinkConfig( nextRoadId, roadDic, firstRoadId, incomingRoad, cp1, linkConfig) nextRoadId += len(connectionRoadsForConfig) newConnectionRoads += connectionRoadsForConfig except Exception as e: logging.warn(f"{self.name}: {e}") raise e return newConnectionRoads def createRoadsForLinkConfig(self, nextRoadId, roadDic, firstRoadId, incomingRoad, cp1, linkConfig): newConnectionRoads = [] for link in linkConfig: fromUniqueLaneId = link[0] incomingLaneId = int(fromUniqueLaneId.split(':')[1]) toUniqueLaneId = link[1] outgoingRoadId = int(toUniqueLaneId.split(':')[0]) outgoingLaneId = int(toUniqueLaneId.split(':')[1]) outgoingRoad = roadDic[outgoingRoadId] if firstRoadId == incomingRoad.id and firstRoadId == outgoingRoad.id: # for U-turns newConnection = self.createSingleLaneConnectionRoad( nextRoadId, incomingRoad, outgoingRoad, incomingLaneId, outgoingLaneId, cp1, cp1) elif firstRoadId == incomingRoad.id: newConnection = self.createSingleLaneConnectionRoad( nextRoadId, incomingRoad, outgoingRoad, incomingLaneId, outgoingLaneId, cp1, pyodrx.ContactPoint.start) elif firstRoadId == outgoingRoad.id: newConnection = self.createSingleLaneConnectionRoad( nextRoadId, incomingRoad, outgoingRoad, incomingLaneId, outgoingLaneId, pyodrx.ContactPoint.start, cp1) else: newConnection = self.createSingleLaneConnectionRoad( nextRoadId, incomingRoad, outgoingRoad, incomingLaneId, outgoingLaneId, pyodrx.ContactPoint.start, pyodrx.ContactPoint.start) newConnectionRoads.append(newConnection) nextRoadId += 1 logging.debug(f"{self.name}: created connection for link {link}") return newConnectionRoads
class StraightRoadHarvester: def __init__(self, outputDir, outputPrefix, lastId=0, straightRoadBuilder=None, straightRoadLen = 2, esminiPath = None, saveImage = False ): self.destinationPrefix = os.path.join(outputDir, outputPrefix) self.configuration = Configuration() self.lastId = lastId self.straightRoadLen = straightRoadLen self.straightRoadBuilder = straightRoadBuilder if straightRoadBuilder is None: self.straightRoadBuilder = StraightRoadBuilder() 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 harvest(self, maxLeftLanes=2, maxRightLanes=2, countryCode=CountryCodes.US, show=False): """[summary] Args: maxLeftLanes (int, optional): [description]. Defaults to 2. maxRightLanes (int, optional): [description]. Defaults to 2. countryCode ([type], optional): [description]. Defaults to CountryCodes.US. show (bool, optional): [description]. Defaults to False. Raises: NotImplementedError: [description] Returns: dic with keys like "n_lanes_left-n_lanes_right" and list of odrs per key """ if countryCode != CountryCodes.US: raise NotImplementedError("Only US is implemented") # permutations # no merge or extension # each lane can have one of the 5 traffic direction. odrs = {} # values are a list of odrs for each key for l in range(maxLeftLanes + 1): for r in range(maxRightLanes + 1): if l == 0 and r == 0: continue odrs[f"{l}-{r}"] = self.harvestUS(l, r, show) # do not change the key convention # Save the odrs objectPath = self.destinationPrefix + f"{countryCode.value}.dill" with(open(objectPath, "wb")) as f: dill.dump(odrs, f) print("Odr objects saved to " + objectPath) print(f"keys {odrs.keys()}") return objectPath def harvestUS(self, n_lanes_left=2, n_lanes_right=2, show=False): # incoming lanes in a junction are right lanes if end point is connected, left lanes if start point is connected. # 5x5x5x5 for 2, 2 print(f"harvestUS with {n_lanes_left} and {n_lanes_right}") odrs = [] # now iterate through lanes and set types. leftCombinations = self.getLaneTurnCombinations(n_lanes_left) rightCombinations = self.getLaneTurnCombinations(n_lanes_right) # for each combinations on left and right, create a new road for leftComb in leftCombinations: for rightComb in rightCombinations: road = self.straightRoadBuilder.createWithDifferentLanes(self.lastId, length=self.straightRoadLen, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right) # right lanes, change last lane secion # left lanes, change first lane section. laneSectionForLeft = road.getFirstLaneSection() laneSectionForRight = road.getLastLaneSection() self.applyTurnCombinationOnLanes(laneSectionForLeft.leftlanes, leftComb) self.applyTurnCombinationOnLanes(laneSectionForRight.rightlanes, rightComb) name = f"straightRoad-{self.lastId}" self.lastId += 1 odr = extensions.createOdrByPredecessor(name, roads=[road], junctions=[]) # 1. save the xml file fname = odr.name xmlPath = self.getOutputPath(fname) odr.write_xml(xmlPath) # 2. save image if self.saveImage is True: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) if show: extensions.view_road(odr, os.path.join('..', self.esminiPath)) odrs.append(odr) # handle cases where we dont need right lanes if n_lanes_right == 0: for leftComb in leftCombinations: road = self.straightRoadBuilder.createWithDifferentLanes(self.lastId, length=self.straightRoadLen, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right) # right lanes, change last lane secion # left lanes, change first lane section. laneSectionForLeft = road.getFirstLaneSection() self.applyTurnCombinationOnLanes(laneSectionForLeft.leftlanes, leftComb) name = f"straightRoad-{self.lastId}" self.lastId += 1 odr = extensions.createOdrByPredecessor(name, roads=[road], junctions=[]) # 1. save the xml file fname = odr.name xmlPath = self.getOutputPath(fname) odr.write_xml(xmlPath) # 2. save image if self.saveImage is True: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) if show: extensions.view_road(odr, os.path.join('..', self.esminiPath)) odrs.append(odr) # handle cases where we dont need left lanes if n_lanes_left == 0: for rightComb in rightCombinations: road = self.straightRoadBuilder.createWithDifferentLanes(self.lastId, length=self.straightRoadLen, n_lanes_left=n_lanes_left, n_lanes_right=n_lanes_right) # right lanes, change last lane secion # left lanes, change first lane section. laneSectionForRight = road.getLastLaneSection() self.applyTurnCombinationOnLanes(laneSectionForRight.rightlanes, rightComb) name = f"straightRoad-{self.lastId}" self.lastId += 1 odr = extensions.createOdrByPredecessor(name, roads=[road], junctions=[]) # 1. save the xml file fname = odr.name xmlPath = self.getOutputPath(fname) odr.write_xml(xmlPath) # 2. save image if self.saveImage is True: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) if show: extensions.view_road(odr, os.path.join('..', self.esminiPath)) odrs.append(odr) return odrs def applyTurnCombinationOnLanes(self, lanes, combination): for i in range(len(lanes)): lanes[i].turnType = combination[i] pass def getLaneTurnCombinations(self, n): """[summary] Args: n ([type]): [description] Returns: [type]: List of combinations. each combination is an ordered list """ # from left to right if n == 0: return [] if n == 1: return [[i] for i in TurnTypes.getAll()] combinations = [] childrenCombinations = self.getLaneTurnCombinations(n-1) for childComb in childrenCombinations: possibleTurns = self.getPossibleTurnsWRTLaneOnRight(childComb[0]) # push into child comb for possibleTurn in possibleTurns: childCopy = childComb.copy() childCopy.insert(0, possibleTurn) combinations.append(childCopy) return combinations def getPossibleTurnsWRTLaneOnRight(self, turnOnRight): if turnOnRight == TurnTypes.RIGHT: return TurnTypes.getAll() if turnOnRight in [TurnTypes.STRAIGHT_RIGHT, TurnTypes.STRAIGHT]: return (TurnTypes.LEFT, TurnTypes.STRAIGHT_LEFT, TurnTypes.STRAIGHT) return (TurnTypes.LEFT, ) # for ALL or LEFT types
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
class test_ExtendedRoad(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.roadBuilder = junctions.RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.roadLinker = RoadLinker() def test_getArcAngle(self): for _ in range(1, 10): for i in range(1, 10): inputAngle = (np.pi * i) / 10 road = self.roadBuilder.createRandomCurve(0, inputAngle) if road.curveType == StandardCurveTypes.S: continue outputAngle = road.getArcAngle() deviation = abs(inputAngle - outputAngle) * 100 / inputAngle print( f"curveType: {road.curveType} inputAngle: {math.degrees(inputAngle)} outputAngle: {math.degrees(outputAngle)} deviation: {deviation}" ) assert deviation < 50.0 def test_getBorderDistanceOfLane(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=10, junction=-1, n_lanes_left=1, n_lanes_right=1)) roads.append( self.straightRoadBuilder.createWithRightTurnLanesOnLeft( 1, length=10, n_lanes=1, junction=1, isLeftTurnLane=True, isRightTurnLane=True, numberOfRightTurnLanesOnLeft=2, mergeLaneOnTheOppositeSideForInternalTurn=False)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, length=10, junction=-1, n_lanes_left=4, n_lanes_right=2)) self.roadLinker.linkConsecutiveRoadsWithNoBranches(roads) assert roads[0].getBorderDistanceOfLane(1, pyodrx.ContactPoint.start) == 3 assert roads[0].getBorderDistanceOfLane(-1, pyodrx.ContactPoint.start) == 3 assert roads[1].getBorderDistanceOfLane(1, pyodrx.ContactPoint.start) == 3 assert roads[1].getBorderDistanceOfLane(2, pyodrx.ContactPoint.end) == 6 assert roads[1].getBorderDistanceOfLane(3, pyodrx.ContactPoint.end) == 9 assert roads[1].getBorderDistanceOfLane(4, pyodrx.ContactPoint.end) == 12 assert roads[1].getBorderDistanceOfLane(-1, pyodrx.ContactPoint.start) == 3 roads[1].updatePredecessorOffset(-1) odrName = "test_getBorderDistanceOfLane" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_getBorderDistanceOfLane.xodr" odr.write_xml(xmlPath) roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=10, junction=-1, n_lanes_left=2, n_lanes_right=2)) roads.append( self.straightRoadBuilder.createWithRightTurnLanesOnLeft( 1, length=10, n_lanes=2, junction=1, isLeftTurnLane=True, isRightTurnLane=True, numberOfRightTurnLanesOnLeft=2, mergeLaneOnTheOppositeSideForInternalTurn=False)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, length=10, junction=-1, n_lanes_left=5, n_lanes_right=3)) self.roadLinker.linkConsecutiveRoadsWithNoBranches(roads) roads[1].updatePredecessorOffset(-2) odrName = "test_getBorderDistanceOfLane" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_getBorderDistanceOfLane.xodr" odr.write_xml(xmlPath) def test_getLanePosition(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, length=10, junction=-1, n_lanes_left=2, n_lanes_right=2)) roads.append( self.straightRoadBuilder.createWithRightTurnLanesOnLeft( 1, length=10, n_lanes=2, junction=1, isLeftTurnLane=True, isRightTurnLane=True, numberOfRightTurnLanesOnLeft=2, mergeLaneOnTheOppositeSideForInternalTurn=False)) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, length=10, junction=-1, n_lanes_left=5, n_lanes_right=3)) self.roadLinker.linkConsecutiveRoadsWithNoBranches(roads) roads[1].updatePredecessorOffset(-2) odrName = "test_getBorderDistanceOfLane" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.printRoadPositions(odr) print(roads[0].getLanePosition(0, pyodrx.ContactPoint.end)) print(roads[0].getLanePosition(1, pyodrx.ContactPoint.end)) print(roads[0].getLanePosition(2, pyodrx.ContactPoint.end)) positionLeftMost = roads[0].getLanePosition(2, pyodrx.ContactPoint.end) assert positionLeftMost[0] == 10.0 assert positionLeftMost[1] == 6.0 assert positionLeftMost[2] == 0 extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath")))
class test_LaneBuilder(unittest.TestCase): 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 test_RightLane(self): # test scenario for connection road roads = [] roads.append(pyodrx.create_straight_road(0, 10)) # roads.append(self.roadBuilder.createSimpleCurve(1, np.pi/4, True, curvature = 0.2)) # 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, pyodrx.ContactPoint.end) odrName = "test_connectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) self.laneBuilder.addRightTurnLaneUS(roads[0], 3) # self.laneBuilder.addRightLaneUS(roads[1]) odr.resetAndReadjust(byPredecessor=True) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test-RightLane.xodr" odr.write_xml(xmlPath) def test_DifferentLaneConfigurations(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, 10, n_lanes_left=1, n_lanes_right=1)) connectionRoad = self.straightRoadBuilder.createWithDifferentLanes( 1, 10, n_lanes_left=2, n_lanes_right=2) roads.append(connectionRoad) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, 10, n_lanes_left=1, n_lanes_right=2)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.end) roads[1].addExtendedSuccessor(roads[2], 0, pyodrx.ContactPoint.start) roads[2].addExtendedPredecessor(roads[1], 0, pyodrx.ContactPoint.end) self.laneBuilder.createLanesForConnectionRoad(connectionRoad, roads[0], roads[2]) odrName = "test_DifferentLaneConfigurations" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_DifferentLaneConfigurations.xodr" odr.write_xml(xmlPath) def test_addMedianIslandsToAllSections(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, 10, n_lanes_left=1, n_lanes_right=1)) self.laneBuilder.addMedianIslandsToAllSections( roads[0], self.configuration.get('default_lane_width')) odrName = "test_DifferentLaneConfigurations" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_addMedianIslandsToAllSections.xodr" odr.write_xml(xmlPath) def test_addMedianIslandsTo3Sections(self): road = self.straightRoadBuilder.create(1, n_lanes_left=1, n_lanes_right=1, length=20, force3Section=False) try: self.laneBuilder.addMedianIslandsTo2Of3Sections( road, 20, skipEndpoint=pyodrx.ContactPoint.start, width=3) assert False except: assert True road = self.straightRoadBuilder.create(1, n_lanes_left=1, n_lanes_right=1, length=20, force3Section=True) self.laneBuilder.addMedianIslandsTo2Of3Sections( road, 20, skipEndpoint=pyodrx.ContactPoint.start, width=3) assert len(road.lanes.lanesections[0].leftlanes) == 1 assert len(road.lanes.lanesections[0].rightlanes) == 1 assert len(road.lanes.lanesections[1].leftlanes) == 2 assert len(road.lanes.lanesections[1].rightlanes) == 2 assert len(road.lanes.lanesections[2].leftlanes) == 2 assert len(road.lanes.lanesections[2].rightlanes) == 2 road = self.straightRoadBuilder.create(1, n_lanes_left=1, n_lanes_right=1, length=20, force3Section=True) self.laneBuilder.addMedianIslandsTo2Of3Sections( road, 20, skipEndpoint=pyodrx.ContactPoint.end, width=3) assert len(road.lanes.lanesections[0].leftlanes) == 2 assert len(road.lanes.lanesections[0].rightlanes) == 2 assert len(road.lanes.lanesections[1].leftlanes) == 2 assert len(road.lanes.lanesections[1].rightlanes) == 2 assert len(road.lanes.lanesections[2].leftlanes) == 1 assert len(road.lanes.lanesections[2].rightlanes) == 1 odrName = "test_DifferentLaneConfigurations" odr = extensions.createOdrByPredecessor(odrName, [road], []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_addMedianIslandsTo3Sections.xodr" odr.write_xml(xmlPath)
class test_NonOverlappingCurve(unittest.TestCase): def setUp(self): self.roadBuilder = RoadBuilder() self.junctionBuilder = JunctionBuilder() self.configuration = Configuration() self.angleCurvatureMap = AngleCurvatureMap() self.esminiPath = self.configuration.get("esminipath") def test_NonOverlappingCurve(self): numberofLanes = 10 laneOffset = 3 # angle = 120 # curve, angle = AngleCurvatureMap.getCurvatureForNonOverlappingRoads(angle, numberofLanes, laneOffset) angle = np.pi * 0.75 # curve, angle = AngleCurvatureMap.getCurvatureForNonOverlappingRoads(angle, numberofLanes, laneOffset) curve = AngleCurvatureMap.getMaxCurvatureAgainstMaxRoadWidth( angle, numberofLanes * laneOffset) roads = [] roads.append( pyodrx.create_straight_road(0, 50, n_lanes=numberofLanes, lane_offset=laneOffset)) roads.append( self.roadBuilder. createSimpleCurveWithLongArcWithLaneNumberandOffset( 1, angle, False, curvature=curve, _n_lanes=numberofLanes, _lane_offset=laneOffset)) roads.append( pyodrx.create_straight_road(2, 50, n_lanes=numberofLanes, lane_offset=laneOffset)) roads[0].add_successor(pyodrx.ElementType.road, 1, pyodrx.ContactPoint.start) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.end) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.road, 1, pyodrx.ContactPoint.end) odrName = "curve_test" odr = extensions.createOdr(odrName, roads, []) extensions.view_road(odr, self.esminiPath) def test_getCurvatureForAngleAndLength(self): numberofLanes = 10 laneOffset = 3 # angle = 120 # curve, angle = AngleCurvatureMap.getCurvatureForNonOverlappingRoads(angle, numberofLanes, laneOffset) angle = np.pi * 1.2 length = 20 # curve, angle = AngleCurvatureMap.getCurvatureForNonOverlappingRoads(angle, numberofLanes, laneOffset) maxCurve = AngleCurvatureMap.getMaxCurvatureAgainstMaxRoadWidth( angle, numberofLanes * laneOffset) curve = AngleCurvatureMap.getCurvatureForAngleAndLength(angle, length) print(f"max curve {maxCurve}, current curve {curve}") roads = [] roads.append( pyodrx.create_straight_road(0, 50, n_lanes=numberofLanes, lane_offset=laneOffset)) roads.append( self.roadBuilder. createSimpleCurveWithLongArcWithLaneNumberandOffset( 1, angle, False, curvature=curve, _n_lanes=numberofLanes, _lane_offset=laneOffset)) roads.append( pyodrx.create_straight_road(2, 50, n_lanes=numberofLanes, lane_offset=laneOffset)) roads[0].add_successor(pyodrx.ElementType.road, 1, pyodrx.ContactPoint.start) roads[1].add_predecessor(pyodrx.ElementType.road, 0, pyodrx.ContactPoint.end) roads[1].add_successor(pyodrx.ElementType.road, 2, pyodrx.ContactPoint.start) roads[2].add_predecessor(pyodrx.ElementType.road, 1, pyodrx.ContactPoint.end) odrName = "curve_test" odr = extensions.createOdr(odrName, roads, []) extensions.view_road(odr, self.esminiPath)
class test_RoadLiner(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminiPath = self.configuration.get("esminipath") self.roadBuilder = RoadBuilder() self.laneBuilder = LaneBuilder() self.laneLinker = LaneLinker() self.straightRoadBuilder = StraightRoadBuilder() self.curveBuilder = CurveRoadBuilder() self.roadLinker = RoadLinker() def test_getAngleBetweenStraightRoads(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, 10, n_lanes_left=1, n_lanes_right=1)) connectionRoad = self.curveBuilder.create(1, np.pi / 4, isJunction=True, curvature=0.2, n_lanes=1) roads.append(connectionRoad) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, 10, n_lanes_left=1, n_lanes_right=2)) connectionRoad2 = self.curveBuilder.create(3, np.pi / 2, isJunction=True, curvature=0.1, n_lanes=1) roads.append(connectionRoad2) roads.append( self.straightRoadBuilder.createWithDifferentLanes(4, 10, n_lanes_left=1, n_lanes_right=1)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.end) roads[1].addExtendedSuccessor(roads[2], 0, pyodrx.ContactPoint.start) roads[2].addExtendedPredecessor(roads[1], 0, pyodrx.ContactPoint.end) roads[2].addExtendedSuccessor(roads[3], 0, pyodrx.ContactPoint.start) roads[3].addExtendedPredecessor(roads[2], 0, pyodrx.ContactPoint.start) roads[3].addExtendedSuccessor(roads[4], 0, pyodrx.ContactPoint.start) roads[4].addExtendedPredecessor(roads[3], 0, pyodrx.ContactPoint.end) self.laneBuilder.createLanesForConnectionRoad(connectionRoad, roads[0], roads[2]) self.laneBuilder.createLanesForConnectionRoad(connectionRoad2, roads[2], roads[4]) odrName = "test_DifferentLaneConfigurations" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[0], roads[2]))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[0], roads[4]))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[2], roads[4]))) # xmlPath = f"output/test_DifferentLaneConfigurations.xodr" # odr.write_xml(xmlPath) def test_StartEnd(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, 10, n_lanes_left=1, n_lanes_right=1)) connectionRoad = self.curveBuilder.create(1, np.pi / 4, isJunction=True, curvature=0.1, n_lanes=1) roads.append(connectionRoad) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, 10, n_lanes_left=1, n_lanes_right=1)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.start) roads[1].addExtendedSuccessor(roads[2], 0, pyodrx.ContactPoint.start) roads[2].addExtendedPredecessor(roads[1], 0, pyodrx.ContactPoint.end) self.laneBuilder.createLanesForConnectionRoad(connectionRoad, roads[0], roads[2]) odrName = "test_StartEnd" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[0], roads[2]))) xmlPath = f"output/test_StartEnd.xodr" odr.write_xml(xmlPath) def test_fails(self): roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes(0, 10, n_lanes_left=1, n_lanes_right=1)) connectionRoad = self.curveBuilder.create(1, np.pi / 4, isJunction=True, curvature=0.2, n_lanes=1) roads.append(connectionRoad) roads.append( self.straightRoadBuilder.createWithDifferentLanes(2, 10, n_lanes_left=1, n_lanes_right=2)) connectionRoad2 = self.curveBuilder.create(3, np.pi / 2, isJunction=True, curvature=0.1, n_lanes=1) roads.append(connectionRoad2) roads.append( self.straightRoadBuilder.createWithDifferentLanes(4, 10, n_lanes_left=1, n_lanes_right=3)) roads[0].addExtendedSuccessor(roads[1], 0, pyodrx.ContactPoint.start) roads[1].addExtendedPredecessor(roads[0], 0, pyodrx.ContactPoint.end) roads[1].addExtendedSuccessor(roads[2], 0, pyodrx.ContactPoint.start) roads[2].addExtendedPredecessor(roads[1], 0, pyodrx.ContactPoint.end) roads[2].addExtendedSuccessor(roads[3], 0, pyodrx.ContactPoint.start) roads[3].addExtendedPredecessor(roads[2], 0, pyodrx.ContactPoint.start) roads[3].addExtendedSuccessor(roads[4], 0, pyodrx.ContactPoint.start) roads[4].addExtendedPredecessor(roads[3], 0, pyodrx.ContactPoint.end) self.laneBuilder.createLanesForConnectionRoad(connectionRoad, roads[0], roads[2]) self.laneBuilder.createLanesForConnectionRoad(connectionRoad2, roads[2], roads[4]) odrName = "test_DifferentLaneConfigurations" odr = extensions.createOdrByPredecessor(odrName, roads, []) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[0], roads[2]))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[0], roads[4]))) print( math.degrees( self.roadLinker.getAngleBetweenStraightRoads( roads[2], roads[4])))
class test_AngleCurvatureMap(unittest.TestCase): def setUp(self): self.configuration = Configuration() self.esminipath = self.configuration.get("esminipath") self.roadBuilder = junctions.RoadBuilder() self.straightRoadBuilder = StraightRoadBuilder() self.roadLinker = RoadLinker() self.connectionBuilder = ConnectionBuilder() self.curveRoadBuilder = CurveRoadBuilder() def test_getCurvatureForAngleAndLength(self): numberofLanes = 5 laneOffset = 3 # angle = np.pi * .75 # angle = np.pi * (5/6) # length = 15 # maxCurve = AngleCurvatureMap.getMaxCurvatureMaxRoadWidth(angle, numberofLanes * laneOffset) # curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength(angle, length, StandardCurveTypes.Simple) # # curve = 0.066666667 # print(f"max curve {maxCurve}, current curve {curve}") # roads = [] # roads.append(self.straightRoadBuilder.createWithDifferentLanes(0, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) # # roads.append(self.curveRoadBuilder.createSimpleCurveWithLongArc(1, angleBetweenEndpoints = angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) # roads.append(self.curveRoadBuilder.createSimple(1, angleBetweenEndpoints = angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) # roads.append(self.straightRoadBuilder.createWithDifferentLanes(2, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) # RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) # RoadLinker.createExtendedPredSuc(predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) # odrName = "test_createSingleLaneConnectionRoad" # odr = extensions.createOdrByPredecessor(odrName, roads, []) # # extensions.printRoadPositions(odr) # road = roads[1] # print(f"{road.id} has length {road.planview.get_total_length()}") # # for geom in road.planview._adjusted_geometries: # # print(geom.length) # assert round(road.planview.get_total_length(), 0) == length # extensions.view_road(odr, os.path.join('..', self.esminipath)) angle = 0.5 while angle < np.pi: length = 100 while length > 0: curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, length, StandardCurveTypes.Simple) roads = [] curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, length, StandardCurveTypes.Simple) roads.append( self.straightRoadBuilder.createWithDifferentLanes( 0, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) roads.append( self.curveRoadBuilder.createSimple( 1, angleBetweenEndpoints=angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.straightRoadBuilder.createWithDifferentLanes( 2, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) RoadLinker.createExtendedPredSuc( predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc( predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_createSingleLaneConnectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) road = roads[1] assert round(road.planview.get_total_length(), 0) == length curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, length, StandardCurveTypes.Simple) roads = [] curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, length, StandardCurveTypes.Simple) roads.append( self.curveRoadBuilder.createSimpleCurveWithLongArc( 1, angleBetweenEndpoints=angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.curveRoadBuilder.createSimple( 1, angleBetweenEndpoints=angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.straightRoadBuilder.createWithDifferentLanes( 2, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) RoadLinker.createExtendedPredSuc( predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc( predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_createSingleLaneConnectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) road = roads[1] assert round(road.planview.get_total_length(), 0) == length length -= 10 angle += 0.25 def test_getMaxCurvatureMaxRoadWidth(self): roads = [] numberofLanes = 1 laneOffset = 3 angle = np.pi * .75 maxCurve = AngleCurvatureMap.getMaxCurvatureAgainstMaxRoadWidth( angle, numberofLanes * laneOffset) curve = maxCurve * 1.1 print(f"max curve {maxCurve}, current curve {curve}") roads.append( self.straightRoadBuilder.createWithDifferentLanes( 0, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) roads.append( self.curveRoadBuilder.createSimpleCurveWithLongArc( 1, angleBetweenEndpoints=angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.straightRoadBuilder.createWithDifferentLanes( 2, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_createSingleLaneConnectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) # extensions.printRoadPositions(odr) road = roads[1] print(f"{road.id} has length {road.planview.get_total_length()}") for geom in road.planview._adjusted_geometries: print(geom.length) extensions.view_road(odr, os.path.join('..', self.esminipath)) def test_getLength(self): angleBetweenEndpoints = 1.5708 curvature = 0.333333 curveType = StandardCurveTypes.Simple currentLength = AngleCurvatureMap.getLength(angleBetweenEndpoints, curvature, curveType) print(f"test length: {currentLength}") numberofLanes = 5 laneOffset = 3 angle = angleBetweenEndpoints length = currentLength maxCurve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, numberofLanes * laneOffset, curveType=curveType) curve = AngleCurvatureMap.getCurvatureForAngleBetweenRoadAndLength( angle, length, curveType=curveType) # curve = 0.066666667 print(f"max curve {maxCurve}, current curve {curve}") roads = [] roads.append( self.straightRoadBuilder.createWithDifferentLanes( 0, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) # roads.append(self.curveRoadBuilder.createSimpleCurveWithLongArc(1, angleBetweenEndpoints = angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.curveRoadBuilder.createSimple(1, angleBetweenEndpoints=angle, curvature=curve, isJunction=True, n_lanes=numberofLanes)) roads.append( self.straightRoadBuilder.createWithDifferentLanes( 2, length=10, junction=-1, n_lanes_left=numberofLanes, n_lanes_right=numberofLanes)) RoadLinker.createExtendedPredSuc(predRoad=roads[0], predCp=pyodrx.ContactPoint.end, sucRoad=roads[1], sucCP=pyodrx.ContactPoint.start) RoadLinker.createExtendedPredSuc(predRoad=roads[1], predCp=pyodrx.ContactPoint.end, sucRoad=roads[2], sucCP=pyodrx.ContactPoint.start) odrName = "test_createSingleLaneConnectionRoad" odr = extensions.createOdrByPredecessor(odrName, roads, []) # extensions.printRoadPositions(odr) road = roads[1] print(f"{road.id} has length {road.planview.get_total_length()}") # for geom in road.planview._adjusted_geometries: # print(geom.length) assert round(road.planview.get_total_length(), 0) == round(length, 0)
class JunctionMerger: 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 getOutputPath(self, fname): return self.destinationPrefix + fname + '.xodr' def canMerge(self, connectionRoadFirst, connectionRoadSecond): #1. if both straight, cannot merge if connectionRoadFirst.curveType is None and connectionRoadSecond.curveType is None: return False #1. if one straight, can merge if connectionRoadFirst.curveType is None or connectionRoadSecond.curveType is None: return True #1. if angles are same but curvatures are opposite. cannot merge because they overlap firstAngle = connectionRoadFirst.getArcAngle() # difference = abs(firstAngle - connectionRoadSecond.getArcAngle()) * 100 / firstAngle difference = abs(firstAngle - connectionRoadSecond.getArcAngle()) dotSign = connectionRoadFirst.getFirstGeomCurvature( ) * connectionRoadSecond.getFirstGeomCurvature() if difference < (np.pi / 10) and dotSign < 0: return False return True def merge2R2L(self, odrs, save=True): # 1 find connectionRoad in the first, it's predecessor is first road, successor is the second road. connectionRoadsFirst = extensions.getConnectionRoads( odrs[0].roads, odrs[0].junctions[0]) connectionRoadFirst = connectionRoadsFirst[0].shallowCopy() connectionRoadsSecond = extensions.getConnectionRoads( odrs[1].roads, odrs[1].junctions[0]) connectionRoadSecond = connectionRoadsSecond[0].shallowCopy() if self.canMerge(connectionRoadFirst, connectionRoadSecond) is False: raise IncompatibleRoadsException( "incompatible junctions to merge.") roadFirstPred = extensions.getRoadFromRoadDic( odrs[0].roads, connectionRoadFirst.predecessor.element_id).shallowCopy() roadFirstSuc = extensions.getRoadFromRoadDic( odrs[0].roads, connectionRoadFirst.successor.element_id).shallowCopy() roadSecondPred = extensions.getRoadFromRoadDic( odrs[1].roads, connectionRoadSecond.predecessor.element_id).shallowCopy() roadSecondSuc = extensions.getRoadFromRoadDic( odrs[1].roads, connectionRoadSecond.successor.element_id).shallowCopy() # case 1: remove roadSecondPred & rebuild links for roads in the first odr roadFirstPred.updateSuccessor(pyodrx.ElementType.junction, connectionRoadFirst.id) connectionRoadFirst.updatePredecessor( pyodrx.ElementType.road, roadFirstPred.id, pyodrx.ContactPoint.end ) # interestingly, this becomes the start point after merging. connectionRoadFirst.updateSuccessor(pyodrx.ElementType.road, roadFirstSuc.id, pyodrx.ContactPoint.start) roadFirstSuc.updatePredecessor(pyodrx.ElementType.junction, connectionRoadFirst.id) roadSecondSuc.updatePredecessor(pyodrx.ElementType.junction, connectionRoadFirst.id) roads = [] roads.append(roadFirstPred) roads.append(connectionRoadFirst) roads.append(roadFirstSuc) roads.append(connectionRoadSecond) roads.append(roadSecondSuc) # fix links for roadFirstSuc, connectionRoadSecond, roadSecondSuc self.regenAndMergeWithConnectionRoad(roadFirstSuc, connectionRoadSecond, roadSecondSuc) # create new junction junction = self.createJunctionFor2Connections(roadFirstPred, connectionRoadFirst, roadFirstSuc, connectionRoadSecond) # newOdr = self.mergeByRoad(self, commonRoads, ords) self.lastId += 1 # create the opendrive odr = pyodrx.OpenDrive("3R_2L_" + str(self.lastId)) for r in roads: odr.add_road(r) odr.add_junction(junction) print(f"starting adjustment. May freeze!!!!!!!!!!!!!") odr.adjust_roads_and_lanes() xmlPath = self.getOutputPath(odr.name) if save: odr.write_xml(xmlPath) if self.saveImage: extensions.saveRoadImageFromFile(xmlPath, self.esminiPath) return odr def regenAndMergeWithConnectionRoad(self, lastRoad_odr_1, connectionRoad_odr_2, lastRoad_odr_2): """Clears all road and lane links for all roads except lastRoad_odr_1. Args: lastRoad_odr_1 ([type]): reassigns successor connectionRoad_odr_2 ([type]): re-ids and reassigns successor and predecessors lastRoad_odr_2 ([type]): [description] re-ids and reassigns predecessor """ # regenerate ids for connectionRoadSecond and roadSecondSuc connectionRoad_odr_2.id = lastRoad_odr_1.id + 100 lastRoad_odr_2.id = connectionRoad_odr_2.id + 1 # fix links for roadFirstSuc, connectionRoadSecond, roadSecondSuc lastRoad_odr_1.updateSuccessor(pyodrx.ElementType.junction, connectionRoad_odr_2.id) connectionRoad_odr_2.updatePredecessor( pyodrx.ElementType.road, lastRoad_odr_1.id, pyodrx.ContactPoint.start ) # interestingly, this becomes the start point after merging. connectionRoad_odr_2.updateSuccessor( pyodrx.ElementType.road, lastRoad_odr_2.id, pyodrx.ContactPoint.start ) # interestingly, this becomes the start point after merging. lastRoad_odr_2.updatePredecessor(pyodrx.ElementType.junction, connectionRoad_odr_2.id) pass def createJunctionFor2Connections(self, roadFirstPred, connectionRoadFirst, roadFirstSuc, connectionRoadSecond): # TODO experiment with connecting more lanes for a single connection road. con1 = pyodrx.Connection(roadFirstPred.id, connectionRoadFirst.id, pyodrx.ContactPoint.start) con1.add_lanelink(-1, -1) con2 = pyodrx.Connection(roadFirstSuc.id, connectionRoadSecond.id, pyodrx.ContactPoint.start) con2.add_lanelink(-1, -1) junction = pyodrx.Junction('junc', 1) junction.add_connection(con1) junction.add_connection(con2) return junction
class test_ThreeWayJunction(unittest.TestCase): def setUp(self): self.configuration = Configuration() outputDir = os.path.join(os.getcwd(), 'output') lastId = 0 self.seed = 2 self.builder = ThreeWayJunctionBuilder(minAngle=np.pi / 9, maxAngle=np.pi * .25, straightRoadLen=20) self.randomState = self.configuration.get("random_state") pass def test_ThreeWayJunctionWithAngle(self): angleBetweenRoads = np.pi / 4 odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.view_road_odrviewer(odr, os.path.join('..',self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath) def test_ThreeWayJunctionWithRandomAngle(self): angleBetweenRoads = ((7 / 18) * np.random.random() + (1 / 9)) * np.pi odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) extensions.view_road_odrviewer( odr, os.path.join('..', self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath) angleBetweenRoads = ((7 / 18) * np.random.random() + (1 / 9)) * np.pi odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.view_road_odrviewer(odr, os.path.join('..',self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath) angleBetweenRoads = ((7 / 18) * np.random.random() + (1 / 9)) * np.pi odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.view_road_odrviewer(odr, os.path.join('..',self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath) angleBetweenRoads = ((7 / 18) * np.random.random() + (1 / 9)) * np.pi odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.view_road_odrviewer(odr, os.path.join('..',self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath) angleBetweenRoads = ((7 / 18) * np.random.random() + (1 / 9)) * np.pi odr = self.builder.ThreeWayJunctionWithAngle( odrId=1, angleBetweenRoads=angleBetweenRoads, maxLanePerSide=4, minLanePerSide=2, cp1=pyodrx.ContactPoint.end) extensions.printRoadPositions(odr) extensions.view_road( odr, os.path.join('..', self.configuration.get("esminipath"))) # extensions.view_road_odrviewer(odr, os.path.join('..',self.configuration.get("esminipath"))) xmlPath = f"output/test_ThreeWayJunctionWithAngle.xodr" odr.write_xml(xmlPath)
def test_Get(self): configuration = Configuration() testVal = configuration.get('test2.test22') assert testVal == 'this is something'