Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/osmWebWizard.py
169659 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2014-2025 German Aerospace Center (DLR) and others.
4
# This program and the accompanying materials are made available under the
5
# terms of the Eclipse Public License 2.0 which is available at
6
# https://www.eclipse.org/legal/epl-2.0/
7
# This Source Code may also be made available under the following Secondary
8
# Licenses when the conditions for such availability set forth in the Eclipse
9
# Public License 2.0 are satisfied: GNU General Public License, version 2
10
# or later which is available at
11
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
14
# @file osmWebWizard.py
15
# @author Jakob Stigloher
16
# @author Jakob Erdmann
17
# @author Michael Behrisch
18
# @author Mirko Barthauer
19
# @date 2014-14-10
20
21
from __future__ import absolute_import
22
from __future__ import print_function
23
24
import os
25
import sys
26
import stat
27
import traceback
28
import webbrowser
29
import datetime
30
import json
31
import threading
32
import subprocess
33
import tempfile
34
import shutil
35
from zipfile import ZipFile
36
import base64
37
import ssl
38
import collections
39
40
import osmGet
41
import osmBuild
42
import randomTrips
43
import ptlines2flows
44
import tileGet
45
import sumolib
46
from webWizard.SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
47
48
SUMO_HOME = os.environ.get("SUMO_HOME", os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
49
50
try:
51
basestring
52
# Allows isinstance(foo, basestring) to work in Python 3
53
except NameError:
54
basestring = str
55
56
typemapdir = os.path.join("${SUMO_HOME}" if "SUMO_HOME" in os.environ else SUMO_HOME, "data", "typemap")
57
typemaps = {
58
"net": os.path.join(typemapdir, "osmNetconvert.typ.xml"),
59
"poly": os.path.join(typemapdir, "osmPolyconvert.typ.xml"),
60
"urban": os.path.join(typemapdir, "osmNetconvertUrbanDe.typ.xml"),
61
"pedestrians": os.path.join(typemapdir, "osmNetconvertPedestrians.typ.xml"),
62
"ships": os.path.join(typemapdir, "osmNetconvertShips.typ.xml"),
63
"bicycles": os.path.join(typemapdir, "osmNetconvertBicycle.typ.xml"),
64
"aerialway": os.path.join(typemapdir, "osmNetconvertAerialway.typ.xml"),
65
}
66
67
# common parameters
68
CP = ["--trip-attributes", 'departLane="best"',
69
"--fringe-start-attributes", 'departSpeed="max"',
70
"--validate", "--remove-loops",
71
"--via-edge-types", ','.join(["highway.motorway",
72
"highway.motorway_link",
73
"highway.trunk_link",
74
"highway.primary_link",
75
"highway.secondary_link",
76
"highway.tertiary_link"])
77
]
78
79
# pedestrian parameters
80
PP = ["--vehicle-class", "pedestrian", "--prefix", "ped", ]
81
82
83
def getParams(vClass, prefix=None):
84
if prefix is None:
85
prefix = vClass
86
return ["--vehicle-class", vClass, "--vclass", vClass, "--prefix", prefix]
87
88
89
vehicleParameters = {
90
"passenger": CP + getParams("passenger", "veh") + ["--min-distance", "300", "--min-distance.fringe", "10",
91
"--allow-fringe.min-length", "1000", "--lanes"],
92
"truck": CP + getParams("truck") + ["--min-distance", "600", "--min-distance.fringe", "10"], # noqa
93
"bus": CP + getParams("bus") + ["--min-distance", "600", "--min-distance.fringe", "10"], # noqa
94
"motorcycle": CP + getParams("motorcycle") + ["--max-distance", "1200"], # noqa
95
"bicycle": CP + getParams("bicycle", "bike") + ["--max-distance", "8000"], # noqa
96
"tram": CP + getParams("tram") + ["--min-distance", "1200", "--min-distance.fringe", "10"], # noqa
97
"rail_urban": CP + getParams("rail_urban") + ["--min-distance", "1800", "--min-distance.fringe", "10"], # noqa
98
"rail": CP + getParams("rail") + ["--min-distance", "2400", "--min-distance.fringe", "10"], # noqa
99
"ship": getParams("ship") + ["--fringe-start-attributes", 'departSpeed="max"', "--validate"],
100
"pedestrian": PP + ["--pedestrians", "--max-distance", "2000"],
101
"persontrips": PP + ["--persontrips", "--trip-attributes", 'modes="public"'],
102
}
103
104
vehicleNames = {
105
"passenger": "Cars",
106
"truck": "Trucks",
107
"bus": "Bus",
108
"motorcycle": "Motorcycles",
109
"bicycle": "Bicycles",
110
"pedestrian": "Pedestrians",
111
"tram": "Trams",
112
"rail_urban": "Urban Trains",
113
"rail": "Trains",
114
"ship": "Ships"
115
}
116
117
118
# all can read and execute, only user can read batch files
119
BATCH_MODE = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
120
BATCH_MODE |= stat.S_IWUSR
121
BATCH_MODE |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
122
123
124
def quoted_str(s):
125
if isinstance(s, float):
126
return "%.6f" % s
127
elif not isinstance(s, str):
128
return str(s)
129
elif '"' in s or ' ' in s:
130
return '"' + s.replace('"', '\\"') + '"'
131
else:
132
return s
133
134
135
class Builder(object):
136
prefix = "osm"
137
138
def __init__(self, data, local):
139
self.files = {}
140
self.files_relative = {}
141
self.data = data
142
143
self.tmp = None
144
if local:
145
now = data.get("outputDir", datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S"))
146
for base in ['', os.path.expanduser('~/Sumo')]:
147
try:
148
self.tmp = os.path.abspath(os.path.join(base, now))
149
os.makedirs(self.tmp, exist_ok=data.get("outputDirExistOk", False))
150
break
151
except Exception:
152
print("Cannot create directory '%s'." % self.tmp, file=sys.stderr)
153
self.tmp = None
154
if self.tmp is None:
155
self.tmp = tempfile.mkdtemp()
156
157
self.origDir = os.getcwd()
158
print("Building scenario in '%s'." % self.tmp)
159
160
def report(self, message):
161
pass
162
163
def filename(self, use, name, usePrefix=True):
164
prefix = self.prefix if usePrefix else ''
165
self.files_relative[use] = prefix + name
166
self.files[use] = os.path.join(self.tmp, prefix + name)
167
168
def getRelative(self, options):
169
result = []
170
dirname = self.tmp
171
ld = len(dirname)
172
for o in options:
173
if isinstance(o, basestring) and o[:ld] == dirname:
174
remove = o[:ld+1]
175
result.append(o.replace(remove, ''))
176
else:
177
result.append(o)
178
return result
179
180
def build(self):
181
# output name for the osm file, will be used by osmBuild, can be
182
# deleted after the process
183
self.filename("osm", "_bbox.osm.xml.gz")
184
# output name for the net file, will be used by osmBuild, randomTrips and sumo-gui
185
self.filename("net", ".net.xml.gz")
186
187
if self.data.get("coords") is None:
188
# fixed input testing mode
189
self.files["osm"] = self.data['osm']
190
else:
191
self.report("Downloading map data")
192
osmArgs = ["-b=" + (",".join(map(str, self.data["coords"]))), "-p", self.prefix, "-d", self.tmp, "-z"]
193
if self.data["poly"]:
194
osmArgs.append("--shapes")
195
if 'osmMirror' in self.data:
196
osmArgs += ["-u", self.data["osmMirror"]]
197
if 'roadTypes' in self.data:
198
osmArgs += ["-r", json.dumps(self.data["roadTypes"])]
199
osmGet.get(osmArgs)
200
201
if not os.path.exists(self.files["osm"]):
202
raise RuntimeError("Download failed")
203
204
options = ["-f", self.files["osm"], "-p", self.prefix, "-d", self.tmp, "-z"]
205
206
self.additionalFiles = []
207
self.routenames = []
208
209
if self.data["poly"]:
210
# output name for the poly file, will be used by osmBuild and sumo-gui
211
self.filename("poly", ".poly.xml.gz")
212
213
options += ["-m", typemaps["poly"]]
214
self.additionalFiles.append(self.files["poly"])
215
216
typefiles = [typemaps["net"]]
217
# leading space ensures that arguments starting with -- are not
218
# misinterpreted as options
219
netconvertOptions = " " + osmBuild.DEFAULT_NETCONVERT_OPTS
220
if self.data.get("options"):
221
netconvertOptions += "," + self.data["options"]
222
netconvertOptions += ",--tls.default-type,actuated"
223
# netconvertOptions += ",--default.spreadtype,roadCenter"
224
if "pedestrian" in self.data["vehicles"]:
225
# sidewalks are already included via typefile
226
netconvertOptions += ",--crossings.guess"
227
netconvertOptions += ",--osm.sidewalks"
228
typefiles.append(typemaps["urban"])
229
typefiles.append(typemaps["pedestrians"])
230
if "ship" in self.data["vehicles"]:
231
typefiles.append(typemaps["ships"])
232
if "bicycle" in self.data["vehicles"]:
233
typefiles.append(typemaps["bicycles"])
234
netconvertOptions += ",--osm.bike-access"
235
# special treatment for public transport
236
if self.data["publicTransport"]:
237
self.filename("stops", "_stops.add.xml")
238
netconvertOptions += ",--ptstop-output,%s" % self.files["stops"]
239
netconvertOptions += ",--ptline-clean-up"
240
self.filename("ptlines", "_ptlines.xml")
241
self.filename("ptroutes", "_pt.rou.xml")
242
netconvertOptions += ",--ptline-output,%s" % self.files["ptlines"]
243
self.additionalFiles.append(self.files["stops"])
244
self.routenames.append(self.files["ptroutes"])
245
netconvertOptions += ",--railway.topology.repair"
246
typefiles.append(typemaps["aerialway"])
247
if self.data["leftHand"]:
248
netconvertOptions += ",--lefthand"
249
if self.data.get("verbose"):
250
netconvertOptions += ",--verbose"
251
if self.data["decal"]:
252
# change projection to web-mercator to match the background image projection
253
netconvertOptions += ",--proj,+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" # noqa
254
255
if self.data["carOnlyNetwork"]:
256
if self.data["publicTransport"]:
257
options += ["--vehicle-classes", "publicTransport"]
258
else:
259
options += ["--vehicle-classes", "passenger"]
260
261
options += ["--netconvert-typemap", ','.join(typefiles)]
262
options += ["--netconvert-options", netconvertOptions]
263
options += ["--polyconvert-options", " -v,--osm.keep-full-type,--osm.merge-relations,1"]
264
265
self.report("Converting map data")
266
osmBuild.build(options)
267
ptOptions = None
268
begin = self.data.get("begin", 0)
269
if self.data["publicTransport"]:
270
self.report("Generating public transport schedule")
271
self.filename("pt_stopinfos", "stopinfos.xml", False)
272
self.filename("pt_vehroutes", "vehroutes.xml", False)
273
self.filename("pt_trips", "trips.trips.xml", False)
274
ptOptions = [
275
"-n", self.files["net"],
276
"-b", begin,
277
"-e", begin + self.data["duration"],
278
"-p", "600",
279
"--random-begin",
280
"--seed", "42",
281
"--ptstops", self.files["stops"],
282
"--ptlines", self.files["ptlines"],
283
"-o", self.files["ptroutes"],
284
"--ignore-errors",
285
# "--no-vtypes",
286
"--vtype-prefix", "pt_",
287
"--stopinfos-file", self.files["pt_stopinfos"],
288
"--routes-file", self.files["pt_vehroutes"],
289
"--trips-file", self.files["pt_trips"],
290
"--min-stops", "0",
291
"--extend-to-fringe",
292
"--verbose",
293
]
294
ptlines2flows.main(ptlines2flows.get_options(ptOptions))
295
296
if self.data["decal"]:
297
self.report("Downloading background images")
298
tileOptions = [
299
"-n", self.files["net"],
300
"-t", "100",
301
"-d", "background_images",
302
"-l", "-300",
303
"-a", "Mozilla/5.0 (X11; Linux x86_64) osmWebWizard.py/1.0 (+https://github.com/eclipse-sumo/sumo)",
304
]
305
try:
306
os.chdir(self.tmp)
307
os.mkdir("background_images")
308
tileGet.get(tileOptions)
309
self.report("Success.")
310
self.decalError = False
311
except Exception as e:
312
os.chdir(self.tmp)
313
shutil.rmtree("background_images", ignore_errors=True)
314
self.report("Error while downloading background images: %s" % e)
315
self.decalError = True
316
317
if self.data["vehicles"] or ptOptions:
318
# routenames stores all routefiles and will join the items later, will
319
# be used by sumo-gui
320
randomTripsCalls = []
321
322
self.edges = sumolib.net.readNet(os.path.join(self.tmp, self.files["net"])).getEdges()
323
324
seed = 42
325
for vehicle in sorted(self.data["vehicles"].keys()):
326
options = self.data["vehicles"][vehicle]
327
self.report("Processing %s" % vehicleNames[vehicle])
328
329
self.filename("route", ".%s.rou.xml" % vehicle)
330
self.filename("trips", ".%s.trips.xml" % vehicle)
331
332
try:
333
options = self.parseTripOpts(vehicle, options, self.data["publicTransport"])
334
except ZeroDivisionError:
335
continue
336
337
if vehicle == "pedestrian" and self.data["publicTransport"]:
338
options += ["--additional-files", ",".join([self.files["stops"], self.files["ptroutes"]])]
339
options += ["--persontrip.walk-opposite-factor", "0.8"]
340
options += ["--duarouter-weights.tls-penalty", "20"]
341
342
options += ["--seed", str(seed)]
343
seed += 1
344
345
try:
346
randomTrips.main(randomTrips.get_options(options))
347
except ValueError:
348
print("Could not generate %s traffic" % vehicle, file=sys.stderr)
349
continue
350
351
randomTripsCalls.append(options)
352
353
# --validate is not called for pedestrians
354
if vehicle == "pedestrian":
355
self.routenames.append(self.files["route"])
356
else:
357
self.routenames.append(self.files["trips"])
358
# clean up unused route file (was only used for validation)
359
os.remove(self.files["route"])
360
361
# create a batch file for reproducing calls to randomTrips.py
362
if os.name == "posix":
363
SUMO_HOME_VAR = "$SUMO_HOME"
364
else:
365
SUMO_HOME_VAR = "%SUMO_HOME%"
366
367
randomTripsPath = os.path.join(SUMO_HOME_VAR, "tools", "randomTrips.py")
368
ptlines2flowsPath = os.path.join(SUMO_HOME_VAR, "tools", "ptlines2flows.py")
369
370
self.filename("build.bat", "build.bat", False)
371
batchFile = self.files["build.bat"]
372
with open(batchFile, 'w') as f:
373
if os.name == "posix":
374
f.write("#!/bin/bash\n")
375
if ptOptions is not None:
376
f.write('python "%s" %s\n' %
377
(ptlines2flowsPath, " ".join(map(quoted_str, self.getRelative(ptOptions)))))
378
for opts in randomTripsCalls:
379
f.write('python "%s" %s\n' %
380
(randomTripsPath, " ".join(map(quoted_str, self.getRelative(opts)))))
381
os.chmod(batchFile, BATCH_MODE)
382
383
def parseTripOpts(self, vehicle, options, publicTransport):
384
"Return an option list for randomTrips.py for a given vehicle"
385
386
begin = self.data.get("begin", 0)
387
opts = ["-n", self.files["net"], "--fringe-factor", options.get("fringeFactor", "1"),
388
"--insertion-density", options["count"],
389
"-o", self.files["trips"],
390
"-r", self.files["route"],
391
"-b", begin,
392
"-e", begin + self.data["duration"]]
393
if vehicle == "pedestrian" and publicTransport:
394
opts += vehicleParameters["persontrips"]
395
else:
396
opts += vehicleParameters[vehicle]
397
398
return opts
399
400
def makeConfigFile(self):
401
"Save the configuration for SUMO in a file"
402
403
self.report("Generating configuration file")
404
405
self.filename("guisettings", ".view.xml")
406
with open(self.files["guisettings"], 'w') as f:
407
if self.data["decal"] and not self.decalError:
408
f.write("""
409
<viewsettings>
410
<scheme name="real world"/>
411
<delay value="20"/>
412
<include href="background_images/settings.xml"/>
413
</viewsettings>
414
""")
415
else:
416
f.write("""
417
<viewsettings>
418
<scheme name="real world"/>
419
<delay value="20"/>
420
</viewsettings>
421
""")
422
sumo = sumolib.checkBinary("sumo")
423
424
self.filename("config", ".sumocfg")
425
opts = [sumo, "-n", self.files_relative["net"], "--gui-settings-file", self.files_relative["guisettings"],
426
"--duration-log.statistics",
427
"--device.rerouting.adaptation-interval", "10",
428
"--device.rerouting.adaptation-steps", "18",
429
"--tls.actuated.jam-threshold", "30",
430
"-v", "--no-step-log", "--save-configuration", self.files_relative["config"], "--ignore-route-errors"]
431
432
if self.routenames:
433
opts += ["-r", ",".join(self.getRelative(self.routenames))]
434
435
# extra output if the scenario contains traffic
436
opts += ["--tripinfo-output", "tripinfos.xml"]
437
opts += ["--statistic-output", "stats.xml"]
438
439
self.filename("outadd", "output.add.xml", False)
440
with open(self.files["outadd"], 'w') as fadd:
441
sumolib.writeXMLHeader(fadd, "$Id$", "additional")
442
fadd.write(' <edgeData id="wizard_example" period="3600" file="edgeData.xml"/>\n')
443
fadd.write("</additional>\n")
444
self.additionalFiles.append(self.files["outadd"])
445
446
if self.data["publicTransport"]:
447
opts += ["--stop-output", "stopinfos.xml"]
448
449
if len(self.additionalFiles) > 0:
450
opts += ["-a", ",".join(self.getRelative(self.additionalFiles))]
451
452
subprocess.call(opts, cwd=self.tmp)
453
454
def createBatch(self):
455
"Create a batch / bash file "
456
457
# use bat as extension, as only Windows needs the extension .bat
458
self.filename("run.bat", "run.bat", False)
459
460
with open(self.files["run.bat"], "w") as batchfile:
461
batchfile.write("sumo-gui -c " + self.files_relative["config"])
462
463
os.chmod(self.files["run.bat"], BATCH_MODE)
464
465
def openSUMO(self):
466
self.report("Calling SUMO")
467
468
sumogui = sumolib.checkBinary("sumo-gui")
469
470
subprocess.Popen([sumogui, "-c", self.files["config"]], cwd=self.tmp)
471
472
def createZip(self):
473
"Create a zip file with everything inside which SUMO GUI needs, returns it base64 encoded"
474
475
self.report("Building zip file")
476
477
self.filename("zip", ".zip")
478
479
with ZipFile(self.files["zip"], "w") as zipfile:
480
files = ["net", "guisettings", "config", "run.bat"]
481
482
if self.data["vehicles"] or self.data["publicTransport"]:
483
files += ["build.bat"]
484
485
if self.data["poly"]:
486
files += ["poly"]
487
488
# translate the pseudo file names to real file names
489
files = list(map(lambda name: self.files[name], files))
490
491
if self.data["vehicles"]:
492
files += self.routenames
493
494
# add the files to the zip
495
for name in files:
496
zipfile.write(name)
497
498
# now open the zip file as raw
499
with open(self.files["zip"], "rb") as zipfile:
500
content = zipfile.read()
501
502
return base64.b64encode(content)
503
504
def finalize(self):
505
try:
506
shutil.rmtree(self.tmp)
507
except Exception:
508
pass
509
510
511
class OSMImporterWebSocket(WebSocket):
512
513
local = False
514
outputDir = None
515
516
def report(self, message):
517
print(message)
518
self.sendMessage(u"report " + message)
519
# number of remaining steps
520
self.steps -= 1
521
522
def handleMessage(self):
523
data = json.loads(self.data)
524
525
thread = threading.Thread(target=self.build, args=(data,))
526
thread.start()
527
528
def build(self, data):
529
if self.outputDir is not None:
530
data['outputDir'] = self.outputDir
531
builder = Builder(data, self.local)
532
builder.report = self.report
533
534
self.steps = len(data["vehicles"]) + 4
535
self.sendMessage(u"steps %s" % self.steps)
536
537
try:
538
builder.build()
539
builder.makeConfigFile()
540
builder.createBatch()
541
542
if self.local:
543
builder.openSUMO()
544
else:
545
data = builder.createZip()
546
builder.finalize()
547
548
self.sendMessage(b"zip " + data)
549
except ssl.CertificateError:
550
self.report("Error with SSL certificate, try 'pip install -U certifi'.")
551
except Exception as e:
552
print(traceback.format_exc())
553
# reset 'Generate Scenario' button
554
while self.steps > 0:
555
self.report(str(e) + " Recovering")
556
if os.path.isdir(builder.tmp) and not os.listdir(builder.tmp):
557
os.rmdir(builder.tmp)
558
os.chdir(builder.origDir)
559
560
561
def get_options(args=None):
562
parser = sumolib.options.ArgumentParser(description="OSM Web Wizard for SUMO - Websocket Server")
563
parser.add_argument("--remote", action="store_true",
564
help="In remote mode, SUMO GUI will not be automatically opened instead a zip file " +
565
"will be generated.")
566
parser.add_argument("--osm-file", default="osm_bbox.osm.xml", dest="osmFile", help="use input file from path.")
567
parser.add_argument("--test-output", dest="testOutputDir",
568
help="Run with pre-defined options on file 'osm_bbox.osm.xml' and " +
569
"write output to the given directory.")
570
parser.add_argument("--bbox", help="bounding box to retrieve in geo coordinates west,south,east,north.")
571
parser.add_argument("-o", "--output", dest="outputDir",
572
help="Write output to the given folder rather than creating a name based on the timestamp")
573
parser.add_argument("--address", default="", help="Address for the Websocket.")
574
parser.add_argument("--port", type=int, default=8010,
575
help="Port for the Websocket. Please edit script.js when using an other port than 8010.")
576
parser.add_argument("-v", "--verbose", action="store_true", default=False, help="tell me what you are doing")
577
parser.add_argument("-b", "--begin", default=0, type=sumolib.miscutils.parseTime,
578
help="Defines the begin time for the scenario.")
579
parser.add_argument("-e", "--end", default=900, type=sumolib.miscutils.parseTime,
580
help="Defines the end time for the scenario.")
581
parser.add_argument("-n", "--netconvert-options", help="additional comma-separated options for netconvert")
582
parser.add_argument("--demand", default="passenger:6f5,bicycle:2f2,pedestrian:4,ship:1f40",
583
help="Traffic demand definition for non-interactive mode.")
584
return parser.parse_args(args)
585
586
587
def main(options):
588
OSMImporterWebSocket.local = options.testOutputDir is not None or not options.remote
589
OSMImporterWebSocket.outputDir = options.outputDir
590
if options.testOutputDir is not None:
591
demand = collections.defaultdict(dict)
592
for mode in options.demand.split(","):
593
k, v = mode.split(":")
594
if "f" in v:
595
demand[k]['count'], demand[k]['fringeFactor'] = v.split("f")
596
else:
597
demand[k]['count'] = v
598
data = {u'begin': options.begin,
599
u'duration': options.end - options.begin,
600
u'vehicles': demand,
601
u'osm': os.path.abspath(options.osmFile),
602
u'poly': options.bbox is None, # reduce download size
603
u'publicTransport': True,
604
u'leftHand': False,
605
u'decal': False,
606
u'verbose': options.verbose,
607
u'carOnlyNetwork': False,
608
u'outputDir': options.testOutputDir,
609
u'coords': options.bbox.split(",") if options.bbox else None,
610
u'options': options.netconvert_options
611
}
612
builder = Builder(data, True)
613
builder.build()
614
builder.makeConfigFile()
615
builder.createBatch()
616
if not options.remote:
617
subprocess.call([sumolib.checkBinary("sumo"), "-c", builder.files["config"]])
618
else:
619
if not options.remote:
620
path = os.path.dirname(os.path.realpath(__file__))
621
# on Linux Firefox refuses to open files in /usr/ #16086
622
if os.name != "nt" and not path.startswith(os.path.expanduser('~')):
623
new_path = os.path.expanduser('~/Sumo')
624
wizard_path = os.path.join(new_path, 'webWizard')
625
if not os.path.exists(wizard_path):
626
os.makedirs(new_path, exist_ok=True)
627
shutil.copytree(os.path.join(path, "webWizard"), wizard_path)
628
path = new_path
629
webbrowser.open("file://" + os.path.join(path, "webWizard", "index.html"))
630
631
server = SimpleWebSocketServer(options.address, options.port, OSMImporterWebSocket)
632
server.serveforever()
633
634
635
if __name__ == "__main__":
636
main(get_options())
637
638