def pathMatch(dbServer, networkName, userName, password, shapePath, limitMap = None): # Default parameters, with explanations and cross-references to Perrine et al., 2015: pointSearchRadius = 1000 # "k": Radius (ft) to search from GTFS point to perpendicular VISTA links pointSearchPrimary = 350 # "k_p": Radius (ft) to search from GTFS point to new VISTA links pointSearchSecondary = 200 # "k_s": Radius (ft) to search from VISTA perpendicular point to previous point limitLinearDist = 3800 # Path distance (ft) to allow new proposed paths from one point to another limitDirectDist = 3500 # Radius (ft) to allow new proposed paths from one point to another limitDirectDistRev = 500 # Radius (ft) to allow backtracking on an existing link (e.g. parking lot) distanceFactor = 1.0 # "f_d": Cost multiplier for Linear path distance driftFactor = 1.5 # "f_r": Cost multiplier for distance from GTFS point to its VISTA link nonPerpPenalty = 1.5 # "f_p": Penalty multiplier for GTFS points that aren't perpendicular to VISTA links limitClosestPoints = 12 # "q_p": Number of close-proximity points that are considered for each GTFS point limitSimultaneousPaths = 8 # "q_e": Number of proposed paths to maintain during pathfinding stage maxHops = 12 # Maximum number of VISTA links to pursue in a path-finding operation # Get the database connected: print("INFO: Connect to database...", file = sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file = sys.stderr) vistaGraph = vista_network.fillGraph(database) # Read in the shapefile information: print("INFO: Read GTFS shapefile...", file = sys.stderr) gtfsShapes = gtfs.fillShapes(shapePath, vistaGraph.gps) # Initialize the path-finder: pathFinder = path_engine.PathEngine(pointSearchRadius, pointSearchPrimary, pointSearchSecondary, limitLinearDist, limitDirectDist, limitDirectDistRev, distanceFactor, driftFactor, nonPerpPenalty, limitClosestPoints, limitSimultaneousPaths) pathFinder.maxHops = maxHops # Begin iteration through each shape: shapeIDs = gtfsShapes.keys() "@type shapeIDs: list<int>" shapeIDs.sort() gtfsNodesResults = {} "@type gtfsNodesResults: dict<int, list<path_engine.PathEnd>>" if limitMap is not None: for shapeID in limitMap: if shapeID not in shapeIDs: print("WARNING: Limit shape ID %d is not found in the shape file." % shapeID, file = sys.stderr) for shapeID in shapeIDs: "@type shapeID: int" if limitMap is not None and shapeID not in limitMap: continue print("INFO: -- Shape ID %d --" % shapeID, file = sys.stderr) # Find the path for the given shape: gtfsNodes = pathFinder.constructPath(gtfsShapes[shapeID], vistaGraph) # File this away as a result for later output: gtfsNodesResults[shapeID] = gtfsNodes return gtfsNodesResults
def restorePathMatch(dbServer, networkName, userName, password, shapePath, pathMatchFilename): # Get the database connected: print("INFO: Connect to database...", file = sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file = sys.stderr) vistaGraph = vista_network.fillGraph(database) # Read in the shapefile information: print("INFO: Read GTFS shapefile...", file = sys.stderr) gtfsShapes = gtfs.fillShapes(shapePath, vistaGraph.gps) # Read the path-match file: print("INFO: Read the path-match file '%s'..." % pathMatchFilename, file = sys.stderr) with open(pathMatchFilename, 'r') as inFile: gtfsNodes = path_engine.readStandardDump(vistaGraph, gtfsShapes, inFile) "@type gtfsNodes: dict<int, list<path_engine.PathEnd>>" # Filter out the unused shapes: unusedShapeIDs = set() for shapeID in gtfsShapes.keys(): if shapeID not in gtfsNodes: del gtfsShapes[shapeID] unusedShapeIDs.add(shapeID) return (vistaGraph, gtfsShapes, gtfsNodes, unusedShapeIDs)
def pathMatch(dbServer, networkName, userName, password, filename, limitMap = None): # Default parameters, with explanations and cross-references to Perrine et al., 2015: pointSearchRadius = 1000 # "k": Radius (ft) to search from GTFS point to perpendicular VISTA links pointSearchPrimary = 350 # "k_p": Radius (ft) to search from GTFS point to new VISTA links pointSearchSecondary = 200 # "k_s": Radius (ft) to search from VISTA perpendicular point to previous point limitLinearDist = 3800 # Path distance (ft) to allow new proposed paths from one point to another limitDirectDist = 3500 # Radius (ft) to allow new proposed paths from one point to another limitDirectDistRev = 500 # Radius (ft) to allow backtracking on an existing link (e.g. parking lot) distanceFactor = 1.0 # "f_d": Cost multiplier for Linear path distance driftFactor = 1.5 # "f_r": Cost multiplier for distance from GTFS point to its VISTA link nonPerpPenalty = 1.5 # "f_p": Penalty multiplier for GTFS points that aren't perpendicular to VISTA links limitClosestPoints = 8 # "q_p": Number of close-proximity points that are considered for each GTFS point limitSimultaneousPaths = 6 # "q_e": Number of proposed paths to maintain during pathfinding stage maxHops = 12 # Maximum number of VISTA links to pursue in a path-finding operation # Get the database connected: print("INFO: Connect to database...", file = sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file = sys.stderr) vistaGraph = vista_network.fillGraph(database) # Read in the GPS track information: print("INFO: Read GDB GPS track...", file = sys.stderr) gpsTracks = fillFromFile(filename, vistaGraph.gps) # Initialize the path-finder: pathFinder = path_engine.PathEngine(pointSearchRadius, pointSearchPrimary, pointSearchSecondary, limitLinearDist, limitDirectDist, limitDirectDistRev, distanceFactor, driftFactor, nonPerpPenalty, limitClosestPoints, limitSimultaneousPaths) pathFinder.maxHops = maxHops # Begin iteration through each shape: datafileIDs = compat.listkeys(gpsTracks) "@type datafileIDs: list<str>" datafileIDs.sort() nodesResults = {} "@type nodesResults: dict<str, list<path_engine.PathEnd>>" if limitMap is not None: for datafileID in limitMap: if datafileID not in datafileIDs: print("WARNING: Limit datafile ID %d is not found in the shape file." % datafileID, file = sys.stderr) for datafileID in datafileIDs: "@type datafileID: int" if limitMap is not None and datafileID not in limitMap: continue print("INFO: -- Datafile %s --" % datafileID, file = sys.stderr) # Find the path for the given shape: gtfsNodes = pathFinder.constructPath(gpsTracks[datafileID], vistaGraph) # File this away as a result for later output: nodesResults[datafileID] = gtfsNodes return nodesResults
def restorePathMatch(dbServer, networkName, userName, password, shapePath, pathMatchFilename, useDirectDist=True): # Get the database connected: print("INFO: Connect to database...", file=sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file=sys.stderr) vistaGraph = vista_network.fillGraph(database, useDirectDist) # Read in the shapefile information: print("INFO: Read GTFS shapefile...", file=sys.stderr) gtfsShapes = gtfs.fillShapes(shapePath, vistaGraph.gps) # Read the path-match file: print("INFO: Read the path-match file '%s'..." % pathMatchFilename, file=sys.stderr) with open(pathMatchFilename, 'r') as inFile: gtfsNodes = path_engine.readStandardDump(vistaGraph, gtfsShapes, inFile) "@type gtfsNodes: dict<int, list<path_engine.PathEnd>>" # Filter out the unused shapes: unusedShapeIDs = set() for shapeID in compat.listkeys(gtfsShapes): if shapeID not in gtfsNodes: del gtfsShapes[shapeID] unusedShapeIDs.add(shapeID) return (vistaGraph, gtfsShapes, gtfsNodes, unusedShapeIDs)
def main(argv): # Initialize from command-line parameters: if len(argv) < 7: syntax(0) dbServer = argv[1] networkName = argv[2] userName = argv[3] password = argv[4] gdbFilename = argv[5] gdbPathMatch = argv[6] sourceID = 0 endTime = 86400 refTime = None problemReport = False if len(argv) > 6: i = 7 while i < len(argv): if argv[i] == "-s" and i < len(argv) - 1: sourceID = int(argv[i + 1]) i += 1 elif argv[i] == "-t" and i < len(argv) - 1: refTime = datetime.strptime(argv[i + 1], '%H:%M:%S') i += 1 elif argv[i] == "-e" and i < len(argv) - 1: endTime = int(argv[i + 1]) i += 1 elif argv[i] == "-p": problemReport = True i += 1 if refTime is None and not problemReport: print("ERROR: No reference time is specified.") syntax(1) # Get the database connected: print("INFO: Connect to database...", file = sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file = sys.stderr) vistaGraph = vista_network.fillGraph(database) # Read in the GPS track information: print("INFO: Read GDB GPS track '%s'..." % gdbFilename, file = sys.stderr) gpsTracks = gdb_extracted.fillFromFile(gdbFilename, vistaGraph.gps) # Restore the path match: print("INFO: Read the GDB path-match file '%s'..." % gdbPathMatch, file = sys.stderr) with open(gdbPathMatch, 'r') as inFile: nodes = path_engine.readStandardDump(vistaGraph, gpsTracks, inFile, lambda x: str(x)) # Assumption: Each shapeID corresponds with one trip that will be reported in the output. # And, each route corresponds with one trip. # Filter out nodes that have one or zero links: for shapeID in nodes.keys(): ctr = 0 for node in nodes[shapeID]: ctr += len(node.routeInfo) if ctr <= 1: print("INFO: Filtering out shapeID %s." % str(shapeID), file = sys.stderr) del nodes[shapeID] del gpsTracks[shapeID] # Deal with Problem Report: if problemReport: print("INFO: Output problem report CSV...", file = sys.stderr) problem_report.problemReport(nodes, vistaGraph) print("INFO: Done.", file = sys.stderr) return # TODO: The logic below is a hack to create unique routes given GDB IDs. There are several # long-term problems with this, including the idea that it is impossible to reuse common # routes (each instance is its own route) and there are assumptions about vehicle ID # numbering in the generated vehicles. # Fabricate routes: routes = {} ctr = 1 # We'll be making arbitrary route IDs: for shapeID in gpsTracks: routes[ctr] = gtfs.RoutesEntry(ctr, shapeID, "") ctr += 1 # Let vehicle IDs be in a different number range: vehCtr = int(ctr / 10000) vehCtr += 10000 # Fabricate trips and stop times: trips = {} stopTimes = {} for routeID in routes: trips[vehCtr] = gtfs.TripsEntry(vehCtr, routes[routeID], "", gpsTracks[routes[routeID].shortName]) stopTimes[trips[vehCtr]] = list() # Fake the system by having no stops defined. vehCtr += 1 tripIDs = trips.keys() tripIDs.sort() # Output the routes file: print("INFO: Dumping public.bus_route.csv...", file = sys.stderr) with open("public.bus_route.csv", 'w') as outFile: transit_gtfs.dumpBusRoutes(trips, userName, networkName, outFile) # Output the routes_link file: print("INFO: Dumping public.bus_route_link.csv...", file = sys.stderr) with open("public.bus_route_link.csv", 'w') as outFile: transit_gtfs.dumpBusRouteLinks(trips, stopTimes, nodes, vistaGraph, 1, False, userName, networkName, refTime, endTime, False, False, False, False, outFile) print("INFO: Dumping public.bus_frequency.csv...", file = sys.stderr) with open("public.bus_frequency.csv", 'w') as outFile: transit_gtfs._outHeader("public.bus_frequency", userName, networkName, outFile) print("\"route\",\"period\",\"frequency\",\"offsettime\",\"preemption\"", file = outFile) for tripID in tripIDs: departureTime = trips[tripID].shapeEntries[0].time timeDiff = departureTime - refTime print("%d,1,86400,%d,0" % (tripID, timeDiff.days * 24 * 3600 + timeDiff.seconds), file = outFile) print("INFO: Dumping public.bus_period.csv...", file = sys.stderr) with open("public.bus_period.csv", 'w') as outFile: transit_gtfs._outHeader("public.bus_period", userName, networkName, outFile) print("\"id\",\"starttime\",\"endtime\"", file = outFile) print("1,0,%d" % endTime, file = outFile) # Now we need to write out to the travel_time output: print("INFO: Dumping public.travel_time.csv...", file = sys.stderr) with open("public.travel_time.csv", 'w') as outFile: transit_gtfs._outHeader("public.travel_time", userName, networkName, outFile) print("\"departure_time\",\"vehicle_id\",\"route_id\",\"exittime\",\"linkid\",\"arrivaltime\",\"sourceid\"", file = outFile) for tripID in tripIDs: nodeList = nodes[trips[tripID].route.shortName] "@type nodeList: list<path_engine.PathEnd>" departureTime = trips[tripID].shapeEntries[0].time lastTime = trips[tripID].shapeEntries[-1].time # Add the first link to the file: timeDiff = departureTime - refTime timeDiffLast = lastTime - refTime outStr = "%d,%d,%d,%d,%d,%d,%d" % (timeDiff.days * 24 * 3600 + timeDiff.seconds, trips[tripID].route.routeID, tripID, timeDiffLast.days * 24 * 3600 + timeDiffLast.seconds, nodeList[0].pointOnLink.link.id, timeDiff.days * 24 * 3600 + timeDiff.seconds, sourceID) print(outStr, file = outFile) for node in nodeList: "@type node: path_engine.PathEnd" if len(node.routeInfo) > 0: # TODO: Deal with midnight if the time is before refTime. arrivalTime = node.shapeEntry.time for link in node.routeInfo: arrivalTimeSec = 3600 * arrivalTime.hour + 60 * arrivalTime.minute + arrivalTime.second # TODO: We need to make vehicleID, routeID and tripID be consistent. timeDiffArr = arrivalTime - refTime outStr = "%d,%d,%d,%d,%d,%d,%d" % (timeDiff.days * 24 * 3600 + timeDiff.seconds, trips[tripID].route.routeID, tripID, timeDiffLast.days * 24 * 3600 + timeDiffLast.seconds, link.id, timeDiffArr.days * 24 * 3600 + timeDiffArr.seconds, sourceID) print(outStr, file = outFile) print("INFO: Done.", file = sys.stderr)
def main(argv): # Initialize from command-line parameters: if len(argv) < 7: syntax(0) dbServer = argv[1] networkName = argv[2] userName = argv[3] password = argv[4] csvFilename = argv[5] csvPathMatch = argv[6] sourceID = 0 endTime = 86400 refTime = None problemReport = False if len(argv) > 6: i = 7 while i < len(argv): if argv[i] == "-s" and i < len(argv) - 1: sourceID = int(argv[i + 1]) i += 1 elif argv[i] == "-t" and i < len(argv) - 1: refTime = datetime.strptime(argv[i + 1], '%H:%M:%S') i += 1 elif argv[i] == "-e" and i < len(argv) - 1: endTime = int(argv[i + 1]) i += 1 elif argv[i] == "-p": problemReport = True i += 1 if refTime is None and not problemReport: print("ERROR: No reference time is specified.") syntax(1) # Get the database connected: print("INFO: Connect to database...", file=sys.stderr) database = vista_network.connect(dbServer, userName, password, networkName) # Read in the topology from the VISTA database: print("INFO: Read topology from database...", file=sys.stderr) vistaGraph = vista_network.fillGraph(database) # Read in the GPS track information: print("INFO: Read ArcGIS CSV GPS track '%s'..." % csvFilename, file=sys.stderr) gpsTracks = arcgiscsv_extracted.fillFromFile(csvFilename, vistaGraph.gps) # Restore the path match: print("INFO: Read the ArcGIS CSV path-match file '%s'..." % csvPathMatch, file=sys.stderr) with open(csvPathMatch, 'r') as inFile: nodes = path_engine.readStandardDump(vistaGraph, gpsTracks, inFile, lambda x: str(x)) # Assumption: Each shapeID corresponds with one trip that will be reported in the output. # And, each route corresponds with one trip. # Filter out nodes that have one or zero links: for shapeID in compat.listkeys(nodes): ctr = 0 for node in nodes[shapeID]: ctr += len(node.routeInfo) if ctr <= 1: print("INFO: Filtering out shapeID %s." % str(shapeID), file=sys.stderr) del nodes[shapeID] del gpsTracks[shapeID] # Deal with Problem Report: if problemReport: print("INFO: Output problem report CSV...", file=sys.stderr) problem_report.problemReport(nodes, vistaGraph) print("INFO: Done.", file=sys.stderr) return # TODO: The logic below is a hack to create unique routes given GDB IDs. There are several # long-term problems with this, including the idea that it is impossible to reuse common # routes (each instance is its own route) and there are assumptions about vehicle ID # numbering in the generated vehicles. # Fabricate routes: routes = {} ctr = 1 # We'll be making arbitrary route IDs: for shapeID in gpsTracks: routes[ctr] = gtfs.RoutesEntry(ctr, shapeID, "") ctr += 1 # Let vehicle IDs be in a different number range: vehCtr = int(ctr / 10000) vehCtr += 10000 # Fabricate trips and stop times: trips = {} stopTimes = {} for routeID in routes: trips[vehCtr] = gtfs.TripsEntry(vehCtr, routes[routeID], "", gpsTracks[routes[routeID].shortName]) stopTimes[trips[vehCtr]] = list( ) # Fake the system by having no stops defined. vehCtr += 1 tripIDs = compat.listkeys(trips) tripIDs.sort() # Output the routes file: print("INFO: Dumping public.bus_route.csv...", file=sys.stderr) with open("public.bus_route.csv", 'w') as outFile: transit_gtfs.dumpBusRoutes(trips, userName, networkName, outFile) # Output the routes_link file: print("INFO: Dumping public.bus_route_link.csv...", file=sys.stderr) with open("public.bus_route_link.csv", 'w') as outFile: transit_gtfs.dumpBusRouteLinks(trips, stopTimes, nodes, vistaGraph, 1, False, userName, networkName, refTime, endTime, False, False, False, False, outFile) print("INFO: Dumping public.bus_frequency.csv...", file=sys.stderr) with open("public.bus_frequency.csv", 'w') as outFile: transit_gtfs._outHeader("public.bus_frequency", userName, networkName, outFile) print( "\"route\",\"period\",\"frequency\",\"offsettime\",\"preemption\"", file=outFile) for tripID in tripIDs: departureTime = trips[tripID].shapeEntries[0].time timeDiff = departureTime - refTime print("%d,1,86400,%d,0" % (tripID, timeDiff.days * 24 * 3600 + timeDiff.seconds), file=outFile) print("INFO: Dumping public.bus_period.csv...", file=sys.stderr) with open("public.bus_period.csv", 'w') as outFile: transit_gtfs._outHeader("public.bus_period", userName, networkName, outFile) print("\"id\",\"starttime\",\"endtime\"", file=outFile) print("1,0,%d" % endTime, file=outFile) # Now we need to write out to the travel_time output: print("INFO: Dumping public.travel_time.csv...", file=sys.stderr) with open("public.travel_time.csv", 'w') as outFile: transit_gtfs._outHeader("public.travel_time", userName, networkName, outFile) print( "\"departure_time\",\"vehicle_id\",\"route_id\",\"exittime\",\"linkid\",\"arrivaltime\",\"sourceid\"", file=outFile) for tripID in tripIDs: nodeList = nodes[trips[tripID].route.shortName] "@type nodeList: list<path_engine.PathEnd>" departureTime = trips[tripID].shapeEntries[0].time lastTime = trips[tripID].shapeEntries[-1].time # Add the first link to the file: timeDiff = departureTime - refTime timeDiffLast = lastTime - refTime outStr = "%d,%d,%d,%d,%d,%d,%d" % ( timeDiff.days * 24 * 3600 + timeDiff.seconds, trips[tripID].route.routeID, tripID, timeDiffLast.days * 24 * 3600 + timeDiffLast.seconds, nodeList[0].pointOnLink.link.id, timeDiff.days * 24 * 3600 + timeDiff.seconds, sourceID) print(outStr, file=outFile) for node in nodeList: "@type node: path_engine.PathEnd" if len(node.routeInfo) > 0: # TODO: Deal with midnight if the time is before refTime. arrivalTime = node.shapeEntry.time for link in node.routeInfo: arrivalTimeSec = 3600 * arrivalTime.hour + 60 * arrivalTime.minute + arrivalTime.second # TODO: We need to make vehicleID, routeID and tripID be consistent. timeDiffArr = arrivalTime - refTime outStr = "%d,%d,%d,%d,%d,%d,%d" % ( timeDiff.days * 24 * 3600 + timeDiff.seconds, trips[tripID].route.routeID, tripID, timeDiffLast.days * 24 * 3600 + timeDiffLast.seconds, link.id, timeDiffArr.days * 24 * 3600 + timeDiffArr.seconds, sourceID) print(outStr, file=outFile) print("INFO: Done.", file=sys.stderr)