from __future__ import absolute_import
from __future__ import print_function
import os
import sys
from xml.dom import pulldom
from xml.sax import handler
from xml.sax import make_parser
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
from sumolib.options import ArgumentParser
from sumolib.miscutils import parseTime
import sumolib
DEPART_ATTRS = {'vehicle': 'depart', 'trip': 'depart', 'flow': 'begin', 'person': 'depart'}
def get_options(args=None):
ap = ArgumentParser()
ap.add_argument("-o", "--outfile", category="output", type=ap.file, help="name of output file")
ap.add_argument("-b", "--big", action="store_true", default=False,
help="Use alternative sorting strategy for large files (slower but more memory efficient)")
ap.add_argument("-v", "--verbose", action="store_true", default=False,
help="Tell me what you are doing")
ap.add_argument("routefile", category="input", type=ap.file, help="route file whose routes should be sorted")
options = ap.parse_args(args=args)
if options.outfile is None:
options.outfile = options.routefile + ".sorted"
return options
def sort_departs(routefile, outfile, verbose):
if isinstance(routefile, str):
stream = open(routefile, 'rb')
else:
stream = routefile
stream.seek(0)
routes_doc = pulldom.parse(stream)
vehicles = []
root = None
for event, parsenode in routes_doc:
if event == pulldom.START_ELEMENT:
if root is None:
root = parsenode.localName
sumolib.writeXMLHeader(outfile, root=root, includeXMLDeclaration=False)
continue
routes_doc.expandNode(parsenode)
departAttr = DEPART_ATTRS.get(parsenode.localName)
if departAttr is not None:
startString = parsenode.getAttribute(departAttr)
if startString == "triggered":
start = -1
else:
start = parseTime(startString)
vehicles.append(
(start, parsenode.toprettyxml(indent="", newl="")))
else:
outfile.write(
" " * 4 + parsenode.toprettyxml(indent="", newl="") + "\n")
if verbose:
print('read %s elements.' % len(vehicles))
vehicles.sort(key=lambda v: v[0])
for _, vehiclexml in vehicles:
outfile.write(" " * 4 + vehiclexml + "\n")
outfile.write("</%s>\n" % root)
if verbose:
print('wrote %s elements.' % len(vehicles))
if isinstance(routefile, str):
stream.close()
class RouteHandler(handler.ContentHandler):
def __init__(self, elements_with_depart):
self.elements_with_depart = elements_with_depart
self._depart = None
def setDocumentLocator(self, locator):
self.locator = locator
def startElement(self, name, attrs):
if name in DEPART_ATTRS.keys():
startString = attrs[DEPART_ATTRS[name]]
if startString == "triggered":
self._depart = -1
else:
self._depart = parseTime(startString)
self._start_line = self.locator.getLineNumber()
if name == "ride" and self._depart == -1 and "depart" in attrs:
self._depart = parseTime(attrs["depart"])
def endElement(self, name):
if name in DEPART_ATTRS.keys():
end_line = self.locator.getLineNumber()
self.elements_with_depart.append(
(self._depart, self._start_line, end_line))
self._depart = None
def create_line_index(file, verbose):
if verbose:
print("Building line offset index for %s" % file)
result = []
offset = 0
with open(file, 'rb') as f:
for line in f:
result.append(offset)
offset += len(line)
return result
def get_element_lines(routefilename, verbose):
if verbose:
print("Parsing %s for line indices and departs" % routefilename)
result = []
parser = make_parser()
parser.setContentHandler(RouteHandler(result))
parser.parse(open(routefilename))
if verbose:
print(" found %s items" % len(result))
return result
def copy_elements(routefilename, outfilename, element_lines, line_offsets, verbose):
if verbose:
print("Copying elements from %s to %s sorted by departure" % (
routefilename, outfilename))
with open(routefilename) as routefile, open(outfilename, 'w') as outfile:
for line in routefile:
outfile.write(line)
if '<routes' in line and 'value=' not in line:
break
for _, start, end in element_lines:
routefile.seek(line_offsets[start - 1])
for __ in range(end - start + 1):
outfile.write(routefile.readline())
outfile.write('</routes>')
def main(args=None):
options = get_options(args=args)
if options.big:
line_offsets = create_line_index(options.routefile, options.verbose)
element_lines = sorted(get_element_lines(options.routefile, options.verbose))
copy_elements(options.routefile, options.outfile, element_lines, line_offsets, options.verbose)
else:
with open(options.routefile) as routefile, open(options.outfile, 'w') as outfile:
for line in routefile:
if line.find('<routes') == 0 or line.find('<additional') == 0:
break
outfile.write(line)
sort_departs(routefile, outfile, options.verbose)
if __name__ == "__main__":
main()