예제 #1
0
파일: mctrain.py 프로젝트: johguse/mctrain
def main():
	global args
	argParser = argparse.ArgumentParser(	add_help=True,
											epilog="Don't have any expections on this piece of software. It's an ugly mess of a hack mainly for personal use.",
											usage="%(prog)s [options] regionFile [regionFile ...]")
											
	curGroup = argParser.add_argument_group("cache")
	curGroup.add_argument("--cache", dest="strCache", type=str, metavar="<str>", default=False, help="Path to cache, unique to the region-files specified")
	curGroup.add_argument("--only-cache", dest="bOnlyCache", action="store_true", default=False, help="Don't read region-files, use only the cache")
	
	curGroup = argParser.add_argument_group("generic")
	curGroup.add_argument("--distance-label", dest="distanceLabel", type=float, metavar="<float>", default=False, help="Distance to look for a sign labeling a node")
	curGroup.add_argument("--component-rail", dest="componentRail", type=int, metavar="<int>", default=30, help="Disregard components comprised of too little rail (default: 30)")
	
	curGroup = argParser.add_argument_group("second pass")
	curGroup.add_argument("--distance-cluster", dest="distanceCluster", type=float, metavar="<float>", default=False, help="Distance used for clustering nodes")
	
	curGroup = argParser.add_argument_group("dividing components")	
	curGroup.add_argument("--component-divide", dest="bComponentDivide", action="store_true", default=False, help="Divide graph into connected components <required>")
	curGroup.add_argument("--combine-pairs", dest="bCombinePairs", action="store_true", default=False, help="Combine all pairs of nodes into one graph")
	curGroup.add_argument("--distance-min", dest="distanceMin", type=float, metavar="<float>", default=False, help="Disregard components where the longest distance between end-nodes are too short")
	
	curGroup = argParser.add_argument_group("drawing")	
	curGroup.add_argument("--switch-as-endpoint", dest="bSwitchAsEndpoint", action="store_true", default=False, help="Draw switches as endpoints")
	curGroup.add_argument("--label-edges", dest="bLabelEdges", action="store_true", default=False, help="Label edges according to geometric distance")
	curGroup.add_argument("--edge-weight", dest="edgeWeight", type=float, metavar="<float>", default=False, help="Draw geometrically longer edges as thicker")
	
	curGroup = argParser.add_argument_group("regions")	
	curGroup.add_argument("regionFile", type=str, nargs="+", help="Region files (.mca) that the graph should be generated from")
	args = argParser.parse_args()

	dLabels = dict()
	sClusterNodes = set()
	print "Fetching rail data..."
	timeStart = time.time()
	dRails, dAllLabels = dict(), dict()
	lenRegionsLeft = len(args.regionFile)

	with ProcessPoolExecutor() as executor:
		for dRegionRails, dRegionLabels in executor.map( railLoaderHelper, args.regionFile ):
			lenRegionsLeft -= 1
			print "\r\tReading regions: {0:>6}".format(lenRegionsLeft),
			sys.stdout.flush()
			for x, y in dRegionRails.items() : dRails[x] = y
			for x, y in dRegionLabels.items() : dAllLabels[x] = y
					
	print ""
		
	# Prune non-existant edges and add edge back if it doesn't exist, for example in intersections
	# This will create some illegal intersections if they exist in the map. Tough luck.
	print "\tAdding bidirectionality..."
	for curPos, sEdges in dRails.iteritems():
		sEdges = set([x for x in sEdges if x in dRails])
		for edge in sEdges: dRails[edge].add(curPos)
		dRails[curPos] = sEdges
		
	timePerRegion = (time.time() - timeStart) / len(args.regionFile)
	print "\tSeconds per region: {0:.2}s".format(timePerRegion)
	print "\tNodes = {0}, Labels = {1}".format( len(dRails), len(dAllLabels) )
	
	print "Generating first pass rudimentary graph..."
	print "\tRemoving nodes not used in any shortest path and small components..."
	Graph.filterPathsAndComponents(dRails, set([x for x in dRails if len(dRails[x]) == 1]), args.componentRail)
	print "\tMerging nodes with only two edges..."
	Graph.minimizeNodes(dRails)
	print "\tNodes = {0}".format( len(dRails) )
	
	if args.distanceCluster:
		print "Generating second pass graph based on distance..."
		print "\tClustering neighbouring nodes..."
		sClusterNodes = Graph.distanceClusterNodes(dRails, args.distanceCluster)
		print "\tRemoving nodes not used in any shortest path..."
		Graph.filterPathsAndComponents( dRails, sClusterNodes.union( [x for x in dRails if len(dRails[x]) == 1] ), None )
		sClusterNodes = set( [x for x in sClusterNodes if len(dRails[x]) > 1 ] )
		print "\tMerging nodes with only two edges..."
		Graph.minimizeNodes(dRails, sClusterNodes)
		print "\tNodes = {0}".format( len(dRails) )
		
	if args.distanceLabel: dLabels = labelNodes(dRails, dAllLabels, args.distanceLabel)
		
	if args.bComponentDivide:
		lGraphs = Graph.componentDivide(dRails, sClusterNodes)
	else:
		lGraphs = [(dRails, None, sClusterNodes)]
		
	if args.distanceMin:
		if not args.bComponentDivide:
			print "warning: min distance filter need components to be divided"
		else:
			print "Removing components with distance < {0}".format(args.distanceMin)
			lGraphDistances = []
			for (curGraph, curEndings, sClusterNodes) in lGraphs:
				maxDistance = 0
				for curEnding in curEndings:
					lDistances = [ Graph.getDistance(curEnding, x) for x in curEndings ]
					curMax = max(lDistances)
					if curMax > maxDistance: maxDistance = curMax
				
				lGraphDistances.append(maxDistance)
			
			lenBefore = len(lGraphs)
			lGraphs = [ x for x, y in zip(lGraphs, lGraphDistances) if y > args.distanceMin ]
			lenAfter = len(lGraphs)
			print "\t{0} components removed".format(lenBefore-lenAfter)
					
	if args.bCombinePairs:
		if not args.bComponentDivide:
			print "warning: combining components containing only a pair of nodes needs components to be divided"
		else:
			lGraphPairs = [ (x, y, z) for (x, y, z) in lGraphs if len(y) == 2 ]
			if lGraphPairs > 1:
				lGraphs = [ (x, y, z) for (x, y, z) in lGraphs if len(y) != 2 ]
				dPairCombined = dict()
				sUnionEndings = set()
				sUnionClusterNodes = set()
				for curGraph, curEndings, curClusterNodes in lGraphPairs:
					for x, y in curGraph.iteritems(): dPairCombined[x] = y
					sUnionEndings = sUnionEndings.union(curEndings)
					sUnionClusterNodes = sUnionClusterNodes.union(curClusterNodes)
				
				lGraphs.append( (dPairCombined, sUnionEndings, sUnionClusterNodes) )
	
	# Generate .DOT and write them
	for i in xrange(0, len(lGraphs)):
		strGraphDot = DotWriter.generateDot( lGraphs[i][0], lGraphs[i][1], lGraphs[i][2], dLabels, args.bSwitchAsEndpoint, args.bLabelEdges, args.edgeWeight )
		strGraphGML = GMLWriter.generateGML( lGraphs[i][0], lGraphs[i][1], lGraphs[i][2], dLabels, args.bSwitchAsEndpoint, args.bLabelEdges, args.edgeWeight )
		
		with open("mctrain-graph-{0}.dot".format(i), "w") as fileOut: fileOut.write(strGraphDot)
		with open("mctrain-graph-{0}.graphml".format(i), "w") as fileOut: fileOut.write(strGraphGML)
		
	print "Done."
	return 0