sumolib.miscutils
1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo 2# Copyright (C) 2012-2026 German Aerospace Center (DLR) and others. 3# This program and the accompanying materials are made available under the 4# terms of the Eclipse Public License 2.0 which is available at 5# https://www.eclipse.org/legal/epl-2.0/ 6# This Source Code may also be made available under the following Secondary 7# Licenses when the conditions for such availability set forth in the Eclipse 8# Public License 2.0 are satisfied: GNU General Public License, version 2 9# or later which is available at 10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html 11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later 12 13# @file miscutils.py 14# @author Jakob Erdmann 15# @author Michael Behrisch 16# @author Mirko Barthauer 17# @date 2012-05-08 18 19from __future__ import absolute_import 20from __future__ import print_function 21from __future__ import division 22import sys 23import time 24import os 25import math 26import colorsys 27import socket 28import random 29import gzip 30import codecs 31import io 32from types import ModuleType, FunctionType 33from gc import get_referents 34try: 35 from urllib.request import urlopen 36except ImportError: 37 from urllib import urlopen 38# needed for backward compatibility 39from .statistics import Statistics, geh, uMax, uMin, round # noqa 40 41 42_BLACKLIST = type, ModuleType, FunctionType 43 44 45def get_size(obj): 46 """sum size of object & members. 47 lifted from https://stackoverflow.com/a/30316760 48 """ 49 if isinstance(obj, (_BLACKLIST)): 50 raise TypeError('getsize() does not take argument of type: ' + str(type(obj))) 51 seen_ids = set() 52 size = 0 53 objects = [obj] 54 while objects: 55 need_referents = [] 56 for obj in objects: 57 if not isinstance(obj, _BLACKLIST) and id(obj) not in seen_ids: 58 seen_ids.add(id(obj)) 59 size += sys.getsizeof(obj) 60 need_referents.append(obj) 61 objects = get_referents(*need_referents) 62 return size 63 64 65def benchmark(func): 66 """ 67 decorator for timing a function 68 """ 69 def benchmark_wrapper(*args, **kwargs): 70 started = time.time() 71 now = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime()) 72 print('function %s called at %s' % (func.__name__, now)) 73 sys.stdout.flush() 74 result = func(*args, **kwargs) 75 print('function %s finished after %f seconds' % 76 (func.__name__, time.time() - started)) 77 sys.stdout.flush() 78 return result 79 return benchmark_wrapper 80 81 82class Benchmarker: 83 """ 84 class for benchmarking a function using a "with"-statement. 85 Preferable over the "benchmark" function for the following use cases 86 - benchmarking a code block that isn't wrapped in a function 87 - benchmarking a function only in some calls 88 """ 89 90 def __init__(self, active, description): 91 self.active = active 92 self.description = description 93 94 def __enter__(self): 95 self.started = time.time() 96 97 def __exit__(self, *args): 98 if self.active: 99 duration = time.time() - self.started 100 print("%s finished after %s" % (self.description, humanReadableTime(duration))) 101 102 103class working_dir: 104 """ 105 temporarily change working directory using 'with' statement 106 """ 107 108 def __init__(self, dir): 109 self.dir = dir 110 self.origdir = os.getcwd() 111 112 def __enter__(self): 113 os.chdir(self.dir) 114 115 def __exit__(self, type, value, traceback): 116 os.chdir(self.origdir) 117 118 119class Colorgen: 120 DISTINCT = [ 121 (0.17, 1.0, 0.5), 122 (0.0, 0.9, 1.0), 123 (0.35, 0.67, 0.71), 124 (0.14, 0.9, 1.0), 125 (0.56, 1.0, 0.78), 126 (0.07, 0.8, 0.96), 127 (0.79, 0.83, 0.71), 128 (0.5, 0.71, 0.94), 129 (0.84, 0.79, 0.94), 130 (0.2, 0.76, 0.96), 131 (0.0, 0.24, 0.98), 132 (0.5, 1.0, 0.5), 133 (0.77, 0.25, 1.0), 134 (0.09, 0.76, 0.67), 135 (0.15, 0.22, 1.0), 136 (0.0, 1.0, 0.5), 137 (0.38, 0.33, 1.0), 138 (0.67, 1.0, 0.5), 139 ] 140 141 def __init__(self, hsv, cycleLength=10.67): 142 self.hsv = hsv 143 self.cycle = [int(random.random() * 256) for x in self.hsv] 144 self.cycleOffset = int(round(256 / cycleLength)) 145 self.distinctIndex = 0 146 147 def get_value(self, opt, index): 148 if opt == 'random': 149 return random.random() 150 if opt == 'cycle': 151 # the 255 below is intentional to get all color values when cycling long enough 152 self.cycle[index] = (self.cycle[index] + self.cycleOffset) % 255 153 return self.cycle[index] / 255.0 154 if opt == 'distinct': 155 if index == 0: 156 self.distinctIndex = (self.distinctIndex + 1) % len(self.DISTINCT) 157 return self.DISTINCT[self.distinctIndex][index] 158 return float(opt) 159 160 def floatTuple(self): 161 """return color as a tuple of floats each in [0,1]""" 162 return colorsys.hsv_to_rgb(*[self.get_value(o, i) for i, o in enumerate(self.hsv)]) 163 164 def byteTuple(self): 165 """return color as a tuple of bytes each in [0,255]""" 166 return tuple([int(round(255 * x)) for x in self.floatTuple()]) 167 168 def __call__(self): 169 """return constant or randomized rgb-color string""" 170 return ','.join(map(str, self.byteTuple())) 171 172 173class priorityDictionary(dict): 174 175 def __init__(self): 176 '''Initialize priorityDictionary by creating binary heap 177 of pairs (value,key). Note that changing or removing a dict entry will 178 not remove the old pair from the heap until it is found by smallest() or 179 until the heap is rebuilt.''' 180 self.__heap = [] 181 dict.__init__(self) 182 183 def smallest(self): 184 '''Find smallest item after removing deleted items from heap.''' 185 if len(self) == 0: 186 raise IndexError("smallest of empty priorityDictionary") 187 heap = self.__heap 188 while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]: 189 lastItem = heap.pop() 190 insertionPoint = 0 191 while 1: 192 smallChild = 2 * insertionPoint + 1 193 if smallChild + 1 < len(heap) and \ 194 heap[smallChild][0] > heap[smallChild + 1][0]: 195 smallChild += 1 196 if smallChild >= len(heap) or lastItem <= heap[smallChild]: 197 heap[insertionPoint] = lastItem 198 break 199 heap[insertionPoint] = heap[smallChild] 200 insertionPoint = smallChild 201 return heap[0][1] 202 203 def __iter__(self): 204 '''Create destructive sorted iterator of priorityDictionary.''' 205 def iterfn(): 206 while len(self) > 0: 207 x = self.smallest() 208 yield x 209 del self[x] 210 return iterfn() 211 212 def __setitem__(self, key, val): 213 '''Change value stored in dictionary and add corresponding 214 pair to heap. Rebuilds the heap if the number of deleted items grows 215 too large, to avoid memory leakage.''' 216 dict.__setitem__(self, key, val) 217 heap = self.__heap 218 if len(heap) > 2 * len(self): 219 self.__heap = [(v, k) for k, v in self.items()] 220 self.__heap.sort() # builtin sort likely faster than O(n) heapify 221 else: 222 newPair = (val, key) 223 insertionPoint = len(heap) 224 heap.append(None) 225 while insertionPoint > 0 and val < heap[(insertionPoint - 1) // 2][0]: 226 heap[insertionPoint] = heap[(insertionPoint - 1) // 2] 227 insertionPoint = (insertionPoint - 1) // 2 228 heap[insertionPoint] = newPair 229 230 def setdefault(self, key, val): 231 '''Reimplement setdefault to call our customized __setitem__.''' 232 if key not in self: 233 self[key] = val 234 return self[key] 235 236 def update(self, other): 237 for key in other.keys(): 238 self[key] = other[key] 239 240 241def getFreeSocketPort(numTries=10): 242 for _ in range(numTries): 243 try: 244 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 245 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 246 s.bind(('', 0)) 247 p = s.getsockname()[1] 248 s.close() 249 return p 250 except socket.error: 251 pass 252 return None 253 254 255def getSocketStream(port, mode='rb'): 256 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 257 s.bind(("localhost", port)) 258 s.listen(1) 259 conn, _ = s.accept() 260 return conn.makefile(mode) 261 262 263# euclidean distance between two coordinates in the plane 264def euclidean(a, b): 265 return math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) 266 267 268def humanReadableTime(seconds): 269 result = "" 270 sign = '-' if seconds < 0 else '' 271 seconds = abs(seconds) 272 ds = 3600 * 24 273 if seconds > ds: 274 result = "%s:" % int(seconds / ds) 275 seconds = seconds % ds 276 result += "%02i:" % int(seconds / 3600) 277 seconds = seconds % 3600 278 result += "%02i:" % int(seconds / 60) 279 seconds = seconds % 60 280 if seconds == int(seconds): 281 seconds = int(seconds) 282 result += "%02i" % seconds 283 return sign + result 284 285 286SPECIAL_TIME_STRINGS = ["triggered", "containerTriggered", "split", "begin"] 287 288 289def parseTime(t, factor=1): 290 try: 291 return float(t) * factor 292 except ValueError: 293 pass 294 try: 295 # prepended zero is ignored if the date value already contains days 296 days, hours, minutes, seconds = ([0] + list(map(float, t.split(':'))))[-4:] 297 sign = -1 if t.strip()[0] == '-' else 1 298 return (3600 * 24 * days + 3600 * hours + 60 * minutes + seconds) * sign * factor 299 except ValueError: 300 if t in SPECIAL_TIME_STRINGS: 301 # signal special case but don't crash 302 return None 303 else: 304 raise 305 306 307def parseBool(val): 308 # see data/xsd/baseTypes:boolType 309 return val in ["true", "True", "x", "1", "yes", "on"] 310 311 312def getFlowNumber(flow): 313 """interpret number of vehicles from a flow parsed by sumolib.xml.parse""" 314 if flow.number is not None: 315 return int(flow.number) 316 if flow.end is not None: 317 duration = parseTime(flow.end) - parseTime(flow.begin) 318 period = 0 319 isFractional = False 320 if flow.period is not None: 321 if 'exp' in flow.period: 322 # use expected value 323 period = 1 / float(flow.period[4:-1]) 324 isFractional = True 325 else: 326 period = float(flow.period) 327 elif flow.probability is not None: 328 # use expected value 329 period = 1 / float(flow.probability) 330 isFractional = True 331 for attr in ['perHour', 'vehsPerHour']: 332 if flow.hasAttribute(attr): 333 period = 3600 / float(flow.getAttribute(attr)) 334 if period > 0: 335 count = duration / period 336 if isFractional: 337 return count 338 else: 339 # flows with a regular period always start at least one vehicle at the begin time 340 return math.ceil(count) 341 else: 342 return 1 343 344 345def intIfPossible(val): 346 if int(val) == val: 347 return int(val) 348 else: 349 return val 350 351 352def openz(fileOrURL, mode="r", **kwargs): 353 """ 354 Opens transparently files, URLs and gzipped files for reading and writing. 355 Special file names "stdout" and "stderr" are handled as well. 356 Also enforces UTF8 on text output / input and should handle BOMs in input. 357 Should be compatible with python 2 and 3. 358 """ 359 encoding = kwargs.get("encoding", "utf8" if "w" in mode else "utf-8-sig") 360 try: 361 if fileOrURL.startswith("http://") or fileOrURL.startswith("https://"): 362 return io.BytesIO(urlopen(fileOrURL).read()) 363 if fileOrURL == "stdout": 364 return sys.stdout 365 if fileOrURL == "stderr": 366 return sys.stderr 367 if fileOrURL.endswith(".gz") and "w" in mode: 368 if "b" in mode: 369 return gzip.open(fileOrURL, mode="w") 370 return gzip.open(fileOrURL, mode="wt", encoding=encoding) 371 if kwargs.get("trySocket") and fileOrURL.isdigit(): 372 return getSocketStream(int(fileOrURL), mode) 373 if kwargs.get("tryGZip", True) and "r" in mode: 374 with gzip.open(fileOrURL) as fd: 375 fd.read(1) 376 if "b" in mode: 377 return gzip.open(fileOrURL) 378 if sys.version_info[0] < 3: 379 return codecs.getreader('utf-8')(gzip.open(fileOrURL)) 380 return gzip.open(fileOrURL, mode="rt", encoding=encoding) 381 except OSError as e: 382 if kwargs.get("printErrors"): 383 print(e, file=sys.stderr) 384 except IOError as e: 385 if kwargs.get("printErrors"): 386 print(e, file=sys.stderr) 387 if "b" in mode: 388 return io.open(fileOrURL, mode=mode) 389 return io.open(fileOrURL, mode=mode, encoding=encoding) 390 391 392def short_names(filenames, noEmpty): 393 if len(filenames) == 1: 394 return filenames 395 reversedNames = [''.join(reversed(f)) for f in filenames] 396 prefix = os.path.commonprefix(filenames) 397 suffix = os.path.commonprefix(reversedNames) 398 prefixLen = len(prefix) 399 suffixLen = len(suffix) 400 shortened = [f[prefixLen:-suffixLen] for f in filenames] 401 if noEmpty and any([not f for f in shortened]): 402 # make longer to avoid empty file names 403 base = os.path.basename(prefix) 404 shortened = [base + f for f in shortened] 405 return shortened 406 407 408def getBaseName(filename): 409 """strip extensions such as .net.xml.gz""" 410 if filename[-11:] == ".net.xml.gz" and len(filename) > 11: 411 return filename[:-11] 412 elif filename[-8:] == ".net.xml" and len(filename) > 8: 413 return filename[:-8] 414 elif filename[-7:] == ".xml.gz" and len(filename) > 7: 415 return filename[:-7] 416 elif filename[-4:] == ".xml" and len(filename) > 4: 417 return filename[:-4] 418 else: 419 return filename
46def get_size(obj): 47 """sum size of object & members. 48 lifted from https://stackoverflow.com/a/30316760 49 """ 50 if isinstance(obj, (_BLACKLIST)): 51 raise TypeError('getsize() does not take argument of type: ' + str(type(obj))) 52 seen_ids = set() 53 size = 0 54 objects = [obj] 55 while objects: 56 need_referents = [] 57 for obj in objects: 58 if not isinstance(obj, _BLACKLIST) and id(obj) not in seen_ids: 59 seen_ids.add(id(obj)) 60 size += sys.getsizeof(obj) 61 need_referents.append(obj) 62 objects = get_referents(*need_referents) 63 return size
sum size of object & members. lifted from https://stackoverflow.com/a/30316760
66def benchmark(func): 67 """ 68 decorator for timing a function 69 """ 70 def benchmark_wrapper(*args, **kwargs): 71 started = time.time() 72 now = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime()) 73 print('function %s called at %s' % (func.__name__, now)) 74 sys.stdout.flush() 75 result = func(*args, **kwargs) 76 print('function %s finished after %f seconds' % 77 (func.__name__, time.time() - started)) 78 sys.stdout.flush() 79 return result 80 return benchmark_wrapper
decorator for timing a function
83class Benchmarker: 84 """ 85 class for benchmarking a function using a "with"-statement. 86 Preferable over the "benchmark" function for the following use cases 87 - benchmarking a code block that isn't wrapped in a function 88 - benchmarking a function only in some calls 89 """ 90 91 def __init__(self, active, description): 92 self.active = active 93 self.description = description 94 95 def __enter__(self): 96 self.started = time.time() 97 98 def __exit__(self, *args): 99 if self.active: 100 duration = time.time() - self.started 101 print("%s finished after %s" % (self.description, humanReadableTime(duration)))
class for benchmarking a function using a "with"-statement. Preferable over the "benchmark" function for the following use cases
- benchmarking a code block that isn't wrapped in a function
- benchmarking a function only in some calls
104class working_dir: 105 """ 106 temporarily change working directory using 'with' statement 107 """ 108 109 def __init__(self, dir): 110 self.dir = dir 111 self.origdir = os.getcwd() 112 113 def __enter__(self): 114 os.chdir(self.dir) 115 116 def __exit__(self, type, value, traceback): 117 os.chdir(self.origdir)
temporarily change working directory using 'with' statement
120class Colorgen: 121 DISTINCT = [ 122 (0.17, 1.0, 0.5), 123 (0.0, 0.9, 1.0), 124 (0.35, 0.67, 0.71), 125 (0.14, 0.9, 1.0), 126 (0.56, 1.0, 0.78), 127 (0.07, 0.8, 0.96), 128 (0.79, 0.83, 0.71), 129 (0.5, 0.71, 0.94), 130 (0.84, 0.79, 0.94), 131 (0.2, 0.76, 0.96), 132 (0.0, 0.24, 0.98), 133 (0.5, 1.0, 0.5), 134 (0.77, 0.25, 1.0), 135 (0.09, 0.76, 0.67), 136 (0.15, 0.22, 1.0), 137 (0.0, 1.0, 0.5), 138 (0.38, 0.33, 1.0), 139 (0.67, 1.0, 0.5), 140 ] 141 142 def __init__(self, hsv, cycleLength=10.67): 143 self.hsv = hsv 144 self.cycle = [int(random.random() * 256) for x in self.hsv] 145 self.cycleOffset = int(round(256 / cycleLength)) 146 self.distinctIndex = 0 147 148 def get_value(self, opt, index): 149 if opt == 'random': 150 return random.random() 151 if opt == 'cycle': 152 # the 255 below is intentional to get all color values when cycling long enough 153 self.cycle[index] = (self.cycle[index] + self.cycleOffset) % 255 154 return self.cycle[index] / 255.0 155 if opt == 'distinct': 156 if index == 0: 157 self.distinctIndex = (self.distinctIndex + 1) % len(self.DISTINCT) 158 return self.DISTINCT[self.distinctIndex][index] 159 return float(opt) 160 161 def floatTuple(self): 162 """return color as a tuple of floats each in [0,1]""" 163 return colorsys.hsv_to_rgb(*[self.get_value(o, i) for i, o in enumerate(self.hsv)]) 164 165 def byteTuple(self): 166 """return color as a tuple of bytes each in [0,255]""" 167 return tuple([int(round(255 * x)) for x in self.floatTuple()]) 168 169 def __call__(self): 170 """return constant or randomized rgb-color string""" 171 return ','.join(map(str, self.byteTuple()))
148 def get_value(self, opt, index): 149 if opt == 'random': 150 return random.random() 151 if opt == 'cycle': 152 # the 255 below is intentional to get all color values when cycling long enough 153 self.cycle[index] = (self.cycle[index] + self.cycleOffset) % 255 154 return self.cycle[index] / 255.0 155 if opt == 'distinct': 156 if index == 0: 157 self.distinctIndex = (self.distinctIndex + 1) % len(self.DISTINCT) 158 return self.DISTINCT[self.distinctIndex][index] 159 return float(opt)
174class priorityDictionary(dict): 175 176 def __init__(self): 177 '''Initialize priorityDictionary by creating binary heap 178 of pairs (value,key). Note that changing or removing a dict entry will 179 not remove the old pair from the heap until it is found by smallest() or 180 until the heap is rebuilt.''' 181 self.__heap = [] 182 dict.__init__(self) 183 184 def smallest(self): 185 '''Find smallest item after removing deleted items from heap.''' 186 if len(self) == 0: 187 raise IndexError("smallest of empty priorityDictionary") 188 heap = self.__heap 189 while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]: 190 lastItem = heap.pop() 191 insertionPoint = 0 192 while 1: 193 smallChild = 2 * insertionPoint + 1 194 if smallChild + 1 < len(heap) and \ 195 heap[smallChild][0] > heap[smallChild + 1][0]: 196 smallChild += 1 197 if smallChild >= len(heap) or lastItem <= heap[smallChild]: 198 heap[insertionPoint] = lastItem 199 break 200 heap[insertionPoint] = heap[smallChild] 201 insertionPoint = smallChild 202 return heap[0][1] 203 204 def __iter__(self): 205 '''Create destructive sorted iterator of priorityDictionary.''' 206 def iterfn(): 207 while len(self) > 0: 208 x = self.smallest() 209 yield x 210 del self[x] 211 return iterfn() 212 213 def __setitem__(self, key, val): 214 '''Change value stored in dictionary and add corresponding 215 pair to heap. Rebuilds the heap if the number of deleted items grows 216 too large, to avoid memory leakage.''' 217 dict.__setitem__(self, key, val) 218 heap = self.__heap 219 if len(heap) > 2 * len(self): 220 self.__heap = [(v, k) for k, v in self.items()] 221 self.__heap.sort() # builtin sort likely faster than O(n) heapify 222 else: 223 newPair = (val, key) 224 insertionPoint = len(heap) 225 heap.append(None) 226 while insertionPoint > 0 and val < heap[(insertionPoint - 1) // 2][0]: 227 heap[insertionPoint] = heap[(insertionPoint - 1) // 2] 228 insertionPoint = (insertionPoint - 1) // 2 229 heap[insertionPoint] = newPair 230 231 def setdefault(self, key, val): 232 '''Reimplement setdefault to call our customized __setitem__.''' 233 if key not in self: 234 self[key] = val 235 return self[key] 236 237 def update(self, other): 238 for key in other.keys(): 239 self[key] = other[key]
176 def __init__(self): 177 '''Initialize priorityDictionary by creating binary heap 178 of pairs (value,key). Note that changing or removing a dict entry will 179 not remove the old pair from the heap until it is found by smallest() or 180 until the heap is rebuilt.''' 181 self.__heap = [] 182 dict.__init__(self)
Initialize priorityDictionary by creating binary heap of pairs (value,key). Note that changing or removing a dict entry will not remove the old pair from the heap until it is found by smallest() or until the heap is rebuilt.
184 def smallest(self): 185 '''Find smallest item after removing deleted items from heap.''' 186 if len(self) == 0: 187 raise IndexError("smallest of empty priorityDictionary") 188 heap = self.__heap 189 while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]: 190 lastItem = heap.pop() 191 insertionPoint = 0 192 while 1: 193 smallChild = 2 * insertionPoint + 1 194 if smallChild + 1 < len(heap) and \ 195 heap[smallChild][0] > heap[smallChild + 1][0]: 196 smallChild += 1 197 if smallChild >= len(heap) or lastItem <= heap[smallChild]: 198 heap[insertionPoint] = lastItem 199 break 200 heap[insertionPoint] = heap[smallChild] 201 insertionPoint = smallChild 202 return heap[0][1]
Find smallest item after removing deleted items from heap.
231 def setdefault(self, key, val): 232 '''Reimplement setdefault to call our customized __setitem__.''' 233 if key not in self: 234 self[key] = val 235 return self[key]
Reimplement setdefault to call our customized __setitem__.
D.update([E, ]**F) -> None. Update D from dict/iterable E and F. If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
242def getFreeSocketPort(numTries=10): 243 for _ in range(numTries): 244 try: 245 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 246 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 247 s.bind(('', 0)) 248 p = s.getsockname()[1] 249 s.close() 250 return p 251 except socket.error: 252 pass 253 return None
269def humanReadableTime(seconds): 270 result = "" 271 sign = '-' if seconds < 0 else '' 272 seconds = abs(seconds) 273 ds = 3600 * 24 274 if seconds > ds: 275 result = "%s:" % int(seconds / ds) 276 seconds = seconds % ds 277 result += "%02i:" % int(seconds / 3600) 278 seconds = seconds % 3600 279 result += "%02i:" % int(seconds / 60) 280 seconds = seconds % 60 281 if seconds == int(seconds): 282 seconds = int(seconds) 283 result += "%02i" % seconds 284 return sign + result
290def parseTime(t, factor=1): 291 try: 292 return float(t) * factor 293 except ValueError: 294 pass 295 try: 296 # prepended zero is ignored if the date value already contains days 297 days, hours, minutes, seconds = ([0] + list(map(float, t.split(':'))))[-4:] 298 sign = -1 if t.strip()[0] == '-' else 1 299 return (3600 * 24 * days + 3600 * hours + 60 * minutes + seconds) * sign * factor 300 except ValueError: 301 if t in SPECIAL_TIME_STRINGS: 302 # signal special case but don't crash 303 return None 304 else: 305 raise
313def getFlowNumber(flow): 314 """interpret number of vehicles from a flow parsed by sumolib.xml.parse""" 315 if flow.number is not None: 316 return int(flow.number) 317 if flow.end is not None: 318 duration = parseTime(flow.end) - parseTime(flow.begin) 319 period = 0 320 isFractional = False 321 if flow.period is not None: 322 if 'exp' in flow.period: 323 # use expected value 324 period = 1 / float(flow.period[4:-1]) 325 isFractional = True 326 else: 327 period = float(flow.period) 328 elif flow.probability is not None: 329 # use expected value 330 period = 1 / float(flow.probability) 331 isFractional = True 332 for attr in ['perHour', 'vehsPerHour']: 333 if flow.hasAttribute(attr): 334 period = 3600 / float(flow.getAttribute(attr)) 335 if period > 0: 336 count = duration / period 337 if isFractional: 338 return count 339 else: 340 # flows with a regular period always start at least one vehicle at the begin time 341 return math.ceil(count) 342 else: 343 return 1
interpret number of vehicles from a flow parsed by sumolib.xml.parse
353def openz(fileOrURL, mode="r", **kwargs): 354 """ 355 Opens transparently files, URLs and gzipped files for reading and writing. 356 Special file names "stdout" and "stderr" are handled as well. 357 Also enforces UTF8 on text output / input and should handle BOMs in input. 358 Should be compatible with python 2 and 3. 359 """ 360 encoding = kwargs.get("encoding", "utf8" if "w" in mode else "utf-8-sig") 361 try: 362 if fileOrURL.startswith("http://") or fileOrURL.startswith("https://"): 363 return io.BytesIO(urlopen(fileOrURL).read()) 364 if fileOrURL == "stdout": 365 return sys.stdout 366 if fileOrURL == "stderr": 367 return sys.stderr 368 if fileOrURL.endswith(".gz") and "w" in mode: 369 if "b" in mode: 370 return gzip.open(fileOrURL, mode="w") 371 return gzip.open(fileOrURL, mode="wt", encoding=encoding) 372 if kwargs.get("trySocket") and fileOrURL.isdigit(): 373 return getSocketStream(int(fileOrURL), mode) 374 if kwargs.get("tryGZip", True) and "r" in mode: 375 with gzip.open(fileOrURL) as fd: 376 fd.read(1) 377 if "b" in mode: 378 return gzip.open(fileOrURL) 379 if sys.version_info[0] < 3: 380 return codecs.getreader('utf-8')(gzip.open(fileOrURL)) 381 return gzip.open(fileOrURL, mode="rt", encoding=encoding) 382 except OSError as e: 383 if kwargs.get("printErrors"): 384 print(e, file=sys.stderr) 385 except IOError as e: 386 if kwargs.get("printErrors"): 387 print(e, file=sys.stderr) 388 if "b" in mode: 389 return io.open(fileOrURL, mode=mode) 390 return io.open(fileOrURL, mode=mode, encoding=encoding)
Opens transparently files, URLs and gzipped files for reading and writing. Special file names "stdout" and "stderr" are handled as well. Also enforces UTF8 on text output / input and should handle BOMs in input. Should be compatible with python 2 and 3.
393def short_names(filenames, noEmpty): 394 if len(filenames) == 1: 395 return filenames 396 reversedNames = [''.join(reversed(f)) for f in filenames] 397 prefix = os.path.commonprefix(filenames) 398 suffix = os.path.commonprefix(reversedNames) 399 prefixLen = len(prefix) 400 suffixLen = len(suffix) 401 shortened = [f[prefixLen:-suffixLen] for f in filenames] 402 if noEmpty and any([not f for f in shortened]): 403 # make longer to avoid empty file names 404 base = os.path.basename(prefix) 405 shortened = [base + f for f in shortened] 406 return shortened
409def getBaseName(filename): 410 """strip extensions such as .net.xml.gz""" 411 if filename[-11:] == ".net.xml.gz" and len(filename) > 11: 412 return filename[:-11] 413 elif filename[-8:] == ".net.xml" and len(filename) > 8: 414 return filename[:-8] 415 elif filename[-7:] == ".xml.gz" and len(filename) > 7: 416 return filename[:-7] 417 elif filename[-4:] == ".xml" and len(filename) > 4: 418 return filename[:-4] 419 else: 420 return filename
strip extensions such as .net.xml.gz