1#!/usr/local/bin/python3.8
2# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3# Copyright (C) 2008-2019 German Aerospace Center (DLR) and others.
4# This program and the accompanying materials
5# are made available under the terms of the Eclipse Public License v2.0
6# which accompanies this distribution, and is available at
7# http://www.eclipse.org/legal/epl-v20.html
8# SPDX-License-Identifier: EPL-2.0
9
10# @file    visum_mapDistricts.py
11# @author  Daniel Krajzewicz
12# @author  Michael Behrisch
13# @date    2007-10-25
14# @version $Id$
15
16"""
17
18This script reads a network and a dump file and
19 draws the network, coloring it by the values
20 found within the dump-file.
21"""
22from __future__ import absolute_import
23from __future__ import print_function
24
25import os
26import sys
27import math
28from optparse import OptionParser
29
30sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
31import sumolib.net  # noqa
32import netshiftadaptor  # noqa
33
34
35def computeDistance(n1, n2):
36    xd = n1._coord[0] - n2._coord[0]
37    yd = n1._coord[1] - n2._coord[1]
38    return math.sqrt(xd * xd + yd * yd)
39
40
41def relAngle(angle1, angle2):
42    angle2 -= angle1
43    if angle2 > 180:
44        angle2 = (360. - angle2) * -1.
45    while angle2 < -180:
46        angle2 = 360 + angle2
47    return angle2
48
49
50# initialise
51optParser = OptionParser()
52optParser.add_option("-v", "--verbose", action="store_true", dest="verbose",
53                     default=False, help="tell me what you are doing")
54# i/o
55optParser.add_option("-1", "--net1", dest="net1",
56                     help="SUMO network to use (mandatory)", metavar="FILE")
57optParser.add_option("-2", "--net2", dest="net2",
58                     help="SUMO network to use (mandatory)", metavar="FILE")
59optParser.add_option("-a", "--nodes1", dest="nodes1",
60                     help="The first matching nodes", metavar="NODELIST")
61optParser.add_option("-b", "--nodes2", dest="nodes2",
62                     help="The second matching nodes", metavar="NODELIST")
63# parse options
64(options, args) = optParser.parse_args()
65
66
67# read networks
68if options.verbose:
69    print("Reading net#1...")
70net1 = sumolib.net.readNet(options.net1)
71
72if options.verbose:
73    print("Reading net#2...")
74net2 = sumolib.net.readNet(options.net2)
75
76# reproject the visum net onto the navteq net
77adaptor = netshiftadaptor.NetShiftAdaptor(
78    net1, net2, options.nodes1.split(","), options.nodes2.split(","))
79adaptor.reproject(options.verbose)
80
81# build a speed-up grid
82xmin = 100000
83xmax = -100000
84ymin = 100000
85ymax = -100000
86for n in net1._nodes:
87    xmin = min(xmin, n._coord[0])
88    xmax = max(xmax, n._coord[0])
89    ymin = min(ymin, n._coord[1])
90    ymax = max(ymax, n._coord[1])
91for n in net2._nodes:
92    xmin = min(xmin, n._coord[0])
93    xmax = max(xmax, n._coord[0])
94    ymin = min(ymin, n._coord[1])
95    ymax = max(ymax, n._coord[1])
96xmin = xmin - .1
97xmax = xmax + .1
98ymin = ymin - .1
99ymax = ymax + .1
100
101
102CELLSIZE = 100
103arr1 = []
104arr2 = []
105for y in range(0, CELLSIZE):
106    arr1.append([])
107    arr2.append([])
108    for x in range(0, CELLSIZE):
109        arr1[-1].append([])
110        arr2[-1].append([])
111
112cw = (xmax - xmin) / float(CELLSIZE)
113ch = (ymax - ymin) / float(CELLSIZE)
114for n in net2._nodes:
115    cx = (n._coord[0] - xmin) / cw
116    cy = (n._coord[1] - ymin) / ch
117    arr1[int(cy)][int(cx)].append(n)
118for n in net1._nodes:
119    cx = (n._coord[0] - xmin) / cw
120    cy = (n._coord[1] - ymin) / ch
121    arr2[int(cy)][int(cx)].append(n)
122
123
124# map
125nmap1to2 = {}
126nmap2to1 = {}
127nodes1 = net2._nodes
128nodes2 = net1._nodes
129highwayNodes2 = set()
130highwaySinks2 = set()
131highwaySources2 = set()
132urbanNodes2 = set()
133for n2 in nodes2:
134    noIncoming = 0
135    noOutgoing = 0
136    for e in n2._outgoing:
137        if e.getSpeed() > 80. / 3.6 and e.getSpeed() < 99:
138            highwayNodes2.add(n2)
139        if e.getSpeed() < 99:
140            noOutgoing = noOutgoing + 1
141    for e in n2._incoming:
142        if e.getSpeed() > 80. / 3.6 and e.getSpeed() < 99:
143            highwayNodes2.add(n2)
144        if e.getSpeed() < 99:
145            noIncoming = noIncoming + 1
146    if n2 in highwayNodes2:
147        if noOutgoing == 0:
148            highwaySinks2.add(n2)
149        if noIncoming == 0:
150            highwaySources2.add(n2)
151    else:
152        urbanNodes2.add(n2)
153print("Found " + str(len(highwaySinks2)) + " highway sinks in net2")
154cont = ""
155for n in highwaySinks2:
156    cont = cont + n._id + ", "
157print(cont)
158cont = ""
159print("Found " + str(len(highwaySources2)) + " highway sources in net2")
160for n in highwaySources2:
161    cont = cont + n._id + ", "
162print(cont)
163
164
165fdd = open("dconns.con.xml", "w")
166fdd.write("<connections>\n")
167highwaySinks1 = set()
168highwaySources1 = set()
169origDistrictNodes = {}
170nnn = {}
171for n1 in nodes1:
172    if n1._id.find('-', 1) < 0:
173        continue
174#   if n1._id.find("38208387")<0:
175#       continue
176    un1 = None
177    for e in n1._outgoing:
178        un1 = e._to
179    for e in n1._incoming:
180        un1 = e._from
181    d = n1._id[:n1._id.find('-', 1)]
182    if d[0] == '-':
183        d = d[1:]
184    if d not in origDistrictNodes:
185        origDistrictNodes[d] = []
186    if options.verbose:
187        print("District: " + d)
188    isHighwayNode = False
189    isHighwaySink = False
190    isHighwaySource = False
191    noIncoming = 0
192    noOutgoing = 0
193    noInConns = 0
194    noOutConns = 0
195    for e in un1._outgoing:
196        if e.getSpeed() > 80. / 3.6 and e.getSpeed() < 99:
197            isHighwayNode = True
198        if e.getSpeed() < 99:
199            noOutgoing = noOutgoing + 1
200        if e.getSpeed() > 99:
201            noOutConns = noOutConns + 1
202    for e in un1._incoming:
203        if e.getSpeed() > 80. / 3.6 and e.getSpeed() < 99:
204            isHighwayNode = True
205        if e.getSpeed() < 99:
206            noIncoming = noIncoming + 1
207        if e.getSpeed() > 99:
208            noInConns = noInConns + 1
209    if options.verbose:
210        print("Check", un1._id, noOutgoing, noIncoming)
211    if isHighwayNode:
212        if noOutgoing == 0:
213            highwaySinks1.add(n1)
214            isHighwaySink = True
215        if noIncoming == 0:
216            highwaySources1.add(n1)
217            isHighwaySource = True
218        # the next is a hack for bad visum-networks
219        if noIncoming == 1 and noOutgoing == 1 and noInConns == 1 and noOutConns == 1:
220            highwaySinks1.add(n1)
221            isHighwaySink = True
222            highwaySources1.add(n1)
223            isHighwaySource = True
224
225    best = None
226    bestDist = -1
227    check = urbanNodes2
228    if n1 in highwaySinks1:
229        check = highwaySinks2
230    elif n1 in highwaySources1:
231        check = highwaySources2
232    elif isHighwayNode:
233        check = highwayNodes2
234    for n2 in check:
235        dist = computeDistance(un1, n2)
236        if bestDist == -1 or bestDist > dist:
237            best = n2
238            bestDist = dist
239    if best:
240        nnn[best] = n1
241        if d not in nmap1to2:
242            nmap1to2[d] = []
243        if best not in nmap1to2[d]:
244            nmap1to2[d].append(best)
245        if best not in nmap2to1:
246            nmap2to1[best] = []
247        if n1 not in nmap2to1[best]:
248            nmap2to1[best].append(n1)
249        if options.verbose:
250            print("a: " + d + "<->" + best._id)
251        if best not in origDistrictNodes[d]:
252            origDistrictNodes[d].append(best)
253
254    preBest = best
255    best = None
256    bestDist = -1
257    check = []
258    if n1 in highwaySinks1 or preBest in highwaySinks2:
259        check = highwaySources2
260    elif n1 in highwaySources1 or preBest in highwaySources2:
261        check = highwaySinks2
262    elif isHighwayNode:
263        check = highwayNodes2
264    for n2 in check:
265        dist = computeDistance(un1, n2)
266        if (bestDist == -1 or bestDist > dist) and n2 != preBest:
267            best = n2
268            bestDist = dist
269    if best:
270        nnn[best] = n1
271        if d not in nmap1to2:
272            nmap1to2[d] = []
273        if best not in nmap1to2[d]:
274            nmap1to2[d].append(best)
275        if best not in nmap2to1:
276            nmap2to1[best] = []
277        if n1 not in nmap2to1[best]:
278            nmap2to1[best].append(n1)
279        print("b: " + d + "<->" + best._id)
280        if best not in origDistrictNodes[d]:
281            origDistrictNodes[d].append(best)
282
283
284if options.verbose:
285    print("Found " + str(len(highwaySinks1)) + " highway sinks in net1")
286    for n in highwaySinks1:
287        print(n._id)
288    print("Found " + str(len(highwaySources1)) + " highway sources in net1")
289    for n in highwaySources1:
290        print(n._id)
291
292
293connectedNodesConnections = {}
294for d in nmap1to2:
295    for n2 in nmap1to2[d]:
296        if n2 in connectedNodesConnections:
297            continue
298        n1i = net1.addNode("i" + n2._id, nnn[n2]._coord)
299        n1o = net1.addNode("o" + n2._id, nnn[n2]._coord)
300        haveIncoming = False
301        incomingLaneNo = 0
302        for e in n2._incoming:
303            if e._id[0] != "i" and e._id[0] != "o":
304                haveIncoming = True
305                incomingLaneNo = incomingLaneNo + e.getLaneNumber()
306        haveOutgoing = False
307        outgoingLaneNo = 0
308        for e in n2._outgoing:
309            if e._id[0] != "i" and e._id[0] != "o":
310                haveOutgoing = True
311                outgoingLaneNo = outgoingLaneNo + e.getLaneNumber()
312        if haveIncoming:
313            e1 = net1.addEdge("o" + n2._id, n2._id, n1o._id, -2)
314            if haveOutgoing:
315                net1.addLane(e1, 20, 100.)
316            else:
317                for i in range(0, incomingLaneNo):
318                    net1.addLane(e1, 20, 100.)
319                    if len(n2._incoming) == 1:
320                        fdd.write('    <connection from="' + n2._incoming[
321                                  0]._id + '" to="' + e1._id + '" lane="' + str(i) + ':' + str(i) + '"/>\n')
322        if haveOutgoing:
323            if options.verbose:
324                print("has outgoing")
325            e2 = net1.addEdge("i" + n2._id, n1i._id, n2._id, -2)
326            if haveIncoming:
327                net1.addLane(e2, 20, 100.)
328            else:
329                for i in range(0, outgoingLaneNo):
330                    net1.addLane(e2, 20, 100.)
331                    if len(n2._outgoing) == 1:
332                        fdd.write('    <connection from="' + e2._id + '" to="' +
333                                  n2._outgoing[0]._id + '" lane="' + str(i) + ':' + str(i) + '"/>\n')
334        connectedNodesConnections[n2] = [n1i, n1o]
335
336
337newDistricts = {}
338districtSources = {}
339districtSinks = {}
340mappedDistrictNodes = {}
341connNodes = {}
342dRemap = {}
343for d in nmap1to2:
344    newDistricts[d] = []
345    if len(nmap1to2[d]) == 1:
346        n = nmap1to2[d][0]
347        if n in dRemap:
348            districtSources[d] = districtSources[dRemap[n]]
349            districtSinks[d] = districtSinks[dRemap[n]]
350            newDistricts[d] = []
351            newDistricts[d].append(n._id)
352            continue
353        else:
354            dRemap[n] = d
355        [ni, no] = connectedNodesConnections[n]
356        if len(ni._outgoing) > 0:
357            districtSources[d] = ni._outgoing[0]._id
358        if len(no._incoming) > 0:
359            districtSinks[d] = no._incoming[0]._id
360        fdd.write('    <connection from="' + no._incoming[0]._id + '"/>\n')
361    else:
362        incomingLaneNoG = 0
363        outgoingLaneNoG = 0
364        for n in nmap1to2[d]:
365            for e in n._incoming:
366                if e._id[0] != "i" and e._id[0] != "o":
367                    incomingLaneNoG = incomingLaneNoG + e.getLaneNumber()
368            for e in n._outgoing:
369                if e._id[0] != "i" and e._id[0] != "o":
370                    outgoingLaneNoG = outgoingLaneNoG + e.getLaneNumber()
371        p1 = [0, 0]
372        p11 = [0, 0]
373        p12 = [0, 0]
374        p2 = [0, 0]
375        for n in nmap1to2[d]:
376            p1[0] = p1[0] + n._coord[0]
377            p1[1] = p1[1] + n._coord[1]
378            p2[0] = p2[0] + nnn[n]._coord[0]
379            p2[1] = p2[1] + nnn[n]._coord[1]
380        p2[0] = (p1[0] + p2[0]) / float(len(origDistrictNodes[d]) * 2)
381        p2[1] = (p1[1] + p2[1]) / float(len(origDistrictNodes[d]) * 2)
382        dn2i = net1.addNode("cci" + d, p2)
383        dn2o = net1.addNode("cci" + d, p2)
384        p11[0] = p1[0] / float(len(origDistrictNodes[d]))
385        p11[1] = p1[1] / float(len(origDistrictNodes[d]))
386        dn1o = net1.addNode("co" + d, p11)
387        e1 = net1.addEdge("co" + d, dn1o._id, dn2o._id, -2)
388        for i in range(0, incomingLaneNoG):
389            net1.addLane(e1, 22, 100.)
390        districtSinks[d] = e1._id
391        p12[0] = p1[0] / float(len(origDistrictNodes[d]))
392        p12[1] = p1[1] / float(len(origDistrictNodes[d]))
393        dn1i = net1.addNode("ci" + d, p12)
394        e2 = net1.addEdge("ci" + d, dn2i._id, dn1i._id, -2)
395        for i in range(0, outgoingLaneNoG):
396            net1.addLane(e2, 21, 100.)
397        districtSources[d] = e2._id
398        runningOutLaneNumber = 0
399        runningInLaneNumber = 0
400        for n2 in nmap1to2[d]:
401            [ni, no] = connectedNodesConnections[n2]
402            print("In: " + ni._id + " " + str(len(ni._incoming)) +
403                  " " + str(len(ni._outgoing)))
404            print("Out: " + no._id + " " + str(len(no._incoming)) +
405                  " " + str(len(no._outgoing)))
406            if len(no._incoming) > 0:
407                incomingLaneNo = 0
408                for e in n2._incoming:
409                    if e._id[0] != "i" and e._id[0] != "o":
410                        incomingLaneNo = incomingLaneNo + e.getLaneNumber()
411                e1 = net1.addEdge("o" + d + "#" + n2._id, no._id, dn1o._id, -2)
412                for i in range(0, incomingLaneNo):
413                    net1.addLane(e1, 19, 100.)
414                    fdd.write('    <connection from="' + "o" + d + "#" + n2._id + '" to="' + dn1o._outgoing[
415                              0]._id + '" lane="' + str(i) + ':' + str(runningOutLaneNumber) + '"/>\n')
416                    runningOutLaneNumber = runningOutLaneNumber + 1
417                fdd.write(
418                    '    <connection from="' + dn1o._outgoing[0]._id + '"/>\n')
419                if incomingLaneNo == 0:
420                    net1.addLane(e1, 19, 100.)
421                    runningOutLaneNumber = runningOutLaneNumber + 1
422            if len(ni._outgoing) > 0:
423                outgoingLaneNo = 0
424                for e in n2._outgoing:
425                    if e._id[0] != "i" and e._id[0] != "o":
426                        outgoingLaneNo = outgoingLaneNo + e.getLaneNumber()
427                e2 = net1.addEdge("i" + d + "#" + n2._id, dn1i._id, ni._id, -2)
428                for i in range(0, outgoingLaneNo):
429                    net1.addLane(e2, 18, 100.)
430                    fdd.write('    <connection from="' + dn1i._incoming[
431                              0]._id + '" to="' + "i" + d + "#" + n2._id + '" lane="' + str(runningInLaneNumber) +
432                              ':' + str(i) + '"/>\n')
433                    runningInLaneNumber = runningInLaneNumber + 1
434                if outgoingLaneNo == 0:
435                    net1.addLane(e2, 18, 100.)
436                    runningInLaneNumber = runningInLaneNumber + 1
437
438fd = open("districts.xml", "w")
439fd.write("<tazs>\n")
440for d in newDistricts:
441    fd.write('    <taz id="' + d + '">\n')
442    if d in districtSources:
443        fd.write(
444            '        <tazSource id="' + districtSources[d] + '" weight="1"/>\n')
445    if d in districtSinks:
446        fd.write(
447            '        <tazSink id="' + districtSinks[d] + '" weight="1"/>\n')
448    fd.write('    </taz>\n')
449fd.write("</tazs>\n")
450fd.close()
451
452
453def writeNode(fd, node):
454    fd.write("   <node id=\"" + node._id + "\" x=\"" +
455             str(node._coord[0]) + "\" y=\"" + str(node._coord[1]) + "\"/>\n")
456
457
458def writeEdge(fd, edge, withGeom=True):
459    fd.write("   <edge id=\"" + edge._id + "\" fromNode=\"" +
460             edge._from._id + "\" toNode=\"" + edge._to._id)
461    fd.write("\" speed=\"" + str(edge._speed))
462    fd.write("\" priority=\"" + str(edge._priority))
463    if withGeom:
464        fd.write("\" spreadType=\"center")
465    fd.write("\" numLanes=\"" + str(len(edge._lanes)) + "\"")
466    shape = edge.getShape()
467    if withGeom:
468        fd.write(" shape=\"")
469        for i, c in enumerate(shape):
470            if i != 0:
471                fd.write(" ")
472            fd.write(str(c[0]) + "," + str(c[1]))
473        fd.write("\"")
474    fd.write("/>\n")
475
476
477def writeNodes(net):
478    fd = open("nodes.xml", "w")
479    fd.write("<nodes>\n")
480    for node in net._nodes:
481        writeNode(fd, node)
482    fd.write("</nodes>\n")
483    fd.close()
484
485
486def writeEdges(net):
487    fd = open("edges.xml", "w")
488    fd.write("<edges>\n")
489    for edge in net._edges:
490        if edge._id.find("#") > 0 or edge._id.find("c") >= 0 or edge._id.find("i") >= 0:
491            writeEdge(fd, edge, False)
492        else:
493            writeEdge(fd, edge)
494    fd.write("</edges>\n")
495    fd.close()
496
497
498fdd.write("</connections>\n")
499writeNodes(net1)
500writeEdges(net1)
501