1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    network.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16# size limit at 1280x1280
17# http://maps.googleapis.com/maps/api/staticmap?size=500x500&path=color:0x000000|weight:10|44.35789,11.3093|44.4378,11.3935&format=GIF&maptype=satellite&scale=2
18
19import os
20import sys
21import subprocess
22import platform
23
24from xml.sax import saxutils, parse, handler
25if __name__ == '__main__':
26    try:
27        APPDIR = os.path.dirname(os.path.abspath(__file__))
28    except:
29        APPDIR = os.path.dirname(os.path.abspath(sys.argv[0]))
30    SUMOPYDIR = os.path.join(APPDIR, '..', '..')
31    sys.path.append(os.path.join(SUMOPYDIR))
32
33import time
34import numpy as np
35from collections import OrderedDict
36import agilepy.lib_base.classman as cm
37import agilepy.lib_base.arrayman as am
38import agilepy.lib_base.xmlman as xm
39from agilepy.lib_base.misc import filepathlist_to_filepathstring, filepathstring_to_filepathlist
40
41from agilepy.lib_base.processes import Process, CmlMixin, P
42
43from agilepy.lib_base.geometry import *
44
45import netconvert
46import publictransportnet as pt
47
48
49MODES = OrderedDict([
50                    ("ignoring", 0),
51                    ("pedestrian", 1),
52                    ("bicycle", 2),
53                    ("motorcycle", 3),
54                    ("passenger", 4),
55                    ("bus", 5),
56                    ("tram", 6),
57                    ("rail_urban", 7),
58                    ("delivery", 8),
59                    ("private", 9),
60                    ("taxi", 10),
61                    ("hov", 11),
62                    ("evehicle", 12),
63                    ("emergency", 13),
64                    ("authority", 14),
65                    ("army", 15),
66                    ("vip", 16),
67                    ("coach", 17),
68                    ("truck", 18),
69                    ("trailer", 19),
70                    ("rail", 20),
71                    ("rail_electric", 21),
72                    ("moped", 22),
73                    ("custom1", 23),
74                    ("custom2", 24),
75                    ("ship", 25),
76                    ])
77
78ID_MODE_PED = MODES['pedestrian']
79ID_MODE_BIKE = MODES['bicycle']
80ID_MODE_CAR = MODES['passenger']
81
82OSMEDGETYPE_TO_MODES = {'highway.cycleway': ([MODES['bicycle']], 5.6),
83                        'highway.pedestrian': ([MODES['pedestrian']], 0.8),
84                        'highway.footway': ([MODES['pedestrian']], 0.8),
85                        'highway.path': ([MODES['pedestrian'], MODES['bicycle']], 5.6),
86                        'highway.service': ([MODES['delivery'], MODES['bicycle']], 13.8),
87                        }
88
89
90class SumoIdsConf(am.ArrayConf):
91    """
92    Sumo id array coniguration
93    """
94    # def __init__(self, **attrs):
95    #    print 'ColConf',attrs
96
97    def __init__(self, refname, name=None, info=None, perm='rw',  xmltag='id'):
98        if name is None:
99            name = 'ID '+refname
100        if info is None:
101            info = refname + ' ID of SUMO network'
102        am.ArrayConf.__init__(self, attrname='ids_sumo', default='',
103                              dtype='object',
104                              perm=perm,
105                              is_index=True,
106                              name=name,
107                              info=info,
108                              xmltag=xmltag,
109                              )
110
111
112class Modes(am.ArrayObjman):
113    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
114    def __init__(self, parent, **kwargs):
115        ident = 'modes'
116        self._init_objman(ident=ident, parent=parent, name='Transport Modes',
117                          xmltag=('vClasses', 'vClass', 'names'),
118                          version=0.1,
119                          **kwargs)
120
121        self._init_attributes()
122        self.add_default()
123
124    def _init_attributes(self):
125        self.add_col(am.ArrayConf('names', '',
126                                  dtype=np.object,
127                                  perm='r',
128                                  is_index=True,
129                                  name='Name',
130                                  info='Name of mode. Used as key for implementing acces restrictions on edges as well as demand modelling.',
131                                  xmltag='vClass',
132                                  ))
133
134        self.add_col(am.ArrayConf('speeds_max', 50.0/3.6,
135                                  dtype=np.float32,
136                                  perm='rw',
137                                  name='Max. Speed',
138                                  unit='m/s',
139                                  info='Maximum possible speed for this mode. Speed is used to estimate free flow link travel times, mainly for routig purposes. Note that speeds are usully limited by the lane speed attribute',
140                                  ))
141        if self.get_version() < 0.1:
142            self.clear()
143            self.add_default()
144            self.set_version(0.1)
145
146    def get_id_mode(self, modename):
147        return self.names.get_id_from_index(modename)
148
149    def has_modename(self, modename):
150        return self.names.has_index(modename)
151
152    def format_ids(self, ids):
153        return ','.join(self.names[ids])
154
155    def get_id_from_formatted(self, idstr):
156        return self.names.get_id_from_index(idstr)
157
158    def get_ids_from_formatted(self, idstrs):
159        return self.names.get_ids_from_indices_save(idstrs.split(','))
160
161    def add_default(self):
162        """
163        Sets the default maximum possible speed for certain modes.
164        """
165        # print 'MODES.add_default'
166        self.add_rows(ids=MODES.values(), names=MODES.keys())
167
168        # these speeds are used to estimate free flow link travel times
169        # mainly for routig purposes
170        # note that speed limits are usully limited by the lane speed attribute
171        speeds_max_kmph = OrderedDict([
172            ("ignoring", 100.0),
173            ("pedestrian", 3.6),
174            ("bicycle", 25.0),
175            ("motorcycle", 130.0),
176            ("passenger", 160.0),
177            ("bus", 90.0),
178            ("tram", 50.0),
179            ("rail_urban", 160.0),
180            ("delivery", 100),
181            ("private", 160),
182            ("taxi", 160),
183            ("hov", 160),
184            ("evehicle", 160),
185            ("emergency", 160),
186            ("authority", 160),
187            ("army", 130),
188            ("vip", 160),
189            ("coach", 90),
190            ("truck", 90),
191            ("trailer", 90),
192            ("rail", 250),
193            ("rail_electric", 300),
194            ("moped", 25),
195            ("custom1", 100),
196            ("custom2", 160),
197            ("ship", 30),
198        ])
199
200        for mode, speed_kmph in speeds_max_kmph.iteritems():
201            self.speeds_max[self.get_id_mode(mode)] = float(speed_kmph)/3.6
202
203        # print '  self.speeds_max',self.speeds_max.get_value()
204
205
206class TrafficLightProgram(am.ArrayObjman):
207    def __init__(self, ident, parent, **kwargs):
208        self._init_objman(ident, parent=parent,
209                          name='TLL Program',
210                          info='Signale phases of a traffic light program.',
211                          xmltag=('', 'phase', ''), **kwargs)
212
213        self.add_col(am.NumArrayConf('durations', 0,
214                                     dtype=np.int32,
215                                     name='Duration',
216                                     unit='s',
217                                     info='The duration of the phase.',
218                                     xmltag='duration',
219                                     ))
220
221        self.add_col(am.NumArrayConf('durations_min', 0,
222                                     dtype=np.int32,
223                                     name='Min. duration',
224                                     unit='s',
225                                     info='The minimum duration of the phase when using type actuated. Optional, defaults to duration.',
226                                     xmltag='minDur',
227                                     ))
228
229        self.add_col(am.NumArrayConf('durations_max', 0,
230                                     dtype=np.int32,
231                                     name='Max. duration',
232                                     unit='s',
233                                     info='The maximum duration of the phase when using type actuated. Optional, defaults to duration.',
234                                     xmltag='maxDur',
235                                     ))
236
237        self.add_col(am.ArrayConf('states', None,
238                                  dtype=np.object,
239                                  perm='rw',
240                                  name='State',
241                                  info="The traffic light states for this phase. Values can be one of these characters: 'r'=red, 'y'=yellow, 'g'=green give priority, 'G'=Green always priority, 'o'=blinking ,'O'=TLS switched off",
242                                  xmltag='state',
243                                  ))
244
245    def add_multi(self, **kwargs):
246        # print 'add_multi',self.ident
247        # print '  durations',kwargs.get('durations',None)
248        # print '  durations_min',kwargs.get('durations_min',None)
249        # print '  durations_max',kwargs.get('durations_max',None)
250        # print '  states',kwargs.get('states',None)
251        return self.add_rows(durations=kwargs.get('durations', None),
252                             durations_min=kwargs.get('durations_min', None),
253                             durations_max=kwargs.get('durations_max', None),
254                             states=kwargs.get('states', None),
255                             )
256
257    # def  write_xml(self, fd, indent, **kwargs):
258    #
259    #    # never print begin-end tags
260    #    # this could go into xml config
261    #    if kwargs.has_key('is_print_begin_end'):
262    #        del kwargs['is_print_begin_end']
263    #    am.ArrayObjman.write_xml(self, fd, indent,is_print_begin_end = False,**kwargs)
264
265
266class TrafficLightLogics(am.ArrayObjman):
267    def __init__(self, ident, tlss, **kwargs):
268        self._init_objman(ident, parent=tlss,
269                          name='Traffic Light Logics',
270                          info='Traffic light Logics (TLLs) for Trafic Light Systems (TLSs).',
271                          xmltag=('tlLogics', 'tlLogic', 'ids_tls'),
272                          **kwargs)
273
274        self.add_col(am.IdsArrayConf('ids_tls', tlss,
275                                     groupnames=['state'],
276                                     name='ID tls',
277                                     info='ID of traffic light system.  Typically the id for a traffic light is identical with the junction id. The name may be obtained by right-clicking the red/green bars in front of a controlled intersection.',
278                                     xmltag='id',  # this will be ID TLS tag used as ID in xml file
279                                     ))
280
281        self.add_col(am.ArrayConf('ids_prog', '',
282                                  dtype=np.object,
283                                  perm='rw',
284                                  name='Prog ID',
285                                  info='Sumo program ID, which is unique within the same traffic light system.',
286                                  xmltag='programID',
287                                  ))
288
289        self.add_col(am.ArrayConf('ptypes', 1,
290                                  choices={
291                                      "static": 1,
292                                      "actuated": 2,
293                                  },
294                                  dtype=np.int32,
295                                  perm='rw',
296                                  name='Prog. type',
297                                  info='The type of the traffic light program (fixed phase durations, phase prolongation based time gaps between vehicles).',
298                                  xmltag='type',
299                                  ))
300
301        self.add_col(am.NumArrayConf('offsets', 0,
302                                     dtype=np.int32,
303                                     perm='rw',
304                                     name='Offset',
305                                     unit='s',
306                                     info='The initial time offset of the program.',
307                                     #is_plugin = True,
308                                     xmltag='offset',
309                                     ))
310
311        self.add_col(cm.ObjsConf('programs',
312                                 name='program',
313                                 info='Tls program.',
314                                 ))
315
316    def make(self, id_tls, id_prog=None, ptype=None, offset=None, **kwargs_prog):
317        # print 'make',id_tls,id_prog
318        if id_prog is None:
319            id_prog = str(len(np.flatnonzero(self.ids_tls == id_tls)))
320
321        id_tll = self.add_row(ids_tls=id_tls,
322                              ids_prog=id_prog,
323                              ptypes=ptype,
324                              offsets=offset,
325                              )
326        # init programme
327        program = TrafficLightProgram(('prog', id_tll), self)
328        self.programs[id_tll] = program
329
330        # add phases
331        program.add_multi(**kwargs_prog)
332
333        return id_tll
334
335
336class TrafficLightSystems(am.ArrayObjman):
337    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Traffic_Light_Program_Definition
338
339    def __init__(self, net, **kwargs):
340
341        self._init_objman(ident='tlss', parent=net,
342                          name='Traffic Light Systems',
343                                # actually tlls table is exported, but ids_sumo is required for ID
344                                xmltag=('tlSystems', 'tlSystem', 'ids_sumo'),
345                          **kwargs)
346
347        self.add_col(SumoIdsConf('TLS', info='SUMO ID of traffic light system.',
348                                 xmltag='id'))
349
350        self.add(cm.ObjConf(TrafficLightLogics('tlls', self)))
351
352        self.add_col(am.IdlistsArrayConf('ids_tlls', self.tlls.value,
353                                         groupnames=['state'],
354                                         name='IDs TLL',
355                                         info='ID list of available Traffic Light Logics (or programs) for the Traffic Light System.',
356                                         ))
357
358        self.add_col(am.IdlistsArrayConf('ids_cons', self.parent.connections,
359                                         groupnames=['state'],
360                                         name='IDs con.',
361                                         info='ID list of controlled connections. These connections corrispond to the elements of the state vector within the program-phases.',
362                                         ))
363
364    # def clear_tlss(self):
365    #    #self.tlls.get_value().clear()
366    #    self.clear()
367
368    def make(self, id_sumo, **kwargs):
369
370        if self.ids_sumo.has_index(id_sumo):
371            # recycle ID from existing
372            id_tls = self.ids_sumo.get_id_from_index(id_sumo)
373        else:
374            # make new TLS
375            id_tls = self.add_row(ids_sumo=id_sumo, ids_tlls=[])
376
377        # make a new TL logic for this traffic light systems
378        id_tll = self.tlls.get_value().make(id_tls, **kwargs)
379        # append new logic to list
380        self.ids_tlls[id_tls].append(id_tll)
381        # print 'TLS.make',id_sumo,id_tls,self.ids_tlls[id_tls]
382        return id_tls
383
384    def clear_tlss(self):
385        """
386        Delete all TLS systems of the network
387        """
388
389        # this will clear the pointer from nodes to a tls
390        # but the node type tls will remain
391        self.parent.nodes.ids_tls.reset()
392        self.parent.connections.are_uncontrolled.reset()
393        self.clear()
394
395    def set_connections(self, id_tls, ids_con):
396        """
397        Set connections, which represent the controlled links of TLD with id_tls
398        Called after connections in ttl file have been parsed.
399        """
400        # print 'set_connections',id_tls,len(ids_con)
401
402        #id_tls = self.ids_sumo.get_id_from_index(id_sumo)
403        self.ids_cons[id_tls] = ids_con
404
405        # check whether the number of connections equals to the number of
406        # dignals in the states
407        #
408        # Attention: they are not equal because there is no connection for
409        # pedestrian crossings! Thos links are under crossins,
410        # but have no link index :(...so just be tolerant
411        #
412        #n_signals = len(ids_con)
413        # print '  ids',self.get_ids(), id_tls in self.get_ids()
414        #tlls = self.tlls.get_value()
415        # print '  self.ids_tlls[id_tls]:',self.ids_tlls[id_tls]
416        # for id_tll in self.ids_tlls[id_tls]:
417        #    prog = tlls.programs[id_tll]
418        #    states = prog.states.get_value()
419        #    #print '  len(state0),n_signals',len(state0),n_signals
420        #    if len(states[0]) != n_signals:
421        #        print 'WARNING: tls %s has inconsistant program. \n  Signals =%d, states=%d'%(self.ids_sumo[id_tls],n_signals,len(states[0]))
422        #        print '  ids_con=',ids_con
423        #        for state in states:
424        #            print '  state',state
425
426    def change_states(self, state_current, state_new):
427        """
428        Change traffic light state is all traffic light programms
429        from state_current to state_new
430        """
431        tlls = self.tlls.get_value()
432        for program in tlls.programs[tlls.get_ids()]:
433            ids_phases = program.get_ids()
434            for id_phase, state in zip(ids_phases, program.states[ids_phases]):
435                program.states[id_phase] = state.replace(state_current, state_new)
436
437    def export_sumoxml(self, filepath=None, encoding='UTF-8'):
438        """
439        Export traffic light systems to SUMO xml file.
440        """
441        # here we export actually the traffic light logics table
442        # and the controlled connections table
443        tlls = self.tlls.get_value()
444        connections = self.parent.connections  # self.ids_cons.get_linktab()
445        lanes = self.parent.lanes
446        edges = self.parent.edges
447
448        # this is the preferred way to specify default filepath
449        if filepath is None:
450            filepath = self.parent.get_rootfilepath()+'.tll.xml'
451
452        print 'export_sumoxml', filepath
453        try:
454            fd = open(filepath, 'w')
455        except:
456            print 'WARNING in export_sumoxml: could not open', filepath
457            return False
458
459        fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
460        xmltag_ttl, xmltag_id, attrconf_id = tlls.xmltag
461        fd.write(xm.begin(xmltag_ttl))
462        indent = 2
463
464        #ids_modes_used = set(self.parent.vtypes.ids_mode[self.ids_vtype.get_value()])
465        ids_tlls = tlls.get_ids()
466        tlls.write_xml(fd, indent=indent,
467                       #xmltag_id = 'id',
468                       #ids = ids_tlls,
469                       #ids_xml = self.ids_sumo[tlls.ids_tls[ids_tlls]],
470                       is_print_begin_end=False,
471                       )
472        # write controlled connections
473        ids_tls = self.get_ids()
474        xmltag_con = 'connection'
475        for ids_con, id_sumo_tls in zip(self.ids_cons[ids_tls], self.ids_sumo[ids_tls]):
476            ids_fromlane = connections.ids_fromlane[ids_con]
477            ids_tolane = connections.ids_tolane[ids_con]
478            inds_fromlane = lanes.indexes[ids_fromlane]
479            inds_tolane = lanes.indexes[ids_tolane]
480            ids_sumo_fromedge = edges.ids_sumo[lanes.ids_edge[ids_fromlane]]
481            ids_sumo_toedge = edges.ids_sumo[lanes.ids_edge[ids_tolane]]
482
483            ind_link = 0
484            for id_sumo_fromedge, id_sumo_toedge, ind_fromlane, ind_tolane in \
485                    zip(ids_sumo_fromedge, ids_sumo_toedge, inds_fromlane, inds_tolane):
486
487                fd.write(xm.start(xmltag_con, indent))
488                fd.write(xm.num('from', id_sumo_fromedge))
489                fd.write(xm.num('to', id_sumo_toedge))
490                fd.write(xm.num('fromLane', ind_fromlane))
491                fd.write(xm.num('toLane', ind_tolane))
492                fd.write(xm.num('tl', id_sumo_tls))
493                fd.write(xm.num('linkIndex', ind_link))
494
495                fd.write(xm.stopit())
496
497                ind_link += 1
498
499        fd.write(xm.end(xmltag_ttl))
500
501
502class Crossings(am.ArrayObjman):
503    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
504    def __init__(self, parent, **kwargs):
505        ident = 'crossings'
506        self._init_objman(ident=ident, parent=parent, name='Crossings',
507                          xmltag=('crossings', 'crossing', ''),
508                          is_plugin=True,
509                          version=0.1,
510                          **kwargs)
511
512        self._init_attributes()
513
514    def _init_attributes(self):
515        net = self.parent
516
517        self.add_col(am.IdsArrayConf('ids_node', net.nodes,
518                                     groupnames=['state'],
519                                     name='ID node',
520                                     info='ID of node where crossings are located.',
521                                     xmltag='node',
522                                     ))
523
524        self.add_col(am.IdlistsArrayConf('ids_edges', net.edges,
525                                         groupnames=['state'],
526                                         name='IDs Edge',
527                                         info='Edge IDs at specific node, where street crossing is possible.',
528                                         xmltag='edges',
529                                         ))
530
531        self.add_col(am.ArrayConf('widths', 4.0,
532                                  dtype=np.float32,
533                                  perm='rw',
534                                  unit='m',
535                                  name='Width',
536                                  info='Crossing width.',
537                                  xmltag='width',
538                                  ))
539
540        self.add_col(am.ArrayConf('are_priority', False,
541                                  dtype=np.bool,
542                                  perm='rw',
543                                  name='Priority',
544                                  info='Whether the pedestrians have priority over the vehicles (automatically set to true at tls-controlled intersections).',
545                                  xmltag='priority',
546                                  ))
547
548        self.add_col(am.ArrayConf('are_discard', False,
549                                  dtype=np.bool,
550                                  perm='rw',
551                                  name='Discard',
552                                  info='Whether the crossing with the given edges shall be discarded.',
553                                  xmltag='discard',
554                                  ))
555
556        if self.get_version() < 0.1:
557            self.init_plugin(True)
558
559    def multimake(self, ids_node=[], **kwargs):
560        n = len(ids_node)
561        return self.add_rows(n=n,
562                             ids_node=ids_node,
563                             **kwargs
564                             )
565
566    def make(self, **kwargs):
567        return self.add_row(ids_node=kwargs['id_node'],
568                            ids_edges=kwargs['ids_edge'],
569                            widths=kwargs.get('width', None),
570                            are_priority=kwargs.get('is_priority', None),
571                            are_discard=kwargs.get('is_discard', None),
572                            )
573
574    def del_element(self, _id):
575        # print 'del_element',id_zone
576        self.del_row(_id)
577
578
579class Connections(am.ArrayObjman):
580    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
581    def __init__(self, parent, **kwargs):
582        ident = 'connections'
583        self._init_objman(ident=ident, parent=parent, name='Connections',
584                          xmltag=('connections', 'connection', None),
585                          **kwargs)
586        self._init_attributes()
587
588    def _init_attributes(self):
589
590        lanes = self.parent.lanes
591        self.add_col(am.IdsArrayConf('ids_fromlane', lanes,
592                                     groupnames=['state'],
593                                     name='ID from-lane',
594                                     info='ID of lane at the beginning of the connection.',
595                                     xmltag='fromLane',
596                                     ))
597
598        self.add_col(am.IdsArrayConf('ids_tolane', lanes,
599                                     name='ID to-lane',
600                                     info='ID of lane at the end of the connection.',
601                                     xmltag='toLane',
602                                     ))
603
604        self.add_col(am.ArrayConf('are_passes', False,
605                                  dtype=np.bool,
606                                  perm='rw',
607                                  name='Pass',
608                                  info=' if set, vehicles which pass this (lane-to-lane) connection will not wait.',
609                                  xmltag='pass',
610                                  ))
611
612        self.add_col(am.ArrayConf('are_keep_clear',  True,
613                                  dtype=np.bool,
614                                  groupnames=['state'],
615                                  perm='rw',
616                                  name='keep clear',
617                                  info='if set to false, vehicles which pass this (lane-to-lane) connection will not worry about blocking the intersection.',
618                                  xmltag='keepClear',
619                                  ))
620
621        self.add_col(am.ArrayConf('positions_cont', 0.0,
622                                  dtype=np.float32,
623                                  perm='rw',
624                                  unit='m',
625                                  name='Cont. Pos.',
626                                  info='if set to 0, no internal junction will be built for this connection. If set to a positive value, an internal junction will be built at this position (in m) from the start of the internal lane for this connection. ',
627                                  xmltag='contPos',
628                                  ))
629
630        self.add_col(am.ArrayConf('are_uncontrolled',  False,
631                                  dtype=np.bool,
632                                  perm='rw',
633                                  name='uncontrolled',
634                                  info='if set to true, This connection will not be TLS-controlled despite its node being controlled.',
635                                  xmltag='uncontrolled',
636                                  ))
637
638    def make(self, **kwargs):
639        return self.add_row(ids_fromlane=kwargs['id_fromlane'],
640                            ids_tolane=kwargs['id_tolane'],
641                            are_passes=kwargs.get('is_passes', None),
642                            are_keep_clear=kwargs.get('is_keep_clear', None),
643                            positions_cont=kwargs.get('position_cont', None),
644                            are_uncontrolled=kwargs.get('is_uncontrolled', None),
645                            )
646
647    def multimake(self, ids_fromlane=[], ids_tolane=[], **kwargs):
648
649        n = len(ids_fromlane)
650        return self.add_rows(n=n,
651                             ids_fromlane=ids_fromlane,
652                             ids_tolane=ids_tolane,
653                             **kwargs
654                             )
655
656    def get_id_from_sumoinfo(self, id_sumo_fromedge, id_sumo_toedge, ind_fromlane, ind_tolane):
657        get_id_lane = self.parent.edges.get_id_lane_from_sumoinfo
658        id_fromlane = get_id_lane(id_sumo_fromedge, ind_fromlane)
659        id_tolane = get_id_lane(id_sumo_toedge, ind_tolane)
660        ids_con = self.select_ids((self.ids_fromlane.value == id_fromlane) & (self.ids_tolane.value == id_tolane))
661        if len(ids_con) == 1:
662            return ids_con[0]
663        else:
664            return -1
665
666    def export_sumoxml(self, filepath, encoding='UTF-8'):
667        try:
668            fd = open(filepath, 'w')
669        except:
670            print 'WARNING in export_sumoxml: could not open', filepath
671            return False
672
673        fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
674        indent = 0
675        self.write_xml(fd, indent)
676
677        fd.close()
678
679    def write_xml(self, fd, indent):
680        # print 'Connections.write_xml'
681
682        xmltag, xmltag_item, attrname_id = self.xmltag
683        #attrsman = self.get_attrsman()
684        #attrsman = self.get_attrsman()
685        #config_fromlane = attrsman.get_config('ids_fromlane')
686        #config_tolane = attrsman.get_config('ids_tolane')
687        colconfigs = self.get_colconfigs(is_all=True)
688        ids_sumoedges = self.parent.edges.ids_sumo
689        ids_laneedge = self.parent.lanes.ids_edge
690        # print '  header'
691        fd.write(xm.start(xmltag, indent))
692        # print '  ', self.parent.get_attrsman().get_config('version').attrname,self.parent.get_attrsman().get_config('version').get_value()
693        #fd.write( self.parent.get_attrsman().get_config('version').write_xml(fd) )
694        self.parent.get_attrsman().get_config('version').write_xml(fd)
695        fd.write(xm.stop())
696
697        for _id in self.get_ids():
698            fd.write(xm.start(xmltag_item, indent+2))
699
700            # print ' make tag and id',_id
701            # fd.write(xm.num(xmltag_id,attrconfig_id[_id]))
702
703            # print ' write columns'
704            for attrconfig in colconfigs:
705                # print '    colconfig',attrconfig.attrname
706                if attrconfig == self.ids_fromlane:
707                    fd.write(xm.num('from', ids_sumoedges[ids_laneedge[self.ids_fromlane[_id]]]))
708                    attrconfig.write_xml(fd, _id)
709
710                elif attrconfig == self.ids_tolane:
711                    fd.write(xm.num('to', ids_sumoedges[ids_laneedge[self.ids_tolane[_id]]]))
712                    attrconfig.write_xml(fd, _id)
713
714                else:
715                    attrconfig.write_xml(fd, _id)
716            fd.write(xm.stopit())
717
718        self.parent.crossings.write_xml(fd, indent=indent+2, is_print_begin_end=False)
719
720        fd.write(xm.end(xmltag, indent))
721
722
723class Lanes(am.ArrayObjman):
724    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
725    def __init__(self, parent, **kwargs):
726        ident = 'lanes'
727        self._init_objman(ident=ident, parent=parent, name='Lanes',
728                          is_plugin=True,
729                          xmltag=('lanes', 'lane', 'indexes'),
730                          version=0.1,
731                          **kwargs)
732
733        self._init_attributes()
734
735    def _init_attributes(self):
736        modes = self.parent.modes
737        edges = self.parent.edges
738
739        self.add_col(am.ArrayConf('indexes', 0,
740                                  dtype=np.int32,
741                                  perm='r',
742                                  name='Lane index',
743                                  info='The enumeration index of the lane (0 is the rightmost lane, <NUMBER_LANES>-1 is the leftmost one).',
744                                  xmltag='index',
745                                  ))
746
747        self.add_col(am.ArrayConf('widths', 3.5,
748                                  dtype=np.float32,
749                                  perm='rw',
750                                  unit='m',
751                                  name='Width',
752                                  info='Lane width.',
753                                  is_plugin=True,
754                                  xmltag='width',
755                                  ))
756
757        self.add_col(am.NumArrayConf('speeds_max', 50.0/3.6,
758                                     dtype=np.float32,
759                                     groupnames=['state'],
760                                     perm='rw',
761                                     name='Max speed',
762                                     unit='m/s',
763                                     info='Maximum speed on lane.',
764                                     xmltag='speed',
765                                     ))
766
767        self.add_col(am.NumArrayConf('offsets_end', 0.0,
768                                     dtype=np.float32,
769                                     groupnames=['state'],
770                                     perm='r',
771                                     name='End offset',
772                                     unit='m',
773                                     info='Move the stop line back from the intersection by the given amount (effectively shortening the lane and locally enlarging the intersection).',
774                                     xmltag='endOffset',
775                                     ))
776
777        self.add_col(am.IdlistsArrayConf('ids_modes_allow', modes,
778                                         name='IDs allowed',
779                                         info='Allowed mode IDs on this lane.',
780                                         xmltag='allow',
781                                         ))
782
783        self.add_col(am.IdlistsArrayConf('ids_modes_disallow', modes,
784                                         name='IDs disallow',
785                                         info='Disallowed mode IDs on this lane.',
786                                         xmltag='disallow',
787                                         ))
788
789        # if self.get_version() < 0.1:
790        #    self.ids_modes_allow.set_value(self.modes_allow.get_value().copy())
791        #    self.ids_modes_disallow.set_value(self.modes_disallow.get_value().copy())
792        #    self.delete('modes_allow')
793        #    self.delete('modes_disallow')
794
795        self.add_col(am.IdsArrayConf('ids_mode', modes,
796                                     groupnames=['state'],
797                                     name='Main mode ID',
798                                     info='ID of main mode of this lane.',
799                                     is_plugin=True,
800                                     ))
801
802        self.add_col(am.IdsArrayConf('ids_edge', edges,
803                                     groupnames=['state'],
804                                     name='ID edge',
805                                     info='ID of edge in which the lane is contained.',
806                                     ))
807
808        self.add_col(am.ListArrayConf('shapes',
809                                      groupnames=['_private'],
810                                      perm='rw',
811                                      name='Shape',
812                                      unit='m',
813                                      info='List of 3D Shape coordinates to describe polyline.',
814                                      is_plugin=True,
815                                      ))
816        self.set_version(0.2)
817        # print 'Lanes._init_attributes ids_modes_allow',self.ids_modes_allow.get_value()
818
819    def get_edges(self):
820        return self.parent.edges
821
822    def get_id_sumo(self, id_lane):
823        return self.parent.edges.ids_sumo[self.ids_edge[id_lane]]+'_%d' % self.indexes[id_lane]
824
825    def get_lengths(self, ids_lane):
826        return self.parent.edges.lengths[self.ids_edge[ids_lane]]
827
828    def multimake(self, indexes=[], **kwargs):
829
830        n = len(indexes)
831        # print 'Lanes.make',kwargs
832        #width = kwargs.get('widths',None)
833        #speed_max = kwargs.get('speed_max',-1)
834        #modes_allow = kwargs.get('modes_allow',[])
835
836        return self.add_rows(n=n,
837                             indexes=indexes,
838                             widths=kwargs['widths'],
839                             speeds_max=kwargs['speeds_max'],
840                             offsets_end=kwargs['offsets_end'],
841                             ids_modes_allow=kwargs['ids_modes_allow'],
842                             ids_modes_disallow=kwargs['ids_modes_disallow'],
843                             ids_mode=kwargs['ids_mode'],
844                             ids_edge=kwargs['ids_edge'],
845                             # shapes = kwargs.get('shapes',[]), # if empty, then computation later from edge shape
846                             )
847
848    def make(self, **kwargs):
849        edges = self.get_edges()
850        id_edge = kwargs['id_edge']
851        index = kwargs['index']
852        # print 'Lanes.make',kwargs
853        width = kwargs.get('width', -1)
854        speed_max = kwargs.get('speed_max', -1)
855        ids_modes_allow = kwargs.get('ids_modes_allow', [])
856
857        is_sidewalk_edge = False
858        is_sidewalk = False
859        if len(ids_modes_allow) > 0:
860            id_mode = ids_modes_allow[0]  # pick first as major mode
861        else:
862            id_mode = -1  # no mode specified
863
864        if index == 0:
865            width_sidewalk_edge = edges.widths_sidewalk[id_edge]
866            is_sidewalk_edge = width_sidewalk_edge > 0
867            is_sidewalk = (MODES['pedestrian'] in ids_modes_allow)  # test for pedestrian sidewalk
868
869        if speed_max < 0:
870            if (index == 0) & is_sidewalk:
871                speed_max = 0.8  # default walk speed
872            else:
873                speed_max = edges.speeds_max[id_edge]
874
875        # print ' is_sidewalk_edge ,is_sidewalk',is_sidewalk_edge ,is_sidewalk
876        if width < 0:
877            width = edges.widths_lanes_default[id_edge]
878
879            if index == 0:
880                if is_sidewalk_edge:  # edge wants sidewalks
881                    width = width_sidewalk_edge
882                elif (not is_sidewalk_edge) & is_sidewalk:  # edge does not want sidewalks, but actually there is a sidewalk
883                    width = 0.9  # default sidewalk width
884                    edges.widths_sidewalk[id_edge] = width
885
886        # if sidewalk, then the edge attribute widths_sidewalk
887        # should be set to actual lane width in case it is less than zero
888        elif index == 0:  # width set for lane 0
889            if (not is_sidewalk_edge) & is_sidewalk:  # edge does not want sidewalks, but actually there is a sidewalk
890                edges.widths_sidewalk[id_edge] = width
891
892        # if index == 0:
893        #      edges.widths_sidewalk[id_edge]= width
894
895        return self.add_row(indexes=index,
896                            widths=width,
897                            speeds_max=speed_max,
898                            offsets_end=kwargs.get('offset_end', None),
899                            ids_modes_allow=ids_modes_allow,
900                            ids_modes_disallow=kwargs.get('ids_modes_disallow', []),
901                            ids_mode=id_mode,
902                            ids_edge=id_edge,
903                            shapes=kwargs.get('shapes', []),  # if empty, then computation later from edge shape
904                            )
905
906    def reshape(self):
907        for id_edge in self.parent.edges.get_ids():
908            self.reshape_edgelanes(id_edge)
909
910    def reshape_edgelanes(self, id_edge):
911        """
912        Recalculate shape of all lanes contained in edge id_edge
913        based on the shape information of this edge.
914        """
915        #
916        #lanes = self.get_lanes()
917        edges = self.parent.edges
918        ids_lane = edges.ids_lanes[id_edge]
919
920        shape = np.array(edges.shapes[id_edge], np.float32)
921
922        # print 'reshape: edgeshape id_edge,ids_lane=',id_edge,ids_lane
923        # print '  shape =',shape
924        n_lanes = len(ids_lane)
925        n_vert = len(shape)
926
927        angles_perb = get_angles_perpendicular(shape)
928
929        dxn = np.cos(angles_perb)
930        dyn = np.sin(angles_perb)
931
932        #laneshapes = np.zeros((n_lanes,n_vert,3), np.float32)
933
934        id_lane = ids_lane[0]
935        # np.ones(n_lanes,np.float32)#lanes.widths[ids_lane]
936        widths = self.widths[ids_lane]
937        widths_tot = np.sum(widths)
938        if edges.types_spread[id_edge] == 1:  # center lane spread
939            widths2 = np.concatenate(([0.0], widths[:-1]))
940            # print '  widths',widths_tot,widths
941            # print '  widths2',widths2
942            displacement = np.cumsum(widths2)
943            displacement = 0.5*(widths_tot)-displacement-0.5*widths
944            # print '  displacement',displacement
945        else:
946            widths2 = np.concatenate(([0.0], widths[:-1]))
947            displacement = np.cumsum(widths2)
948            displacement = displacement[-1]-displacement-0.5*widths+widths[-1]
949
950        for i in range(n_lanes):
951            id_lane = ids_lane[i]
952            # print ' displacement[i] ',displacement[i]#,
953
954            # if 1:#len(self.shapes[id_lane])==0: # make only if not existant
955            laneshape = np.zeros(shape.shape, np.float32)
956            # print ' dx \n',dxn*displacement[i]
957            # print ' dy \n',dyn*displacement[i]
958            laneshape[:, 0] = dxn*displacement[i] + shape[:, 0]
959            laneshape[:, 1] = dyn*displacement[i] + shape[:, 1]
960            laneshape[:, 2] = shape[:, 2]
961            self.shapes[id_lane] = laneshape
962
963        self.shapes.set_modified(True)
964
965    def get_laneindex_allowed(self, ids_lane, id_mode):
966        # print 'get_laneindex_allowed',ids_lane, id_mode
967        # check ignoring mode and give it the first non pedestrian only lane
968        if id_mode == MODES["ignoring"]:
969            ind = 0
970            while is_cont & (ind < len(ids_lane)):
971                id_lane = ids_lane[ind]
972                if len(self.ids_modes_allow[id_lane]) > 0:
973                    if MODES["pedestrian"] not in self.ids_modes_allow[id_lane]:
974                        return ind
975                    else:
976                        ind += 1
977
978                else:
979                    return ind
980            print 'WARNING: ignoring mode has no access on footpath'
981            return -1
982
983            # return len(ids_lane)-1
984
985        is_cont = True
986        ind = 0
987        while is_cont & (ind < len(ids_lane)):
988            # print '  ',ind,len(self.ids_modes_allow[id_lane]),len(self.ids_modes_disallow[id_lane]),id_mode in self.ids_modes_allow[id_lane],id_mode in self.ids_modes_disallow[id_lane]
989            id_lane = ids_lane[ind]
990            if len(self.ids_modes_allow[id_lane]) > 0:
991                if id_mode in self.ids_modes_allow[id_lane]:
992                    return ind
993                else:
994                    ind += 1
995
996            elif len(self.ids_modes_disallow[id_lane]) > 0:
997                if id_mode in self.ids_modes_disallow[id_lane]:
998                    ind += 1
999                else:
1000                    return ind
1001            else:
1002                # no restrictions
1003                return ind
1004
1005        # no unrestricted lane found
1006        return -1  # not allowed on either lane
1007
1008    def add_access(self, id_lane, id_mode):
1009        if len(self.ids_modes_allow[id_lane]) > 0:
1010            # there are restrictions
1011            self.ids_modes_allow[id_lane].append(id_mode)
1012
1013        # make sure id_mode is not disallowed
1014        if id_mode in self.ids_modes_disallow[id_lane]:
1015            self.ids_modes_disallow[id_lane].remove(id_lane)
1016
1017    def get_laneindex_allowed_old(self, ids_lane, id_mode):
1018        is_cont = True
1019        ind = 0
1020        n_lane = len(ids_lane)
1021        while is_cont & (ind < n_lane):
1022            id_lane = ids_lane[ind]
1023            if len(self.ids_modes_allow[id_lane]) > 0:
1024                if id_mode in self.ids_modes_allow[id_lane]:
1025                    return ind
1026                else:
1027                    ind += 1
1028
1029            elif len(self.ids_modes_disallow[id_lane]) > 0:
1030                if id_mode in self.ids_modes_disallow[id_lane]:
1031                    ind += 1
1032                else:
1033                    return ind
1034            else:
1035                # no restrictions
1036                return ind
1037
1038        # no unrestricted lane found
1039        return -1  # not allowed on either lane
1040
1041    def get_accesslevel(self, ids_lane, id_mode):
1042        """
1043        Returns access level of mode on lanes ids_lane:
1044            -1 = No access
1045            0 = all modes can access
1046            1 = mode can access with a restricted number of other modes
1047            2 = exclusive access for id_mode
1048        """
1049        # print 'get_accesslevel',ids_lane
1050        is_mode_only = False
1051        is_mode_mixed = False
1052        is_modes_all = False
1053        #is_blocked = False
1054        # print '  ids_modes_allow',self.ids_modes_allow.get_value()
1055        for id_lane, ids_modes_allow, ids_modes_disallow in zip(ids_lane, self.ids_modes_allow[ids_lane], self.ids_modes_disallow[ids_lane]):
1056            # print '  ids_modes_allow',ids_modes_allow,'ids_modes_disallow',ids_modes_disallow,'id_lane',id_lane
1057            if ids_modes_allow is None:
1058                ids_modes_allow = []
1059            if ids_modes_disallow is None:
1060                ids_modes_disallow = []
1061
1062            n_allow = len(ids_modes_allow)
1063            #n_disallow = len(ids_modes_disallow)
1064
1065            if n_allow == 0:
1066                is_mode_only = False
1067                is_modes_all = True
1068                is_blocked = id_mode in ids_modes_disallow
1069
1070            elif n_allow == 1:
1071                # if id_mode == ids_modes_allow[0]:
1072                #    is_mode_only = True
1073                # else:
1074                #    is_blocked = True
1075                # break
1076                is_mode_only = id_mode == ids_modes_allow[0]
1077                is_blocked = (id_mode in ids_modes_disallow) | (not(is_mode_only))
1078
1079            else:
1080                is_mode_only = False
1081                #is_blocked &=  id_mode in ids_modes_disallow
1082                is_blocked = (id_mode not in ids_modes_allow) | (id_mode in ids_modes_disallow)
1083
1084            # print '  is_blocked',is_blocked,'is_modes_all',is_modes_all,'is_mode_only',is_mode_only
1085            if not is_blocked:
1086                # means access has been found
1087                break
1088
1089        # print '  Final: is_blocked',is_blocked,'is_modes_all',is_modes_all,'is_mode_only',is_mode_only
1090        if is_blocked:  # & (not is_mode_only)&(not is_modes_all):
1091            a = -1
1092            # print '  accesslevel=',a
1093            return a
1094            # return -1
1095
1096        if is_mode_only:
1097            a = 2
1098            # return 2
1099
1100        elif is_modes_all:
1101            a = 0
1102            # return 0
1103
1104        else:
1105            a = 1
1106            # return 1
1107
1108        # print '  accesslevel=',a
1109        return a
1110
1111    def get_coord_from_pos(self, id_lane, pos):
1112        return get_coord_on_polyline_from_pos(self.shapes[id_lane], pos)
1113
1114    def get_sumoinfo_from_id_lane(self, id_lane):
1115        id_sumo_edge = self.parent.edges.ids_sumo[self.ids_edge[id_lane]]
1116        return id_sumo_edge+'_'+str(self.indexes[id_lane])
1117
1118
1119class Roundabouts(am.ArrayObjman):
1120    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
1121    def __init__(self, parent, edges, nodes, **kwargs):
1122        ident = 'roundabouts'
1123        self._init_objman(ident=ident, parent=parent,
1124                          name='Roundabouts',
1125                          xmltag=('roundabouts', 'roundabout', ''),
1126                          **kwargs)
1127
1128        self.add_col(am.IdlistsArrayConf('ids_edges', edges,
1129                                         groupnames=['state'],
1130                                         name='IDs edges',
1131                                         info='List with edges IDs.',
1132                                         xmltag='edges',
1133                                         ))
1134
1135        self.add_col(am.IdlistsArrayConf('ids_nodes', nodes,
1136                                         groupnames=['state'],
1137                                         name='IDs Nodes',
1138                                         info='List with node IDs.',
1139                                         xmltag='nodes',
1140                                         ))
1141
1142    def multimake(self, ids_nodes=[], **kwargs):
1143        n = len(ids_nodes)
1144        return self.add_rows(n=n,
1145                             ids_nodes=ids_nodes, **kwargs
1146                             )
1147
1148    def make(self, **kwargs):
1149        return self.add_row(ids_nodes=kwargs['ids_node'],
1150                            ids_edges=kwargs['ids_edge'],
1151                            )
1152
1153
1154class Edges(am.ArrayObjman):
1155    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
1156    def __init__(self, parent, **kwargs):
1157        ident = 'edges'
1158        self._init_objman(ident=ident, parent=parent,
1159                          name='Edges',
1160                          xmltag=('edges', 'edge', 'ids_sumo'),
1161                          version=0.2,
1162                          **kwargs)
1163
1164        self._init_attributes()
1165        self._init_constants()
1166
1167    def _init_attributes(self):
1168
1169        self.add_col(SumoIdsConf('Edge'))
1170
1171        self.add_col(am.ArrayConf('types', '',
1172                                  dtype=np.object,
1173                                  perm='rw',
1174                                  name='Type',
1175                                  info='Edge reference OSM type.',
1176                                  xmltag='type',  # should not be exported?
1177                                  ))
1178
1179        self.add_col(am.ArrayConf('nums_lanes', 1,
1180                                  dtype=np.int32,
1181                                  perm='r',
1182                                  name='# of lanes',
1183                                  info='Number of lanes.',
1184                                  xmltag='numLanes',
1185                                  ))
1186
1187        self.add_col(am.NumArrayConf('speeds_max', 50.0/3.6,
1188                                     dtype=np.float32,
1189                                     groupnames=['state'],
1190                                     perm='rw',
1191                                     name='Max speed',
1192                                     unit='m/s',
1193                                     info='Maximum speed on edge.',
1194                                     xmltag='speed',
1195                                     ))
1196
1197        self.add_col(am.ArrayConf('priorities', 1,
1198                                  dtype=np.int32,
1199                                  perm='rw',
1200                                  name='Priority',
1201                                  info='Road priority, 1 is lowest (local access road or footpath), 13 is highest (national motorway).',
1202                                  xmltag='priority',
1203                                  ))
1204
1205        self.add_col(am.NumArrayConf('lengths', 0.0,
1206                                     dtype=np.float32,
1207                                     groupnames=['state'],
1208                                     perm='r',
1209                                     name='Length',
1210                                     unit='m',
1211                                     info='Edge length.',
1212                                     xmltag='length ',
1213                                     ))
1214        if self.get_version() < 0.3:
1215            self.lengths.set_xmltag('length')
1216            # self.lengths.set_xmltag(None)
1217
1218        self.add_col(am.NumArrayConf('widths', 0.0,
1219                                     dtype=np.float32,
1220                                     groupnames=['state'],
1221                                     perm='r',
1222                                     name='Width',
1223                                     unit='m',
1224                                     info='Edge width.',
1225                                     is_plugin=True,
1226                                     #xmltag = 'width',
1227                                     ))
1228
1229        self.add_col(am.ListArrayConf('shapes',
1230                                      groupnames=['_private'],
1231                                      perm='rw',
1232                                      name='Shape',
1233                                      unit='m',
1234                                      info='Edge shape as list of 3D shape coordinates representing a polyline.',
1235                                      is_plugin=True,
1236                                      xmltag='shape',
1237                                      ))
1238
1239        self.add_col(am.ArrayConf('types_spread', 0,
1240                                  choices={
1241                                      "right": 0,
1242                                      "center": 1,
1243                                  },
1244                                  dtype=np.int32,
1245                                  perm='rw',
1246                                  name='Spread type',
1247                                  info='Determines how the lanes are spread with respect to main link coordinates.',
1248                                  xmltag='spreadType',
1249                                  ))
1250
1251        self.add_col(am.ArrayConf('names', '',
1252                                  dtype=np.object,
1253                                  perm='rw',
1254                                  name='Name',
1255                                  info='Road name, for visualization only.',
1256                                  xmltag='name',
1257                                  ))
1258
1259        self.add_col(am.NumArrayConf('offsets_end', 0.0,
1260                                     dtype=np.float32,
1261                                     groupnames=['state'],
1262                                     perm='r',
1263                                     name='End offset',
1264                                     unit='m',
1265                                     info='Move the stop line back from the intersection by the given amount (effectively shortening the edge and locally enlarging the intersection).',
1266                                     xmltag='endOffset',
1267                                     ))
1268
1269        self.add_col(am.NumArrayConf('widths_lanes_default', 3.5,
1270                                     dtype=np.float32,
1271                                     groupnames=['state'],
1272                                     perm='rw',
1273                                     name='Default lane width',
1274                                     unit='m',
1275                                     info='Default lane width for all lanes of this edge in meters (used for visualization).',
1276                                     #xmltag = '',
1277                                     ))
1278
1279        # move this to lane in future versions
1280        self.add_col(am.NumArrayConf('widths_sidewalk', -1.0,
1281                                     dtype=np.float32,
1282                                     groupnames=['state'],
1283                                     perm='rw',
1284                                     name='Sidewalk width',
1285                                     unit='m',
1286                                     info='Adds a sidewalk with the given width (defaults to -1 which adds nothing).',
1287                                     #xmltag = 'sidewalkWidth',
1288                                     ))
1289
1290        self.set_version(0.2)
1291
1292    def _init_constants(self):
1293        self._segvertices = None
1294        self._edgeinds = None
1295        self._seginds = None
1296        self._segvertexinds = None
1297        self.do_not_save_attrs(['_segvertices', '_edgeinds', '_seginds', '_segvertexinds'])
1298
1299    def set_nodes(self, nodes):
1300        # set ref to nodes table, once initialized
1301        self.add_col(am.IdsArrayConf('ids_fromnode', nodes,
1302                                     groupnames=['state'],
1303                                     name='ID from-node',
1304                                     info='ID of node at the beginning of the edge.',
1305                                     xmltag='from',
1306                                     ))
1307
1308        self.add_col(am.IdsArrayConf('ids_tonode', nodes,
1309                                     groupnames=['state'],
1310                                     name='ID to-node',
1311                                     info='ID of node at the end of the edge.',
1312                                     xmltag='to',
1313                                     ))
1314
1315    def set_lanes(self, lanes):
1316        self.add_col(am.IdlistsArrayConf('ids_lanes', lanes,
1317                                         groupnames=['state'],
1318                                         name='IDs Lanes',
1319                                         info='List with IDs of lanes.',
1320                                         xmltag='lanes',
1321                                         is_xml_include_tab=True,
1322                                         ))
1323
1324    def get_outgoing(self, id_edge):
1325        # print 'get_outgoing',id_edge,self.ids_tonode[id_edge],self.parent.nodes.ids_outgoing[self.ids_tonode[id_edge]]
1326        ids_edges = self.parent.nodes.ids_outgoing[self.ids_tonode[id_edge]]
1327        if ids_edges is None:  # dead end
1328            return []
1329        else:
1330            return ids_edges
1331
1332    def get_incoming(self, id_edge):
1333        # TODO: would be good to have [] as default instead of None!!
1334        ids_edges = self.parent.nodes.ids_incoming[self.ids_fromnode[id_edge]]
1335        if ids_edges is None:  # dead end
1336            return []
1337        else:
1338            return ids_edges
1339
1340    def get_lanes(self):
1341        return self.parent.lanes
1342
1343    def get_id_lane_from_sumoinfo_check(self, id_sumo_edge,  ind_lane):
1344        if self.ids_sumo.has_index(id_sumo_edge):
1345            id_edge = self.ids_sumo.get_id_from_index(id_sumo_edge)
1346            if ind_lane < self.nums_lanes[id_edge]:
1347                return self.ids_lanes[id_edge][ind_lane]
1348        return -1
1349
1350    def get_id_lane_from_sumoinfo(self, id_sumo_edge,  ind_lane):
1351        id_edge = self.ids_sumo.get_id_from_index(id_sumo_edge)
1352        return self.ids_lanes[id_edge][ind_lane]
1353
1354    def get_sumoinfo_from_id_lane(self, id_lane):
1355        return self.parent.lanes.get_sumoinfo_from_id_lane(id_lane)
1356
1357    def is_oneway(self, id_edge):
1358        ids_incoming = self.parent.nodes.ids_incoming[self.ids_fromnode[id_edge]]
1359        ids_outgoing = self.parent.nodes.ids_outgoing[self.ids_tonode[id_edge]]
1360        if (ids_incoming is None) | (ids_outgoing is None):
1361            return True
1362        else:
1363            return set(ids_incoming).isdisjoint(ids_outgoing)
1364
1365    def has_sidewalk(self, id_edge):
1366        return MODES["pedestrian"] in self.parent.lanes.ids_modes_allow[self.ids_lanes[id_edge][0]]
1367
1368    def get_fstar(self, is_return_lists=False, is_return_arrays=False,
1369                  is_ignor_connections=False):
1370        """
1371        Returns the forward star graph of the network as dictionary:
1372            fstar[id_fromedge] = set([id_toedge1, id_toedge2,...])
1373
1374            if is_return_lists = True then a list of edges is the value
1375            of fstar
1376
1377            if is_return_arrays = True then a numpy array of edges is the value
1378            of fstar
1379
1380            if is_ignor_connections = True then all possible successive edges
1381            are considered, disregarding the actual connections
1382
1383        """
1384        #ids_edge = self.get_ids()
1385        #fstar = np.array(np.zeros(np.max(ids_edge)+1, np.obj))
1386        fstar = {}
1387        if is_ignor_connections:
1388            # here we ignore connections and look at the
1389            # outgoing edges of node database
1390            ids_outgoing = self.parent.nodes.ids_outgoing
1391            ids = self.get_ids()
1392            for id_edge, id_tonode in zip(ids, self.ids_tonode[ids]):
1393                ids_edge_outgoing = ids_outgoing[id_tonode]
1394                if ids_edge_outgoing is not None:
1395                    fstar[id_edge] = set(ids_edge_outgoing)
1396                else:
1397                    fstar[id_edge] = set()
1398        else:
1399            # here we check actual connections
1400            # this is important if correct turns are desired
1401            # for exampole in car routing
1402            connections = self.parent.connections
1403            lanes = self.parent.lanes
1404            inds_con = connections.get_inds()
1405            ids_fromedge = lanes.ids_edge[connections.ids_fromlane.get_value()[inds_con]]
1406            ids_toedge = lanes.ids_edge[connections.ids_tolane.get_value()[inds_con]]
1407
1408            for id_edge in self.get_ids():
1409                fstar[id_edge] = set()
1410
1411            for id_fromedge, id_toedge in zip(ids_fromedge, ids_toedge):
1412                fstar[id_fromedge].add(id_toedge)
1413
1414        if is_return_lists | is_return_arrays:
1415            for id_edge in self.get_ids():
1416                ids_toedges = list(fstar[id_edge])
1417                if is_return_arrays:
1418                    fstar[id_edge] = np.array(ids_toedges, dtype=np.int32)
1419                else:
1420                    fstar[id_edge] = ids_toedges
1421        return fstar
1422
1423    def get_bstar(self, is_return_lists=False, is_return_arrays=False,
1424                  is_ignor_connections=False):
1425        """
1426        Returns the backward star graph of the network as dictionary:
1427            fstar[id_fromedge] = set([id_fromedge1, id_fromedge2,...])
1428
1429            if is_return_lists = True then a list of edges is the value
1430            of bstar
1431
1432            if is_return_arrays = True then a numpy array of edges is the value
1433            of bstar
1434
1435            if is_ignor_connections = True then all possible preceding edges
1436            are considered, disregarding the actual connections
1437
1438        """
1439        #ids_edge = self.get_ids()
1440        #fstar = np.array(np.zeros(np.max(ids_edge)+1, np.obj))
1441        bstar = {}
1442        if is_ignor_connections:
1443            # here we ignore connections and look at the
1444            # outgoing edges of node database
1445            ids_incoming = self.parent.nodes.ids_incoming
1446            ids = self.get_ids()
1447            for id_edge, id_fromnode in zip(ids, self.ids_fromnode[ids]):
1448                ids_edge_incoming = ids_incoming[id_fromnode]
1449                if ids_edge_incoming is not None:
1450                    bstar[id_edge] = set(ids_edge_incoming)
1451                else:
1452                    bstar[id_edge] = set()
1453        else:
1454            # here we check actual connections
1455            # this is important if correct turns are desired
1456            # for exampole in car routing
1457            connections = self.parent.connections
1458            lanes = self.parent.lanes
1459            inds_con = connections.get_inds()
1460            ids_fromedge = lanes.ids_edge[connections.ids_fromlane.get_value()[inds_con]]
1461            ids_toedge = lanes.ids_edge[connections.ids_tolane.get_value()[inds_con]]
1462
1463            for id_edge in self.get_ids():
1464                bstar[id_edge] = set()
1465
1466            for id_fromedge, id_toedge in zip(ids_fromedge, ids_toedge):
1467                bstar[id_toedge].add(id_fromedge)
1468
1469        if is_return_lists | is_return_arrays:
1470            for id_edge in self.get_ids():
1471                ids_fromedges = list(bstar[id_edge])
1472                if is_return_arrays:
1473                    bstar[id_edge] = np.array(ids_fromedges, dtype=np.int32)
1474                else:
1475                    bstar[id_edge] = ids_fromedges
1476        return bstar
1477
1478    def get_accesslevel(self, id_edge, id_mode):
1479        """
1480        Returns access level of mode on edge id_edge:
1481            -1 = No access
1482            0 = all modes can access
1483            1 = mode can access with a restricted number of other modes
1484            2 = exclusive access for id_mode
1485        """
1486        # print 'get_accesslevel',id_edge,self.ids_sumo[id_edge]
1487        return self.parent.lanes.get_accesslevel(self.ids_lanes[id_edge], id_mode)
1488
1489    def get_accesslevels(self, id_mode):
1490        """
1491        The returned array represents the access levels that corresponds to
1492        edge IDs.
1493
1494        Access levels of mode on edge id_edge:
1495            -1 = No access
1496            0 = all modes can access
1497            1 = mode can access with a restricted number of other modes
1498            2 = exclusive access for id_mode
1499        """
1500        get_accesslevel = self.parent.lanes.get_accesslevel
1501        ids_edge = self.get_ids()
1502        accesslevels = np.zeros(np.max(ids_edge)+1, np.int8)
1503        for id_edge, ids_lane in zip(ids_edge, self.ids_lanes[ids_edge]):
1504            accesslevels[id_edge] = get_accesslevel(ids_lane, id_mode)
1505        return accesslevels
1506
1507    def get_distances(self, id_mode=0, is_check_lanes=False):
1508        """
1509        Returns distances for all edges.
1510        The returned array represents the distance that corresponds to
1511        edge IDs.
1512
1513        If is_check_lanes is True, then the lane speeds are considered where
1514        the respective mode is allowed.
1515
1516        If not allowed on a particular edge,
1517        then the respective edge distance is negative.
1518        """
1519        # print 'get_distances id_mode,is_check_lanes,speed_max',id_mode,is_check_lanes,speed_max
1520        ids_edge = self.get_ids()
1521        dists = np.zeros(np.max(ids_edge)+1, np.float32)
1522        #speeds = self.speeds_max[ids_edge]
1523
1524        # if speed_max is not None:
1525        #    speeds = np.clip(speeds, 0.0, speed_max)
1526        #
1527        # elif id_mode is not None:
1528        #    # limit allowed speeds with max speeds of mode
1529        #    speeds = np.clip(speeds, 0.0, self.parent.modes.speeds_max[id_mode])
1530
1531        radii = self.ids_fromnode.get_linktab().radii
1532        dists[ids_edge] = radii[self.ids_fromnode[ids_edge]] + self.lengths[ids_edge] + radii[self.ids_tonode[ids_edge]]
1533        ids_lanes = self.ids_lanes
1534        if is_check_lanes & (id_mode > 0):  # mode 0 can pass everywhere
1535            get_laneindex_allowed = self.parent.lanes.get_laneindex_allowed
1536            has_noaccess = -1
1537            for id_edge in ids_edge:
1538                if get_laneindex_allowed(ids_lanes[id_edge], id_mode) == has_noaccess:
1539                    dists[id_edge] = has_noaccess
1540
1541                #ind = get_laneindex_allowed(ids_lanes[id_edge], id_mode)
1542                # print '  check id_edge, ind',id_edge, ind
1543                # if ind<0:
1544                #    dists[id_edge] = ind # =-1
1545
1546        return dists
1547
1548    def get_times(self, id_mode=0, is_check_lanes=False, speed_max=None,
1549                  modeconst_excl=0.0, modeconst_mix=0.0, ):
1550        """
1551        Returns freeflow travel times for all edges..radii
1552        The returned array represents the travel time that corresponds to
1553        edge IDs.
1554
1555        If is_check_lanes is True, then the lane speeds are considered where
1556        the respective mode is allowed.
1557
1558        If not allowed on a particular edge,
1559        then the respective edge travel time is negative.
1560
1561        modeconst_excl and modeconst_mix are constants added to the
1562        time if the respective edge provides exclusive or reserver mixed
1563        access for the specifird mode
1564
1565        """
1566        # print 'get_times id_mode,is_check_lanes,speed_max',id_mode,is_check_lanes,speed_max
1567        ids_edge = self.get_ids()
1568        times = np.zeros(np.max(ids_edge)+1, np.float32)
1569        speeds = self.speeds_max[ids_edge]
1570
1571        if speed_max is not None:
1572            speeds = np.clip(speeds, 0.0, speed_max)
1573
1574        elif id_mode is not None:
1575            # limit allowed speeds with max speeds of mode
1576            speeds = np.clip(speeds, 0.0, self.parent.modes.speeds_max[id_mode])
1577
1578        times[ids_edge] = self.lengths[ids_edge]/speeds
1579        ids_lanes = self.ids_lanes
1580        if is_check_lanes & (id_mode > 0):  # mode 0 can pass everywhere
1581            #get_laneindex_allowed = self.parent.lanes.get_laneindex_allowed
1582            get_accesslevel = self.parent.lanes.get_accesslevel
1583            invalid = -1
1584            #ids_edge = self.get_ids()
1585            #accesslevels = np.zeros(np.max(ids_edge)+1, np.int8)
1586            for id_edge, ids_lane in zip(ids_edge, self.ids_lanes[ids_edge]):
1587                #accesslevels[id_edge] = get_accesslevel(ids_lane, id_mode)
1588                accesslevel = get_accesslevel(ids_lane, id_mode)
1589                if accesslevel == invalid:
1590                    times[id_edge] = invalid
1591                elif accesslevel == 2:
1592                    times[id_edge] = max(times[id_edge] + modeconst_excl, 0)
1593                elif accesslevel == 1:
1594                    times[id_edge] = max(times[id_edge] + modeconst_mix, 0)
1595                    # here we could multiply with a factor
1596                #ind = get_laneindex_allowed(ids_lanes[id_edge], id_mode)
1597                # print '  check id_edge, ind',id_edge, ind
1598                # if ind<0:
1599                #    # making times negative will prevent the router to use
1600                #    # this edge
1601                #    times[id_edge] = ind # =-1
1602
1603        return times
1604
1605    def select_accessible_mode(self, id_mode):
1606        """
1607        Returns an array with all allowed edge ids for this mode
1608        and an array with the corrisponding lane index
1609        """
1610        get_laneindex_allowed = self.parent.lanes.get_laneindex_allowed
1611        ids_lanes = self.ids_lanes
1612        ids_edges = self.get_ids()
1613        are_allowed = np.zeros(len(ids_edges), dtype=np.bool)
1614        inds_lane = np.zeros(len(ids_edges), dtype=np.int32)
1615        for i, id_edge in zip(xrange(len(ids_edges)), ids_edges):
1616            ind_lane = get_laneindex_allowed(ids_lanes[id_edge], id_mode)
1617            are_allowed[i] = ind_lane >= 0
1618            inds_lane[i] = ind_lane
1619        return ids_edges[are_allowed], inds_lane[are_allowed]
1620
1621    def get_laneindex_allowed(self, id_edge, id_mode):
1622        """
1623        Returns first lane index of edge id_edge on which id_mode
1624        is allowed.
1625        -1 means not allowed on edge
1626        """
1627        return self.parent.lanes.get_laneindex_allowed(self.ids_lanes[id_edge], id_mode)
1628
1629    def get_laneid_allowed(self, id_edge, id_mode):
1630        """
1631        Returns first lane ID of edge id_edge on which id_mode
1632        is allowed.
1633        -1 means not allowed on edge
1634        """
1635        ids_lane = self.ids_lanes[id_edge]
1636        laneind = self.parent.lanes.get_laneindex_allowed(ids_lane, id_mode)
1637        if laneind == -1:
1638            return -1
1639        else:
1640            return ids_lane[laneind]
1641
1642    def multimake(self, ids_sumo=[], **kwargs):
1643        # fixing of insufficient shape data in edge reader
1644        return self.add_rows(n=len(ids_sumo), ids_sumo=ids_sumo,  **kwargs)
1645
1646    def add_reverse(self, id_edge):
1647        self.types_spread[id_edge] = self.types_spread.choices["right"]
1648
1649        id_edge_reverse = self.add_row(ids_sumo='-'+self.ids_sumo[id_edge],
1650                                       ids_fromnode=self.ids_tonode[id_edge],
1651                                       ids_tonode=self.ids_fromnode[id_edge],
1652                                       types=self.types[id_edge],
1653                                       nums_lanes=self.num_lanes[id_edge],
1654                                       speeds_max=self.speed_max[id_edge],
1655                                       priorities=self.priority[id_edge],
1656                                       #lengths = length,
1657                                       shapes=self.shapes[id_edge][::-1],
1658                                       types_spread=self.types_spread[id_edge],
1659                                       names=self.names[id_edge],
1660                                       offsets_end=self.offsets_end[id_edge],
1661                                       widths_lanes_default=self.widths_lanes_default[id_edge],
1662                                       widths_sidewalk=self.widths_sidewalk[id_edge],
1663                                       )
1664        # TODO: add lanes and connections
1665        return id_edge_reverse
1666
1667    def make(self, id_fromnode=0,
1668             id_tonode=0,
1669             id_sumo='',
1670             type_edge='',
1671             num_lanes=1,
1672             speed_max=50.0/3.6,
1673             priority=1,
1674             #length = 0.0,
1675             shape=[],
1676             type_spread='right',
1677             name='',
1678             offset_end=0.0,
1679             width_lanes_default=None,
1680             width_sidewalk=-1,
1681             ):
1682
1683        if len(shape) < 2:  # insufficient shape data
1684            #shape = np.array([ nodes.coords[id_fromnode], nodes.coords[id_tonode] ], np.float32)
1685            # shape should be a list of np array coords
1686            # ATTENTIOn: we need to copy here, otherwise the reference
1687            # to node coordinates will be kept!!
1688            coords = self.ids_tonode.get_linktab().coords
1689            shape = [1.0*coords[id_fromnode], 1.0*coords[id_tonode]]
1690
1691        # print 'Edges.make'
1692        # print '  shape',shape,type(shape)
1693
1694        return self.add_row(ids_sumo=id_sumo,
1695                            ids_fromnode=id_fromnode,
1696                            ids_tonode=id_tonode,
1697                            types=type_edge,
1698                            nums_lanes=num_lanes,
1699                            speeds_max=speed_max,
1700                            priorities=priority,
1701                            #lengths = length,
1702                            shapes=shape,
1703                            types_spread=self.types_spread.choices[type_spread],
1704                            names=name,
1705                            offsets_end=offset_end,
1706                            widths_lanes_default=width_lanes_default,
1707                            widths_sidewalk=width_sidewalk,
1708                            )
1709
1710    def make_segment_edge_map(self, ids=None, is_laneshapes=True):
1711        """
1712        Generates a vertex matrix with line segments of all edges
1713        and a map that maps each line segment to edge index.
1714        """
1715        # TODO: _seginds not correctly constructed for given ids
1716
1717        # here we can make some selection on edge inds
1718        if ids is None:
1719            inds = self.get_inds()
1720        else:
1721            inds = self.get_inds(ids)
1722        print 'make_linevertices', len(inds)
1723
1724        linevertices = np.zeros((0, 2, 3), np.float32)
1725        vertexinds = np.zeros((0, 2), np.int32)
1726        polyinds = []
1727
1728        lineinds = []
1729        #linecolors = []
1730        #linecolors_highl = []
1731        linebeginstyles = []
1732        lineendstyles = []
1733
1734        i = 0
1735        ind_line = 0
1736
1737        if is_laneshapes:
1738            ids_lanes = self.ids_lanes.get_value()
1739            laneshapes = self.parent.lanes.shapes
1740        else:
1741            polylines = self.shapes.get_value()  # [inds]
1742
1743        #polylines = self.shapes[inds]
1744        # print '  len(polylines)',len(polylines)
1745        for ind in inds:
1746
1747            if is_laneshapes:
1748                polyline = laneshapes[ids_lanes[ind][0]]
1749            else:
1750                polyline = polylines[ind]
1751
1752            n_seg = len(polyline)
1753            # print '  =======',n_seg#,polyline
1754
1755            if n_seg > 1:
1756                polyvinds = range(n_seg)
1757                # print '  polyvinds\n',polyvinds
1758                vi = np.zeros((2*n_seg-2), np.int32)
1759                vi[0] = polyvinds[0]
1760                vi[-1] = polyvinds[-1]
1761
1762                # Important type conversion!!
1763                v = np.zeros((2*n_seg-2, 3), np.float32)
1764                v[0] = polyline[0]
1765                v[-1] = polyline[-1]
1766                if len(v) > 2:
1767
1768                    # print 'v[1:-1]',v[1:-1]
1769                    # print 'v=\n',v
1770                    #m = np.repeat(polyline[1:-1],2,0)
1771                    # print 'm\n',m,m.shape,m.dtype
1772                    #v[1:-1] = m
1773                    v[1:-1] = np.repeat(polyline[1:-1], 2, 0)
1774                    vi[1:-1] = np.repeat(polyvinds[1:-1], 2)
1775                #vadd = v.reshape((-1,2,3))
1776                # print '  v\n',v
1777                # print '  vi\n',vi
1778
1779                n_lines = len(v)/2
1780                # print '  v\n',v
1781                polyinds += n_lines*[ind]
1782                lineinds.append(np.arange(ind_line, ind_line+n_lines))
1783                ind_line += n_lines
1784                # print '  polyinds\n',polyinds,n_lines
1785                #linecolors += n_lines*[colors[ind]]
1786                #linecolors_highl += n_lines*[colors_highl[ind]]
1787
1788                # print '  linebeginstyle',linebeginstyle,beginstyles[ind]
1789
1790            else:
1791                # empty polygon treatment
1792                v = np.zeros((0, 3), np.float32)
1793                vi = np.zeros((0), np.int32)
1794
1795            linevertices = np.concatenate((linevertices, v.reshape((-1, 2, 3))))
1796            vertexinds = np.concatenate((vertexinds, vi.reshape((-1, 2))))
1797            # print '  linevertex\n',linevertices
1798            i += 1
1799        self._segvertices = linevertices
1800
1801        self._edgeinds = np.array(polyinds, np.int32)
1802        self._seginds = lineinds
1803        self._segvertexinds = np.array(vertexinds, np.int32)
1804
1805    def get_dist_point_to_edge(self, p, id_edge,
1806                               is_detect_initial=False,
1807                               is_detect_final=False,
1808                               is_return_segment=False):
1809        """
1810        Returns eucledian distance from a point p to a given edge.
1811        As a second argument it returns the coordinates of the
1812        line segment (x1,y1,x2,y2) which is closest to the point.
1813        """
1814        inds_seg = self.get_inds_seg_from_id_edge(id_edge)
1815        vertices = self._segvertices
1816        x1 = vertices[inds_seg, 0, 0]
1817        y1 = vertices[inds_seg, 0, 1]
1818
1819        x2 = vertices[inds_seg, 1, 0]
1820        y2 = vertices[inds_seg, 1, 1]
1821
1822        dists2 = get_dist_point_to_segs(p[0:2], x1, y1, x2, y2,
1823                                        is_ending=True,
1824                                        is_detect_initial=is_detect_initial,
1825                                        is_detect_final=is_detect_final
1826                                        )
1827        if is_detect_final | is_detect_initial:
1828            are_finals = np.isnan(dists2)
1829            # print '  dists2',dists2
1830            # print '  are_finals',are_finals
1831            if np.all(are_finals):  # point outside all segments of edge
1832                if is_return_segment:
1833                    return np.nan, [np.nan, np.nan, np.nan, np.nan]
1834                else:
1835                    return np.nan
1836            else:
1837                dists2[are_finals] = np.inf
1838
1839        ind_min = np.argmin(dists2)
1840        if is_return_segment:
1841            return np.sqrt(dists2[ind_min]), (x1[ind_min], y1[ind_min], x2[ind_min], y2[ind_min])
1842        else:
1843            return np.sqrt(dists2[ind_min])
1844
1845    def get_closest_edge(self, p, is_get2=False):
1846        """
1847        Returns edge id which is closest to point p.
1848        Requires execution of make_segment_edge_map
1849        """
1850        # print 'get_closest_edge',p
1851        if len(self) == 0:
1852            return np.array([], np.int)
1853
1854        if self._segvertices is None:
1855            self.make_segment_edge_map()
1856
1857        vertices = self._segvertices
1858        x1 = vertices[:, 0, 0]
1859        y1 = vertices[:, 0, 1]
1860
1861        x2 = vertices[:, 1, 0]
1862        y2 = vertices[:, 1, 1]
1863
1864        # print '  x1', x1
1865        # print '  x2', x2
1866        #halfwidths = 0.5*self.get_widths_array()[self._polyinds]
1867        d2 = get_dist_point_to_segs(p[0:2], x1, y1, x2, y2, is_ending=True)
1868        # print '  min(d2)=',np.min(d2),'argmin=',np.argmin(d2),self.get_ids(self._edgeinds[np.argmin(d2)])
1869        if not is_get2:
1870            return self.get_ids(self._edgeinds[np.argmin(d2)])
1871        else:
1872            # return 2 best matches
1873            ind1 = np.argmin(d2)
1874            id_edge1 = self.get_ids(self._edgeinds[ind1])
1875            d2[ind1] = np.inf
1876            id_edge2 = self.get_ids(self._edgeinds[np.argmin(d2)])
1877            return [id_edge1, id_edge2]
1878
1879    def get_ids_edge_from_inds_seg(self, inds_seg):
1880        return self.get_ids(self._edgeinds[inds_seg])
1881
1882    def get_inds_seg_from_id_edge(self, id_edge):
1883        # print 'get_inds_seg_from_id_edge id_edge, ind_edge',id_edge,self.get_ind(id_edge)
1884        return self._seginds[self.get_ind(id_edge)]
1885
1886    def get_segvertices_xy(self):
1887        if self._segvertices is None:
1888            self.make_segment_edge_map()
1889
1890        vertices = self._segvertices
1891        x1 = vertices[:, 0, 0]
1892        y1 = vertices[:, 0, 1]
1893
1894        x2 = vertices[:, 1, 0]
1895        y2 = vertices[:, 1, 1]
1896        return x1, y1, x2, y2
1897
1898    def get_closest_edge_fast(self, p, x1, y1, x2, y2):
1899        """
1900        Returns edge id which is closest to point p.
1901        Requires execution of make_segment_edge_map
1902        and predetermined 2d segment coordinates with  get_segvertices_xy
1903        """
1904        d2 = get_dist_point_to_segs(p[0:2], x1, y1, x2, y2, is_ending=True)
1905        # print '  min(d2)=',np.min(d2),'argmin=',np.argmin(d2),self.get_ids(self._edgeinds[np.argmin(d2)])
1906        return self.get_ids(self._edgeinds[np.argmin(d2)])
1907
1908    def export_sumoxml(self, filepath, encoding='UTF-8'):
1909        try:
1910            fd = open(filepath, 'w')
1911        except:
1912            print 'WARNING in export_sumoxml: could not open', filepath
1913            return False
1914        fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
1915
1916        fd.write(xm.begin('edges'))
1917        indent = 2
1918        self.write_xml(fd, indent=indent, is_print_begin_end=False)
1919        self.parent.roundabouts.write_xml(fd, indent=indent, is_print_begin_end=False)
1920        fd.write(xm.end('edges'))
1921        fd.close()
1922
1923    def update(self, ids=None, is_update_lanes=False):
1924        print 'Edges.update'
1925
1926        if ids is None:
1927            self.widths.value = self.nums_lanes.value * self.widths_lanes_default.value \
1928                + (self.widths_sidewalk.value >= 0) * (self.widths_sidewalk.value-self.widths_lanes_default.value)
1929
1930            # print '  self.widths.values =  \n',self.widths.value
1931            #polylines = polypoints_to_polylines(self.shapes.value)
1932            # print '  polylines[0:4]=\n',polylines[0:4]
1933            # print '  polylines[3].shape',polylines[3].shape
1934            #self.lengths.value = get_length_polylines(polypoints_to_polylines(self.shapes.value))
1935            # if len(self)>10:
1936            #    print '  shapes',self.shapes[1:10]
1937            #    print '  shapes.get_value',self.shapes.get_value()[0:9]
1938            #    print '  shapes.value',self.shapes.value[0:9]
1939            # else:
1940            #    print '  only len %d'%len(self)
1941            self.lengths.value = get_length_polypoints(self.shapes.value)
1942            ids = self.get_ids()
1943        else:
1944            self.widths[ids] = self.nums_lanes[ids] * self.widths_lanes_default[ids] \
1945                + (self.widths_sidewalk[ids] >= 0) * (self.widths_sidewalk[ids]-self.widths_lanes_default[ids])
1946            # print '  self.shapes[ids]',self.shapes[ids],type(self.shapes[ids])
1947            self.lengths[ids] = get_length_polypoints(self.shapes[ids])
1948
1949        self.widths.set_modified(True)
1950        self.lengths.set_modified(True)
1951
1952        if is_update_lanes:
1953            # print 'recalc laneshapes',ids
1954            lanes = self.get_lanes()
1955            for id_edge in ids:
1956                lanes.reshape_edgelanes(id_edge)
1957            self.make_segment_edge_map()
1958
1959    def set_shapes(self, ids, vertices, is_update_lanes=True):
1960        # print 'set_shapes',ids,vertices
1961
1962        self.shapes[ids] = vertices
1963        if not hasattr(ids, '__iter__'):
1964            ids = [ids]
1965        self.update(ids, is_update_lanes=is_update_lanes)
1966
1967    def get_coord_from_pos(self, id_edge, pos):
1968        """
1969        Returns network coordinate on edge id_edge at position pos.
1970        """
1971        return get_coord_on_polyline_from_pos(self.shapes[id_edge], pos)
1972
1973    def get_pos_from_coord(self, id_edge, coord):
1974        """
1975        Returns position on edge id_edge with coord
1976        perpendicularly projected on edge.
1977        """
1978
1979        return get_pos_on_polyline_from_coord(self.shapes[id_edge], coord)
1980
1981    def update_lanes(self, id_edge, ids_lane):
1982        # print 'update_lanes',id_edge,self.ids_sumo[id_edge] ,ids_lanes,self.nums_lanes[id_edge]
1983        # if self._is_laneshape:
1984        #    laneshapes = edges.get_laneshapes(self._id_edge, )
1985        #    lanes.shapes[self._ids_lanes[0]]
1986        if len(ids_lane) == 0:
1987            # no lanes given...make some with default values
1988            ids_lane = []
1989            lanes = self.get_lanes()
1990            for i in xrange(self.nums_lanes[id_edge]):
1991                id_lane = lanes.make(index=i, id_edge=id_edge)
1992                ids_lane.append(id_lane)
1993
1994        self.ids_lanes[id_edge] = ids_lane
1995
1996    def correct_endpoint(self):
1997        """
1998        Corrects end-point for older versione.
1999        """
2000        ids_sumo = self.ids_sumo.get_value()
2001        types_spread = self.types_spread.get_value()
2002        shapes = self.shapes.get_value()
2003        ids_fromnode = self.ids_fromnode.get_value()
2004        ids_tonode = self.ids_tonode.get_value()
2005        coords = self.parent.nodes.coords
2006        ind = 0
2007        is_corrected = False
2008        eps = 50.0
2009        for id_sumo, type_spread, shape, id_fromnode, id_tonode in zip(ids_sumo, types_spread, shapes, ids_fromnode, ids_tonode):
2010
2011            inds_oppo = np.flatnonzero((ids_tonode == id_fromnode) & (ids_fromnode == id_tonode))
2012            if len(inds_oppo) >= 1:
2013                ind_oppo = inds_oppo[0]
2014                # print '  correct',id_sumo,ids_sumo[ind_oppo]
2015
2016                ind_oppo = inds_oppo[0]
2017                shape_oppo = list(shapes[ind_oppo])
2018                shape_oppo.reverse()
2019                # print '  shape',shape
2020                # print '  shape',shape_oppo
2021                # print '  id_fromnode',id_fromnode,ids_tonode[ind_oppo]
2022                # print '  id_tomnode',id_tonode,ids_fromnode[ind_oppo]
2023                # print '  coords',coords[id_fromnode], coords[id_tonode]
2024                if len(shape_oppo) == len(shape):
2025
2026                    shapes[ind][0] = coords[id_fromnode]
2027                    shapes[ind_oppo][-1] = coords[id_fromnode]
2028                    #types_spread[inds_oppo[0]] = 0
2029                    #types_spread[ind] = 0
2030                    is_corrected = True
2031            ind += 1
2032
2033        if is_corrected:
2034            self.update(is_update_lanes=True)
2035
2036    def correct_spread(self):
2037        """
2038        Corrects spread type for older versione.
2039        """
2040        ids_sumo = self.ids_sumo.get_value()
2041        types_spread = self.types_spread.get_value()
2042        shapes = self.shapes.get_value()
2043        ind = 0
2044        is_corrected = False
2045        eps = 50.0
2046        for id_sumo, type_spread, shape in zip(ids_sumo, types_spread, shapes):
2047            if type_spread == 1:
2048                if id_sumo[0] == '-':
2049                    inds_oppo = np.flatnonzero(ids_sumo == id_sumo[1:])
2050                    if len(inds_oppo) == 1:
2051                        ind_oppo = inds_oppo[0]
2052                        shape_oppo = np.array(shapes[ind_oppo], np.float32)
2053                        if len(shape_oppo) == len(shape):
2054                            shape_oppo = list(shapes[ind_oppo])
2055                            shape_oppo.reverse()
2056                            shape_oppo = np.array(shape_oppo, np.float32)
2057                            dist = np.sum(np.abs(shape_oppo - np.array(shape, np.float32)))/float(len(shape))
2058                            # print '   id_sumo,dist',id_sumo,dist,eps
2059                            if dist < eps:
2060                                types_spread[inds_oppo[0]] = 0
2061                                types_spread[ind] = 0
2062                                is_corrected = True
2063            ind += 1
2064
2065        if is_corrected:
2066            self.update(is_update_lanes=True)
2067
2068
2069class Nodes(am.ArrayObjman):
2070    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Node_Descriptions
2071    def __init__(self, parent,
2072                 **kwargs):
2073        ident = 'nodes'
2074        self._init_objman(ident=ident, parent=parent, name='Nodes',
2075                          xmltag=('nodes', 'node', 'ids_sumo'),
2076                          version=0.1,
2077                          **kwargs)
2078        self._init_attributes()
2079
2080    def _init_attributes(self):
2081
2082        self.add_col(SumoIdsConf('Node'))
2083
2084        self.add_col(am.ArrayConf('coords',  np.zeros(3, dtype=np.float32),
2085                                  dtype=np.float32,
2086                                  groupnames=['state'],
2087                                  perm='r',
2088                                  name='Coords',
2089                                  unit='m',
2090                                  info='Node center coordinates.',
2091                                  ))
2092
2093        self.add_col(am.ArrayConf('radii',  5.0,
2094                                  dtype=np.float32,
2095                                  groupnames=['state'],
2096                                  perm='rw',
2097                                  name='Radius',
2098                                  info='Node radius',
2099                                  ))
2100
2101        self.add_col(am.ListArrayConf('shapes',
2102                                      groupnames=['_private'],
2103                                      perm='rw',
2104                                      name='Shape',
2105                                      unit='m',
2106                                      info='Node shape as list of 3D shape coordinates representing a polyline.',
2107                                      is_plugin=True,
2108                                      xmltag='shape',
2109                                      ))
2110
2111        self.add_col(am.ArrayConf('are_costum_shape',  False,
2112                                  dtype=np.bool,
2113                                  groupnames=['state'],
2114                                  perm='rw',
2115                                  name='costum shape',
2116                                  info='Node has a custom shape.',
2117                                  xmltag='customShape',
2118                                  ))
2119
2120        self.add(cm.AttrConf('radius_default', 3.0,
2121                             groupnames=['options'],
2122                             perm='rw',
2123                             unit='m',
2124                             name='Default radius',
2125                             info='Default node radius.',
2126                             ))
2127
2128        self.add_col(am.ArrayConf('types', 0,
2129                                  choices={
2130                                      "priority": 0,
2131                                      "traffic_light": 1,
2132                                      "right_before_left": 2,
2133                                      "unregulated": 3,
2134                                      "priority_stop": 4,
2135                                      "traffic_light_unregulated": 5,
2136                                      "allway_stop": 6,
2137                                      "rail_signal": 7,
2138                                      "zipper": 8,
2139                                      "traffic_light_right_on_red": 9,
2140                                      "rail_crossing": 10,
2141                                      "dead_end": 11,
2142                                  },
2143                                  dtype=np.int32,
2144                                  perm='rw',
2145                                  name='Type',
2146                                  info='Node type.',
2147                                  xmltag='type',
2148                                  ))
2149
2150        # this is actually a property defined in the TLS logic
2151        self.add_col(am.ArrayConf('types_tl', 0,
2152                                  dtype=np.int32,
2153                                  choices={
2154                                      "none": 0,
2155                                      "static": 1,
2156                                      "actuated": 2,
2157                                  },
2158                                  perm='rw',
2159                                  name='TL type',
2160                                  info='Traffic light type.',
2161                                  xmltag='tlType',
2162                                  ))
2163
2164        self.add_col(am.ArrayConf('turnradii',  1.5,
2165                                  dtype=np.float32,
2166                                  groupnames=['state'],
2167                                  perm='rw',
2168                                  name='Turn rad',
2169                                  unit='m',
2170                                  info='optional turning radius (for all corners) for that node.',
2171                                  xmltag='radius',
2172                                  ))
2173
2174        self.add_col(am.ArrayConf('are_keep_clear',  True,
2175                                  dtype=np.bool,
2176                                  groupnames=['state'],
2177                                  perm='rw',
2178                                  name='keep clear',
2179                                  info='Whether the junction-blocking-heuristic should be activated at this node.',
2180                                  xmltag='keepClear',
2181                                  ))
2182
2183    def set_edges(self, edges):
2184
2185        self.add_col(am.IdlistsArrayConf('ids_incoming', edges,
2186                                         groupnames=['state'],
2187                                         name='ID incoming',
2188                                         info='ID list of incoming edges.',
2189                                         ))
2190
2191        self.add_col(am.IdlistsArrayConf('ids_outgoing', edges,
2192                                         groupnames=['state'],
2193                                         name='ID outgoing',
2194                                         info='ID list of outgoing edges.',
2195                                         ))
2196
2197        self.add_col(am.IdlistsArrayConf('ids_controlled', edges,
2198                                         groupnames=['state'],
2199                                         name='IDs controlled',
2200                                         info='ID list of controlled edges. Edges which shall be controlled by a joined TLS despite being incoming as well as outgoing to the jointly controlled nodes.',
2201                                         xmltag='controlledInner',
2202                                         ))
2203        if self.get_version() < 0.1:
2204            self.delete('ids_tl_prog')
2205            self.turnradii.xmltag = 'radius'
2206            self.are_keep_clear.xmltag = 'keepClear'
2207            self.types_tl.xmltag = 'tlType'
2208            self.add_col(am.IdlistsArrayConf('ids_controlled', edges,
2209                                             groupnames=['state'],
2210                                             name='IDs controlled',
2211                                             info='ID list of controlled edges. Edges which shall be controlled by a joined TLS despite being incoming as well as outgoing to the jointly controlled nodes.',
2212                                             ))
2213
2214    def set_tlss(self, tlss):
2215
2216        self.add_col(am.IdsArrayConf('ids_tls',  tlss,
2217                                     groupnames=['state'],
2218                                     name='ID Tls',
2219                                     info='ID of traffic light system (TLS). Nodes with the same tls-value will be joined into a single traffic light system.',
2220                                     xmltag='tl',
2221                                     ))
2222
2223    def multimake(self, ids_sumo=[], **kwargs):
2224        return self.add_rows(n=len(ids_sumo), ids_sumo=ids_sumo,  **kwargs)
2225
2226    def configure_tls(self, id_node, id_tls, typecode=None, tlstype=None, nodetype="traffic_light"):
2227        if typecode is None:
2228            self.types_tl[id_node] = self.types_tl.choices[tlstype]
2229        else:
2230            self.types_tl[id_node] = typecode
2231
2232        self.types[id_node] = self.types.choices[nodetype]
2233        self.ids_tls[id_node] = id_tls
2234
2235    def remove_tls(self, id_node=None, id_tls=None, nodetype="priority"):
2236        if id_tls is not None:
2237            ids_node = self.select_ids(self.ids_tls.get_value() == id_tls)
2238
2239            self.types_tl[ids_node] = self.types_tl.choices["none"]
2240            self.types[ids_node] = self.types.choices[nodetype]
2241            self.ids_tls[ids_node] = -1
2242        else:
2243            self.types_tl[id_node] = self.types_tl.choices["none"]
2244            self.types[id_node] = self.types.choices[nodetype]
2245            self.ids_tls[id_node] = -1
2246
2247    def make(self, id_sumo='', nodetype='priority', coord=[],
2248             type_tl='static', id_tl_prog=0,
2249             shape=[],
2250             is_costum_shape=False,
2251             turnradius=1.5,
2252             is_keep_clear=True):
2253
2254        return self.add_row(ids_sumo=id_sumo,
2255                            types=self.types.choices[nodetype],
2256                            coords=coord,
2257                            are_costum_shape=is_costum_shape,
2258                            shape=shape,
2259                            types_tl=self.types_tl.choices[type_tl],
2260                            ids_tl_prog=id_tl_prog,
2261                            turnradii=turnradius,
2262                            are_keep_clear=is_keep_clear,
2263                            )
2264
2265    def add_outgoing(self, id_node, id_edge):
2266        if self.ids_outgoing[id_node] is not None:
2267            if id_edge not in self.ids_outgoing[id_node]:
2268                self.ids_outgoing[id_node].append(id_edge)
2269        else:
2270            self.ids_outgoing[id_node] = [id_edge]
2271
2272    def add_incoming(self, id_node, id_edge):
2273        # print 'add_incoming id_node,id_edge',id_node,id_edge
2274        # print '  ids_incoming',self.ids_incoming[id_node],type(self.ids_incoming[id_node])
2275        if self.ids_incoming[id_node] is not None:
2276            if id_edge not in self.ids_incoming[id_node]:
2277                self.ids_incoming[id_node].append(id_edge)
2278        else:
2279            self.ids_incoming[id_node] = [id_edge]
2280
2281    def export_sumoxml(self, filepath, encoding='UTF-8'):
2282        try:
2283            fd = open(filepath, 'w')
2284        except:
2285            print 'WARNING in export_sumoxml: could not open', filepath
2286            return False
2287        fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
2288        indent = 0
2289        self.write_xml(fd, indent)
2290        fd.close()
2291
2292    def write_xml(self, fd, indent):
2293        # print 'Nodes.write_xml'
2294        xmltag, xmltag_item, attrname_id = self.xmltag
2295        attrsman = self.get_attrsman()
2296        attrconfig_id = attrsman.get_config(attrname_id)  # getattr(self.get_attrsman(), attrname_id)
2297        xmltag_id = attrconfig_id.xmltag
2298        #attrsman = self.get_attrsman()
2299        coordsconfig = attrsman.get_config('coords')
2300        shapesconfig = attrsman.get_config('shapes')
2301        colconfigs = attrsman.get_colconfigs(is_all=True)
2302
2303        # print '  header'
2304        fd.write(xm.start(xmltag, indent))
2305        # print '  ', self.parent.get_attrsman().get_config('version').attrname,self.parent.get_attrsman().get_config('version').get_value()
2306        #fd.write( self.parent.get_attrsman().get_config('version').write_xml(fd) )
2307        self.parent.get_attrsman().get_config('version').write_xml(fd)
2308        fd.write(xm.stop())
2309
2310        fd.write(xm.start('location', indent+2))
2311        # print '  groups:',self.parent.get_attrsman().get_groups()
2312
2313        for attrconfig in self.parent.get_attrsman().get_group('location'):
2314            # print '    locationconfig',attrconfig.attrname
2315            attrconfig.write_xml(fd)
2316        fd.write(xm.stopit())
2317
2318        ids = self.get_ids()
2319        for _id, is_costum_shape in zip(ids, self.are_costum_shape[ids]):
2320            fd.write(xm.start(xmltag_item, indent+2))
2321
2322            # print ' make tag and id',_id
2323            fd.write(xm.num(xmltag_id, attrconfig_id[_id]))
2324
2325            # print ' write columns'
2326            for attrconfig in colconfigs:
2327                # print '    colconfig',attrconfig.attrname
2328                if attrconfig == coordsconfig:
2329                    x, y, z = attrconfig[_id]
2330                    fd.write(xm.num('x', x))
2331                    fd.write(xm.num('y', y))
2332                    fd.write(xm.num('z', z))
2333
2334                elif attrconfig == shapesconfig:
2335                    # write only if customshaped
2336                    if is_costum_shape:
2337                        attrconfig.write_xml(fd, _id)
2338
2339                elif attrconfig != attrconfig_id:
2340                    attrconfig.write_xml(fd, _id)
2341
2342            fd.write(xm.stopit())
2343
2344        fd.write(xm.end(xmltag, indent))
2345
2346    # def clean_node(self, id_node):
2347
2348    def clean(self, is_reshape_edgelanes=False, nodestretchfactor=1.2, n_min_nodeedges=2):
2349        # is_reshape_edgelanes = False, nodestretchfactor = 2.8
2350        print 'Nodes.clean', len(self), 'is_reshape_edgelanes', is_reshape_edgelanes
2351
2352        edges = self.parent.edges
2353        lanes = self.parent.lanes
2354        rad_min = self.radius_default.value
2355
2356        # print '  id(edges.shapes),id(edges.shapes.value)', id(edges.shapes),id(edges.shapes.value)#,edges.shapes.value
2357        # print '  id(self.coords),id(self.coords.value)', id(self.coords),id(self.coords.value)#,self.coords.value
2358        # print '  self.coords.value.shape',self.coords.value.shape
2359        # print '  len(self.coords),self.coords.shape',len(self.coords.value),self.coords.value
2360        for id_node in self.get_ids():
2361            ind_node = self.get_inds(id_node)
2362            # if id_node in TESTNODES:
2363            #    print 79*'_'
2364            #    print '  node',id_node
2365            #    print '   coords',self.coords[id_node]
2366            #    print '   coords',TESTNODES[0],self.coords[TESTNODES[0]]
2367            #    print '   coords',TESTNODES[1],self.coords[TESTNODES[1]]
2368            #    print '   radii',self.radii[id_node]
2369
2370            # attention, this is s safe method in case
2371            # that ids_outgoing and ids_incoming are not yet defined
2372            ids_edge_out = edges.select_ids(edges.ids_fromnode.value == id_node)
2373            ids_edge_in = edges.select_ids(edges.ids_tonode.value == id_node)
2374
2375            if (len(ids_edge_out) > 0) | (len(ids_edge_in) > 0):
2376                # distanza ad altri nodi
2377                #d = np.sum(np.abs(self.coords[id_node]-self.coords.value),1)
2378                #d = np.linalg.norm(self.coords[id_node]-self.coords.value,1)
2379                coords = self.coords[id_node]
2380                d = get_norm_2d(coords-self.coords.value)
2381                d[ind_node] = np.inf
2382                d_min = np.min(d)
2383                # print '  d_min',d_min
2384
2385                # estimate circumference of junction and determine node radius
2386                n_edges = len(ids_edge_in) + len(ids_edge_out)
2387                width_av = np.mean(np.concatenate((edges.widths[ids_edge_in], edges.widths[ids_edge_out])))
2388
2389                # here we assume a node with 6 entrance sides and a and 2 average width edges per side
2390                #circum = 2.0*max(6,n_edges)*width_av
2391                circum = nodestretchfactor*max(2, n_edges)*width_av
2392
2393                # print '  n_edges,width_av,radius',n_edges,width_av,max(6,n_edges)*width_av/(2*np.pi)
2394                radius = min(max(circum/(n_min_nodeedges*np.pi), rad_min), 0.4*d_min)
2395                self.radii[id_node] = radius
2396
2397                # if id_node in TESTNODES:
2398                #    print '  AFTER change radius:'#OK
2399                #    print '   coords',TESTNODES[0],self.coords[TESTNODES[0]]
2400                #    print '   coords',TESTNODES[1],self.coords[TESTNODES[1]]
2401
2402                for id_edge in ids_edge_in:
2403                    # print '    in edge',id_edge
2404                    shape = edges.shapes[id_edge]
2405                    n_shape = len(shape)
2406                    # edges.shapes[id_edge][::-1]:
2407                    for i in xrange(n_shape-1, -1, -1):
2408                        d = get_norm_2d(np.array([shape[i]-coords]))[0]
2409                        # print '      i,d,r',i , d, radius,d>radius
2410                        if d > radius:
2411                            # print '        **',i,d, radius
2412                            break
2413                    x, y = shape[i][:2]
2414                    # print 'shape',shape,
2415                    #dx,dy = shape[i+1][:2] - shape[i][:2]
2416                    dx, dy = coords[:2] - shape[i][:2]
2417                    dn = np.sqrt(dx*dx + dy*dy)
2418                    x1 = x + (d-radius)*dx/dn
2419                    y1 = y + (d-radius)*dy/dn
2420
2421                    if i == n_shape-1:
2422
2423                        shape[-1][:2] = [x1, y1]
2424                        edges.shapes[id_edge] = shape
2425
2426                    else:  # elif i>0:
2427                        shape[i+1][:2] = [x1, y1]
2428                        edges.shapes[id_edge] = shape[:i+2]
2429
2430                    # print '    x,y',x,y
2431                    # print '    x1,y1',x1,y1
2432                    # print '  shape[:i+2]',shape[:i+2]
2433                    # print '  shapes[id_edge]',edges.shapes[id_edge]
2434                    if is_reshape_edgelanes:
2435                        lanes.reshape_edgelanes(id_edge)
2436
2437                for id_edge in ids_edge_out:
2438                    # print '    out edge',id_edge
2439                    shape = edges.shapes[id_edge]
2440                    n_shape = len(shape)
2441                    # edges.shapes[id_edge][::-1]:
2442                    for i in xrange(n_shape):
2443                        d = get_norm_2d(np.array([shape[i]-coords]))[0]
2444                        # print '      i,d,r',i , d, radius,d>radius
2445                        if d > radius:
2446                            # print '        **',i,d, radius
2447                            break
2448                    x, y = coords[:2]  # shape[i-1][:2]
2449                    # print 'shape',shape,
2450                    #dx,dy = shape[i][:2]- shape[i-1][:2]
2451                    dx, dy = shape[i][:2] - coords[:2]
2452                    dn = np.sqrt(dx*dx + dy*dy)
2453                    x1 = x + (radius)*dx/dn
2454                    y1 = y + (radius)*dy/dn
2455                    if i == 0:
2456                        shape[0][:2] = [x1, y1]
2457                        edges.shapes[id_edge] = shape
2458
2459                    elif i < n_shape:
2460
2461                        shape[i-1][:2] = [x1, y1]
2462                        edges.shapes[id_edge] = shape[i-1:]
2463                    # print '    x,y',x,y
2464                    # print '    x1,y1',x1,y1
2465                    # print '  shape[:i+2]',shape[:i+2]
2466                    # print '  shapes[id_edge]',edges.shapes[id_edge]
2467                    if is_reshape_edgelanes:
2468                        lanes.reshape_edgelanes(id_edge)
2469
2470        self.radii.set_modified(True)
2471        edges.shapes.set_modified(True)
2472
2473
2474class Network(cm.BaseObjman):
2475    def __init__(self, parent=None, name='Network', **kwargs):
2476        print 'Network.__init__', parent, name
2477        self._init_objman(ident='net', parent=parent, name=name,
2478                          # xmltag = 'net',# no, done by netconvert
2479                          version=0.1,
2480                          **kwargs)
2481        attrsman = self.set_attrsman(cm.Attrsman(self))
2482        # print '  Network.parent',self.parent
2483        self._init_attributes()
2484
2485    def _init_attributes(self):
2486        attrsman = self.get_attrsman()
2487        self.version = attrsman.add(cm.AttrConf('version', '0.25',
2488                                                groupnames=['aux'],
2489                                                perm='r',
2490                                                name='Network version',
2491                                                info='Sumo network version',
2492                                                xmltag='version'
2493                                                ))
2494
2495        self.modes = attrsman.add(cm.ObjConf(Modes(self)))
2496        self.modes.clear()
2497        self.modes.add_default()
2498
2499        # print 'Network.__init__'
2500        # print '  MODES.values()',MODES.values()
2501        # print '  MODES.keys()',MODES.keys()
2502
2503        # self.modes.print_attrs()
2504        ##
2505
2506        ##
2507        self.nodes = attrsman.add(cm.ObjConf(Nodes(self)))
2508        self.edges = attrsman.add(cm.ObjConf(Edges(self)))
2509        self.lanes = attrsman.add(cm.ObjConf(Lanes(self)))
2510
2511        self.edges.set_nodes(self.nodes)
2512        self.edges.set_lanes(self.lanes)
2513        self.nodes.set_edges(self.edges)
2514        self.roundabouts = attrsman.add(cm.ObjConf(Roundabouts(self, self.edges, self.nodes)))
2515        self.connections = attrsman.add(cm.ObjConf(Connections(self)))
2516        self.crossings = attrsman.add(cm.ObjConf(Crossings(self)))
2517
2518        self.tlss = attrsman.add(cm.ObjConf(TrafficLightSystems(self)))
2519        self.nodes.set_tlss(self.tlss)
2520
2521        self.ptstops = attrsman.add(cm.ObjConf(pt.PtStops(self)))
2522
2523        self._offset = attrsman.add(cm.AttrConf('_offset', np.array([0.0, 0.0], dtype=np.float32),
2524                                                groupnames=['location', ],
2525                                                perm='r',
2526                                                name='Offset',
2527                                                info='Network offset in WEP coordinates',
2528                                                xmltag='netOffset',
2529                                                xmlsep=',',
2530                                                ))
2531
2532        self._projparams = attrsman.add(cm.AttrConf('_projparams', "!",
2533                                                    groupnames=['location', ],
2534                                                    perm='r',
2535                                                    name='Projection',
2536                                                    info='Projection parameters',
2537                                                    xmltag='projParameter',
2538                                                    ))
2539
2540        self._boundaries = attrsman.add(cm.AttrConf('_boundaries', np.array([0.0, 0.0, 0.0, 0.0], dtype=np.float32),
2541                                                    groupnames=['location', ],
2542                                                    perm='r',
2543                                                    name='Boundaries',
2544                                                    unit='m',
2545                                                    info='Network boundaries',
2546                                                    xmltag='convBoundary',
2547                                                    xmlsep=',',
2548                                                    ))
2549
2550        self._boundaries_orig = attrsman.add(cm.AttrConf('_boundaries_orig', np.array([0.0, 0.0, 0.0, 0.0]),
2551                                                         groupnames=['location', ],
2552                                                         perm='r',
2553                                                         name='Orig. boundaries',
2554                                                         info='Original network boundaries',
2555                                                         xmltag='origBoundary',
2556                                                         xmlsep=',',
2557                                                         ))
2558
2559    def _init_constants(self):
2560        pass
2561        #self._oldoffset = self._offset.copy()
2562        # print 'net._init_constants',self._offset,self._oldoffset
2563    # def set_oldoffset(self, offset):
2564    #    """
2565    #    Set explicitely an old net offset, if existing.
2566    #    This allows to update coordinates and shapes outside the network.
2567    #
2568    #    """
2569    #    self._oldoffset = offset
2570
2571    def set_version(self, version):
2572        self.version = version
2573
2574    def get_version(self):
2575        return self.version
2576
2577    def is_empty(self):
2578        return (len(self.nodes) == 0) & (len(self.edges) == 0)
2579
2580    def set_offset(self, offset):
2581        # if (offset is not self._offset) :
2582        #        self._oldoffset = self._offset.copy()
2583        self._offset = offset
2584
2585    def get_offset(self):
2586        return self._offset
2587
2588    # def is_offset_change(self):
2589    #    """
2590    #    Returns true if offset changed be approx 1 mm after last net import
2591    #    """
2592    #    return np.sum(abs(self._oldoffset - self._offset))>0.002
2593
2594    # def get_deltaoffset(self):
2595    #    return self._offset - self._oldoffset
2596
2597    # def remove_oldoffset(self):
2598    #    self._oldoffset = None
2599
2600    def set_boundaries(self, convBoundary, origBoundary=None):
2601        """
2602        Format of Boundary box
2603         [MinX, MinY ,MaxX, MaxY ]
2604
2605        """
2606        self._boundaries = convBoundary
2607        if origBoundary is None:
2608            self._boundaries_orig = self._boundaries
2609        else:
2610            self._boundaries_orig = origBoundary
2611
2612    def get_boundaries(self, is_netboundaries=False):
2613        if is_netboundaries:
2614            return self._boundaries
2615        else:
2616            return self._boundaries, self._boundaries_orig
2617
2618    def merge_boundaries(self, convBoundary, origBoundary=None):
2619        """
2620        Format of Boundary box
2621         [MinX, MinY ,MaxX, MaxY ]
2622
2623        """
2624        # print 'mergeBoundaries'
2625        self._boundaries = self.get_boundary_union(convBoundary, self._boundaries)
2626        if origBoundary is None:
2627            self._boundaries_orig = self._boundaries
2628        else:
2629            self._boundaries_orig = self.get_boundary_union(origBoundary, self._boundaries_orig)
2630        # print '  self._boundaries_orig =',self._boundaries_orig
2631        # print '  self._boundaries =',self._boundaries
2632
2633    def get_boundary_union(self, BB1, BB2):
2634        return [min(BB1[0], BB2[0]), min(BB1[1], BB2[1]), max(BB1[2], BB2[2]), max(BB1[3], BB2[3])]
2635
2636    def intersects_boundaries(self, BB):
2637        """
2638        Tests if the given Bounding Box or line intersects with
2639        the network boundaries.
2640
2641        Returns True if it is partially inside, or touching the
2642        border.
2643        Format of Boundary box
2644         [MinX, MinY ,MaxX, MaxY ]
2645            0     1     2     3
2646
2647        Returns False otherwise
2648        """
2649        # print 'intersects_boundaries'
2650        # print '  self',self._boundaries
2651        # print '  BB',BB
2652        # print ' return',( (self._boundaries[2] >= BB[0]) & (self._boundaries[0] <= BB[2]) &
2653        #     (self._boundaries[3] >= BB[1]) & (self._boundaries[1] <= BB[3]) )
2654
2655        return ((self._boundaries[2] >= BB[0]) & (self._boundaries[0] <= BB[2]) &
2656                (self._boundaries[3] >= BB[1]) & (self._boundaries[1] <= BB[3]))
2657        # if ( (self._boundaries[2] >= BB[0]) & (self._boundaries[0] <= BB[2]) &
2658        #     (self._boundaries[3] >= BB[1]) & (self._boundaries[1] <= BB[3]) ):
2659        #    return True
2660        # else:
2661        #    return False
2662
2663    def get_projparams(self):
2664        return self._projparams
2665
2666    def set_projparams(self, projparams="!"):
2667        # print 'setprojparams',projparams
2668        self._projparams = projparams
2669
2670    def get_rootfilename(self):
2671        if self.parent is not None:  # scenario exists
2672            return self.parent.get_rootfilename()
2673        else:
2674            return self.get_ident()
2675
2676    def get_rootfilepath(self):
2677        if self.parent is not None:
2678            return self.parent.get_rootfilepath()
2679        else:
2680            return os.path.join(os.getcwd(), self.get_rootfilename())
2681
2682    def get_filepath(self):
2683        """
2684        Default network filepath.
2685        """
2686        return self.get_rootfilepath()+'.net.xml'
2687
2688    def get_addfilepath(self):
2689        """
2690        Default filepath for additional files.
2691        """
2692        return self.get_rootfilepath()+'.add.xml'
2693
2694    def clear_net(self):
2695        """
2696        Remove all netelements.
2697        """
2698        self.clear()
2699        self.modes.add_default()
2700        # do other cleanup jobs
2701
2702    def call_netedit(self, filepath=None, is_maps=False, is_poly=True):
2703
2704        #filepath = self.export_netxml(filepath)
2705        if filepath is None:
2706            filepath = self.get_filepath()
2707
2708        # remove old netfile, is exists
2709        if os.path.isfile(filepath):
2710            os.remove(filepath)
2711
2712        filepath_edges, filepath_nodes, filepath_connections, filepath_tlss = self.export_painxml(filepath=filepath)
2713        if filepath_edges != "":
2714            # print '  netconvert: success'
2715            names = os.path.basename(filepath).split('.')
2716            dirname = os.path.dirname(filepath)
2717            if len(names) >= 3:
2718                rootname = '.'.join(names[:-2])
2719            elif len(names) <= 2:
2720                rootname = names[0]
2721
2722            addfilepath = self.export_addxml(is_ptstops=True, is_poly=False)
2723            if addfilepath is not False:
2724                option_addfiles_in = '  --sumo-additionals-file '+filepathlist_to_filepathstring(addfilepath)
2725            else:
2726                option_addfiles_in = ''
2727
2728            option_addfiles_out = ' --additionals-output ' + filepathlist_to_filepathstring(self.get_addfilepath())
2729
2730            configfilepath = self.write_guiconfig(rootname, dirname, is_maps)
2731
2732            #+ ' --sumo-net-file ' + filepathlist_to_filepathstring(filepath)
2733
2734            cml = 'netedit --ignore-errors.edge-type'\
2735                + ' --node-files '+filepathlist_to_filepathstring(filepath_nodes)\
2736                + ' --edge-files '+filepathlist_to_filepathstring(filepath_edges)\
2737                + ' --connection-files '+filepathlist_to_filepathstring(filepath_connections)\
2738                + ' --output-file '+filepathlist_to_filepathstring(filepath)\
2739                + ' --gui-settings-file ' + filepathlist_to_filepathstring(configfilepath)\
2740                + option_addfiles_in + option_addfiles_out
2741            #+ ' --output-prefix '+ filepathlist_to_filepathstring(os.path.join(dirname,rootname))
2742
2743            if len(self.tlss) > 0:
2744                cml += ' --tllogic-files '+filepathlist_to_filepathstring(filepath_tlss)
2745
2746            proc = subprocess.Popen(cml, shell=True)
2747            print '  run_cml cml=', cml
2748            # print '  pid = ',proc.pid
2749            proc.wait()
2750            if proc.returncode == 0:
2751                print '  netedit:success'
2752
2753                return self.import_netxml()
2754                # return self.import_xml() # use if netedit exports to plain xml files
2755            else:
2756                print '  netedit:error'
2757                return False
2758        else:
2759            print '  netconvert:error'
2760            return False
2761
2762    def call_sumogui(self, filepath=None, is_maps=True, is_poly=True):
2763
2764        if filepath is None:
2765            filepath = self.get_filepath()
2766            dirname = os.path.dirname(filepath)
2767        names = os.path.basename(filepath).split('.')
2768        dirname = os.path.dirname(filepath)
2769        if len(names) >= 3:
2770            rootname = '.'.join(names[:-2])
2771        elif len(names) <= 2:
2772            rootname = names[0]
2773
2774        configfilepath = self.write_guiconfig(rootname, dirname, is_maps)
2775
2776        #addfilepath = self.export_addxml(is_ptstops = True, is_poly = True)
2777
2778        stopfilepath = self.ptstops.export_sumoxml()
2779        if is_poly:
2780            polyfilepath = self.parent.landuse.export_polyxml()
2781        else:
2782            polyfilepath = None
2783
2784        # print '  is_polystopfilepath,polyfilepath',is_poly,stopfilepath,polyfilepath
2785        addfilepathlist = []
2786        if (stopfilepath is not None):
2787            addfilepathlist.append(stopfilepath)
2788
2789        if (polyfilepath is not None):
2790            addfilepathlist.append(polyfilepath)
2791
2792        if len(addfilepathlist) > 0:
2793            option_addfiles = '  --additional-files '+filepathlist_to_filepathstring(addfilepathlist)
2794        else:
2795            option_addfiles = ''
2796
2797        # if addfilepath is not False:
2798        #    option_addfiles = '  --additional-files '+filepathlist_to_filepathstring(addfilepath)
2799        # else:
2800        #    option_addfiles = ''
2801
2802        cml = 'sumo-gui '\
2803            + ' --net-file '+filepathlist_to_filepathstring(filepath)\
2804            + ' --gui-settings-file '+filepathlist_to_filepathstring(configfilepath)\
2805            + option_addfiles
2806
2807        proc = subprocess.Popen(cml, shell=True)
2808        print '  run_cml cml=', cml
2809        print '  pid = ', proc.pid
2810        proc.wait()
2811        return proc.returncode
2812
2813    def write_guiconfig(self, rootname=None, dirname=None, is_maps=False):
2814
2815        # check if there are maps
2816        maps = None
2817        if is_maps:
2818            if self.parent is not None:
2819                maps = self.parent.landuse.maps
2820
2821        # write netedit configfile
2822        templatedirpath = os.path.dirname(os.path.abspath(__file__))
2823        fd_template = open(os.path.join(templatedirpath, 'netedit_config.xml'), 'r')
2824
2825        if (rootname is not None) & (dirname is not None):
2826            configfilepath = os.path.join(dirname, rootname+'.netedit.xml')
2827        else:
2828            configfilepath = self.get_rootfilepath()+'.netedit.xml'
2829
2830        print 'write_guiconfig', configfilepath, is_maps & (maps is not None), maps
2831        fd_config = open(configfilepath, 'w')
2832        for line in fd_template.readlines():
2833            if line.count('<decals>') == 1:
2834                fd_config.write(line)
2835                if is_maps & (maps is not None):
2836                    maps.write_decals(fd_config, indent=12)
2837            else:
2838                fd_config.write(line)
2839
2840        fd_template.close()
2841        fd_config.close()
2842
2843        return configfilepath
2844
2845    def import_netxml(self, filepath=None, rootname=None, is_clean_nodes=False, is_remove_xmlfiles=False):
2846        print 'import_netxml', filepath
2847
2848        if rootname is None:
2849            rootname = self.get_rootfilename()
2850
2851        if filepath is None:
2852            filepath = self.get_filepath()
2853
2854        dirname = os.path.dirname(filepath)
2855
2856        if os.path.isfile(filepath):
2857            # print '  modes.names',self.modes.names
2858            cml = 'netconvert'\
2859                + ' --sumo-net-file '+filepathlist_to_filepathstring(filepath)\
2860                + ' --plain-output-prefix '+filepathlist_to_filepathstring(os.path.join(dirname, rootname))
2861            proc = subprocess.Popen(cml, shell=True)
2862            print '  run_cml cml=', cml
2863            print '  pid = ', proc.pid
2864            proc.wait()
2865            if not proc.returncode:
2866                print '  modes.names', self.modes.names
2867                return self.import_xml(rootname, dirname)
2868            else:
2869                return False
2870        else:
2871            return False
2872
2873    def export_addxml(self, filepath=None,
2874                      is_ptstops=True, is_poly=False,
2875                      encoding='UTF-8'):
2876        """
2877        Export additional file
2878        """
2879        if filepath is None:
2880            filepath = self.get_addfilepath()
2881
2882        try:
2883            fd = open(filepath, 'w')
2884
2885        except:
2886            print 'WARNING in write_obj_to_xml: could not open', filepath
2887            return False
2888
2889        #xmltag, xmltag_item, attrname_id = self.xmltag
2890        fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
2891        indent = 0
2892        #fd.write(xm.begin('routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://sumo.sf.net/xsd/routes_file.xsd"',indent))
2893
2894        fd.write(xm.begin('additional', indent))
2895
2896        if is_ptstops:
2897            if len(self.ptstops) > 0:
2898                self.ptstops.write_xml(fd, indent=indent+2)
2899
2900        if is_poly:
2901            if self.parent is not None:
2902                facilities = self.parent.landuse.facilities
2903                if len(facilities) > 0:
2904                    fd.write(xm.start('location', indent+2))
2905                    # print '  groups:',self.parent.net.get_attrsman().get_groups()
2906                    for attrconfig in self.get_attrsman().get_group('location'):
2907                            # print '    locationconfig',attrconfig.attrname
2908                        attrconfig.write_xml(fd)
2909                    fd.write(xm.stopit())
2910
2911                    facilities.write_xml(fd, indent=indent+2, is_print_begin_end=False)
2912
2913        fd.write(xm.end('additional', indent))
2914        fd.close()
2915        return filepath
2916
2917    def export_painxml(self, filepath=None, is_export_tlss=True):
2918        # now create rootfilepath in order to export first
2919        # the various xml file , then call netconvert
2920
2921        if filepath is None:
2922            filepath = self.get_filepath()
2923
2924        names = os.path.basename(filepath).split('.')
2925        dirname = os.path.dirname(filepath)
2926        if len(names) >= 3:
2927            rootname = '.'.join(names[:-2])
2928        elif len(names) <= 2:
2929            rootname = names[0]
2930
2931        filepath_edges = os.path.join(dirname, rootname+'.edg.xml')
2932        filepath_nodes = os.path.join(dirname, rootname+'.nod.xml')
2933        filepath_connections = os.path.join(dirname, rootname+'.con.xml')
2934        filepath_tlss = os.path.join(dirname, rootname+'.tll.xml')
2935
2936        self.edges.export_sumoxml(filepath_edges)
2937        self.nodes.export_sumoxml(filepath_nodes)
2938        self.connections.export_sumoxml(filepath_connections)
2939
2940        if (len(self.tlss) > 0) & is_export_tlss:
2941            self.tlss.export_sumoxml(filepath_tlss)
2942
2943        return filepath_edges, filepath_nodes, filepath_connections, filepath_tlss
2944
2945    def export_netxml(self, filepath=None, is_export_tlss=True, is_netconvert=True):
2946
2947        # now create rootfilepath in order to export first
2948        # the various xml file , then call netconvert
2949        if filepath is None:
2950            filepath = self.get_filepath()
2951
2952        print 'Net.export_netxml', filepath
2953        filepath_edges, filepath_nodes, filepath_connections, filepath_tlss = self.export_painxml(
2954            filepath=filepath, is_export_tlss=is_export_tlss)
2955
2956        #cml = 'netconvert --verbose --ignore-errors.edge-type'
2957        cml = 'netconvert --ignore-errors.edge-type'\
2958            + ' --node-files '+filepathlist_to_filepathstring(filepath_nodes)\
2959            + ' --edge-files '+filepathlist_to_filepathstring(filepath_edges)\
2960            + ' --connection-files '+filepathlist_to_filepathstring(filepath_connections)\
2961            + ' --output-file '+filepathlist_to_filepathstring(filepath)
2962
2963        if (len(self.tlss) > 0) & (is_export_tlss):
2964            cml += ' --tllogic-files '+filepathlist_to_filepathstring(filepath_tlss)
2965
2966        if is_netconvert:
2967            proc = subprocess.Popen(cml, shell=True)
2968            print 'run_cml cml=', cml
2969            print '  pid = ', proc.pid
2970            proc.wait()
2971            if proc.returncode == 0:
2972                print '  success'
2973                return filepath
2974            else:
2975                print '  success'
2976                return ''
2977        else:
2978            return ''
2979
2980    def import_xml(self, rootname=None, dirname=None, is_clean_nodes=False, is_remove_xmlfiles=False):
2981
2982        if not self.is_empty():
2983            oldoffset = self.get_offset()
2984        else:
2985            oldoffset = None
2986        print 'Network.import_xml oldoffset', oldoffset
2987        # remove current network
2988        # print '  remove current network'
2989        self.clear_net()
2990        # reload default SUMO MODES (maybe should not be here)
2991        # self.modes.add_rows(ids =  MODES.values(), names = MODES.keys())
2992
2993        if rootname is None:
2994            rootname = self.get_rootfilename()
2995
2996        if dirname is None:
2997            dirname = os.path.dirname(self.get_rootfilepath())
2998
2999        # print 'import_xml',dirname,rootname
3000        nodefilepath = os.path.join(dirname, rootname+'.nod.xml')
3001        edgefilepath = os.path.join(dirname, rootname+'.edg.xml')
3002        confilepath = os.path.join(dirname, rootname+'.con.xml')
3003        tlsfilepath = os.path.join(dirname, rootname+'.tll.xml')
3004
3005        if os.path.isfile(edgefilepath) & os.path.isfile(nodefilepath) & os.path.isfile(confilepath):
3006            nodereader = self.import_sumonodes(nodefilepath, is_remove_xmlfiles)
3007            edgereader = self.import_sumoedges(edgefilepath, is_remove_xmlfiles)
3008            if is_clean_nodes:
3009                # make edges and lanes end at the node boundaries
3010                # also recalculate lane shapes from edge shapes...if lane shapes are missing
3011                #self.lanes.reshape() #
3012                self.nodes.clean(is_reshape_edgelanes=True)
3013            else:
3014                # just recalculate lane shapes from edge shapes...if lane shapes are missing
3015                self.lanes.reshape()
3016            #    #pass
3017
3018            self.import_sumoconnections(confilepath, is_remove_xmlfiles)
3019
3020            if os.path.isfile(tlsfilepath):
3021                self.import_sumotls(tlsfilepath, is_remove_xmlfiles)
3022
3023            # print '  check additionals',self.ptstops.get_stopfilepath(),os.path.isfile(self.ptstops.get_stopfilepath())
3024            if os.path.isfile(self.ptstops.get_stopfilepath()):
3025                self.ptstops.import_sumostops(is_remove_xmlfiles=is_remove_xmlfiles)
3026
3027            # this fixes some references to edges and tls
3028            nodereader.write_to_net_post()
3029
3030            if oldoffset is not None:
3031                # check if offset changed
3032                # if self.is_offset_change():
3033                deltaoffset = self.get_offset()-oldoffset
3034                # print '  check update_netoffset',deltaoffset,oldoffset,self.get_offset(),np.sum(abs(deltaoffset))>0.002
3035                if np.sum(abs(deltaoffset)) > 0.002:
3036                    # communicate to scenario
3037                    if self.parent is not None:
3038                        self.parent.update_netoffset(deltaoffset)
3039
3040            # clean up ...should be done in each importer??
3041            # if is_remove_xmlfiles:
3042            #    os.remove(nodefilepath)
3043            #    os.remove(edgefilepath)
3044            #    os.remove(confilepath)
3045
3046            #    if os.path.isfile(tlsfilepath):
3047            #        os.remove(tlsfilepath)
3048            return True
3049        else:
3050            self.get_logger().w('import_sumonodes: files not found', key='message')
3051            return False
3052
3053    def import_sumonodes(self, filename, is_remove_xmlfiles=False, logger=None, **others):
3054        print 'import_sumonodes', filename
3055        # print '  parent',self.parent
3056        self.get_logger().w('import_sumonodes', key='message')
3057
3058        # timeit
3059        exectime_start = time.clock()
3060
3061        counter = SumoNodeCounter()
3062
3063        #reader = SumoEdgeReader(self, **others)
3064        # try:
3065
3066        parse(filename, counter)
3067        # print '  after: n_edge', counter.n_edge
3068        fastreader = SumoNodeReader(self, counter)
3069        parse(filename, fastreader)
3070
3071        fastreader.write_to_net()
3072
3073        # timeit
3074        print '  exec time=', time.clock() - exectime_start
3075        return fastreader
3076
3077    def import_sumoedges(self, filename, is_remove_xmlfiles=False, logger=None, **others):
3078        print 'import_sumoedges', filename
3079        logger = self.get_logger()
3080        logger.w('import_sumoedges', key='message')
3081        # timeit
3082        exectime_start = time.clock()
3083
3084        counter = SumoEdgeCounter()
3085
3086        #reader = SumoEdgeReader(self, **others)
3087        # try:
3088
3089        parse(filename, counter)
3090        # print '  after: n_edge', counter.n_edge
3091        fastreader = SumoEdgeReader(self, counter)
3092        parse(filename, fastreader)
3093
3094        fastreader.write_to_net()
3095        self.edges.update()
3096
3097        if is_remove_xmlfiles:
3098            os.remove(filename)
3099        # timeit
3100        print '  exec time=', time.clock() - exectime_start
3101
3102        # except KeyError:
3103        #    print >> sys.stderr, "Please mind that the network format has changed in 0.16.0, you may need to update your network!"
3104        #    raise
3105        return fastreader
3106
3107    def import_sumoconnections(self, filename, is_remove_xmlfiles=False, logger=None, **others):
3108        print 'import_sumoedges', filename
3109        logger = self.get_logger()
3110        logger.w('import_sumoconnections', key='message')
3111
3112        # timeit
3113        exectime_start = time.clock()
3114
3115        counter = SumoConnectionCounter()
3116
3117        parse(filename, counter)
3118        fastreader = SumoConnectionReader(self, counter)
3119        parse(filename, fastreader)
3120
3121        fastreader.write_to_net()
3122
3123        # timeit
3124        exectime_end = time.clock()
3125        print '  exec time=', exectime_end - exectime_start
3126        return fastreader
3127
3128    def import_sumotls(self, filename, is_remove_xmlfiles=False, logger=None, **others):
3129        """
3130        Import traffic ligh signals from tll.xml file
3131        as part of a complete import net process.
3132        """
3133        print 'import_sumotls', filename
3134
3135        if logger is None:
3136            logger = self.get_logger()
3137        logger.w('import_sumotls', key='message')
3138
3139        # timeit
3140        exectime_start = time.clock()
3141
3142        reader = SumoTllReader(self)
3143        parse(filename, reader)
3144
3145        # timeit
3146        exectime_end = time.clock()
3147        print '  exec time=', exectime_end - exectime_start
3148        return reader
3149
3150    def import_sumotls_to_net(self, filename, is_remove_xmlfiles=False, logger=None, **others):
3151        """
3152        Import traffic ligh signals from tll.xml file into an existing network.
3153        """
3154        print 'import_sumotls_to_net', filename
3155
3156        # associate nodes with sumo tls ID
3157        #map_id_node_to_id_tlss_sumo = {}
3158
3159        ids_node = self.nodes.select_ids(self.nodes.ids_tls.get_value() > -1)
3160        ids_tlss_sumo = self.tlss.ids_sumo[self.nodes.ids_tls[ids_node]].copy()
3161        # for id_node, id_tls_sumo in zip(ids_node, self.tlss.ids_sumo[self.nodes.ids_tls[ids_node]]):
3162        #    map_id_node_to_id_tlss_sumo[id_node] = id_tls_sumo
3163
3164        # clear all TLSs
3165        self.tlss.clear()
3166
3167        if logger is None:
3168            logger = self.get_logger()
3169        logger.w('import_sumotls_to_net', key='message')
3170
3171        # timeit
3172        exectime_start = time.clock()
3173
3174        reader = SumoTllReader(self)
3175        parse(filename, reader)
3176
3177        # timeit
3178        exectime_end = time.clock()
3179        print '  exec time=', exectime_end - exectime_start
3180
3181        # reestablish TLS IDs of nodes
3182
3183        # for id_node, id_tls_sumo in
3184        # zip(ids_node, self.nodes.ids_tls[ids_node]):
3185        #    map_id_node_to_id_tlss_sumo[id_node] = id_tls_sumo
3186        self.nodes.ids_tls[ids_node] = self.tlss.ids_sumo.get_ids_from_indices(ids_tlss_sumo)
3187
3188    def get_id_mode(self, modename):
3189        return self.modes.get_id_mode(modename)
3190
3191    def add_node(self, **kwargs):
3192        return self.nodes.make(**kwargs)
3193
3194    def add_nodes(self,  **kwargs):
3195        # print 'add_nodes'
3196        return self.nodes.multimake(**kwargs)
3197
3198    def clean_nodes(self, **kwargs):
3199        self.nodes.clean(**kwargs)
3200        self.edges.update()
3201
3202    def add_edge(self,  **kwargs):
3203        # print 'add_edge'
3204        return self.edges.make(**kwargs)
3205
3206    def add_edges(self,  **kwargs):
3207        # print 'add_edges'
3208        return self.edges.multimake(**kwargs)
3209
3210    def add_roundabout(self, **kwargs):
3211        return self.roundabouts.make(**kwargs)
3212
3213    def add_roundabouts(self, **kwargs):
3214        return self.roundabouts.multimake(**kwargs)
3215
3216    def add_lane(self, **kwargs):
3217        # print 'add_lane\n',
3218        # for key, value in kwargs.iteritems():
3219        #    print '  ',key,type(value),value
3220        return self.lanes.make(**kwargs)
3221
3222    def add_lanes(self, **kwargs):
3223        # print 'add_lanes\n',
3224        # for key, value in kwargs.iteritems():
3225        #    print '  ',key,type(value),value
3226        return self.lanes.multimake(**kwargs)
3227
3228    def complete_connections(self):
3229        """
3230        Extend connections also to pedestrian edges.
3231        """
3232        edges = self.edges
3233        nodes = self.nodes
3234        for id_node in self.nodes.get_ids():
3235            ids_outgoing = nodes.ids_outgoing[id_node]
3236            ids_incoming = nodes.ids_incoming[id_node]
3237            if ids_outgoing is None:
3238                ids_outgoing = set()
3239            else:
3240                ids_outgoing = set(ids_outgoing)
3241
3242            if ids_incoming is None:
3243                ids_incoming = set()
3244            else:
3245                ids_incoming = set(ids_incoming)
3246
3247            ids_outgoing_all = set(edges.select_ids(edges.ids_fromnode.get_value() == id_node))
3248            ids_incoming_all = set(edges.select_ids(edges.ids_tonode.get_value() == id_node))
3249
3250            for id_edge in ids_outgoing_all.difference(ids_outgoing):
3251                self.connect_edge_incoming(id_edge, ids_incoming_all)
3252
3253            for id_edge in ids_incoming_all.difference(ids_incoming):
3254                self.connect_edge_outgoing(id_edge, ids_outgoing_all)
3255
3256    def connect_edge_incoming(self, id_edge_to, ids_edge_from):
3257        """
3258        Connect id_edge_to with edges in list ids_edge_from.
3259        Attention, only lane index 0 of edge id_edge_to is connected.
3260        """
3261        # print 'connect_edge', ids_edge_from, id_edge_to
3262        for id_edge_from in ids_edge_from:
3263            ids_lane_from = self.edges.ids_lanes[id_edge_from]
3264            ids_lane_to = self.edges.ids_lanes[id_edge_to]
3265            if len(ids_lane_from) == 1:
3266                self.add_connection(id_fromlane=ids_lane_from[0], id_tolane=ids_lane_to[0])
3267
3268            # TODO: make connections also to multi lane edge
3269            else:
3270                for id_lane_from in ids_lane_from:
3271                    if not MODES["pedestrian"] in self.lanes.ids_modes_allow[id_lane_from]:
3272                        self.add_connection(id_fromlane=id_lane_from, id_tolane=ids_lane_to[0])
3273                        break
3274
3275    def connect_edge_outgoing(self, id_edge, ids_edge_to):
3276        """
3277        Connect id_edge with edges in list ids_edge_to.
3278        Attention, only lane index 0 of edge id_edge_from is connected.
3279        """
3280        # print 'connect_edge', ids_edge_from, id_edge_to
3281        for id_edge_to in ids_edge_to:
3282            ids_lane_to = self.edges.ids_lanes[id_edge_to]
3283            ids_lane_from = self.edges.ids_lanes[id_edge]
3284            if len(ids_lane_to) == 1:
3285                self.add_connection(id_fromlane=ids_lane_from[0], id_tolane=ids_lane_to[0])
3286
3287            # TODO: make connections also to multi lane edge
3288            else:
3289                for id_lane_to in ids_lane_to:
3290                    if not MODES["pedestrian"] in self.lanes.ids_modes_allow[id_lane_to]:
3291                        self.add_connection(id_fromlane=ids_lane_from[0], id_tolane=id_lane_to)
3292                        break
3293
3294    def add_connection(self, id_fromlane=-1, id_tolane=-1, **kwargs):
3295        # print 'add_connections id_fromlane , id_tolane ',id_fromlane , id_tolane
3296
3297        # for key, value in kwargs.iteritems():
3298        #    print '  ',key,type(value),value
3299
3300        id_fromedge = self.lanes.ids_edge[id_fromlane]
3301        id_toedge = self.lanes.ids_edge[id_tolane]
3302        id_node = self.edges.ids_tonode[id_fromedge]
3303        self.nodes.add_incoming(id_node, id_fromedge)
3304        self.nodes.add_outgoing(id_node, id_toedge)
3305
3306        return self.connections.make(id_fromlane=id_fromlane, id_tolane=id_tolane, **kwargs)
3307
3308    def add_connections(self, ids_fromlane=[], ids_tolane=[], **kwargs):
3309
3310        # for key, value in kwargs.iteritems():
3311        #    print '  ',key,type(value),value
3312
3313        ids_fromedge = self.lanes.ids_edge[ids_fromlane]
3314        ids_toedge = self.lanes.ids_edge[ids_tolane]
3315        ids_node = self.edges.ids_tonode[ids_fromedge]
3316        add_incoming = self.nodes.add_incoming
3317        add_outgoing = self.nodes.add_outgoing
3318        for id_node, id_fromedge, id_toedge in zip(ids_node, ids_fromedge, ids_toedge):
3319            add_incoming(id_node, id_fromedge)
3320            add_outgoing(id_node, id_toedge)
3321
3322        return self.connections.multimake(ids_fromlane=ids_fromlane, ids_tolane=ids_tolane, **kwargs)
3323
3324    def add_crossing(self, **kwargs):
3325        # print 'add_crossing\n',
3326        return self.crossings.make(**kwargs)
3327
3328    def add_crossings(self, **kwargs):
3329        # print 'add_crossings\n',
3330        return self.crossings.multimake(**kwargs)
3331
3332
3333class SumoConnectionCounter(handler.ContentHandler):
3334    """Parses a SUMO edge XML file and counts edges and lanes."""
3335
3336    def __init__(self):
3337        self.n_con = 0
3338        self.n_cross = 0
3339
3340    def startElement(self, name, attrs):
3341        if name == 'connection':
3342            if attrs.has_key('to'):
3343                self.n_con += 1
3344
3345        if name == 'crossing':
3346            self.n_cross += 1
3347
3348
3349class SumoConnectionReader(handler.ContentHandler):
3350    """Parses a SUMO connection XML file"""
3351
3352    def __init__(self, net, counter):
3353        self._net = net
3354
3355        # print 'SumoConnectionReader:n_con,n_cross',counter.n_con,counter.n_cross
3356
3357        # connections
3358        self._ind_con = -1
3359        self.ids_fromlane = np.zeros(counter.n_con, np.int32)
3360        self.ids_tolane = np.zeros(counter.n_con, np.int32)
3361        self.are_passes = np.zeros(counter.n_con, np.bool)
3362        self.are_keep_clear = np.zeros(counter.n_con, np.bool)
3363        self.positions_cont = np.zeros(counter.n_con, np.float32)
3364        self.are_uncontrolled = np.zeros(counter.n_con, np.bool)
3365
3366        # crossings
3367        self._ind_cross = -1
3368        self.ids_node = np.zeros(counter.n_cross, np.int32)
3369        self.ids_edges = np.zeros(counter.n_cross, np.object)
3370        self.widths = np.zeros(counter.n_cross, np.float32)
3371        self.are_priority = np.zeros(counter.n_cross, np.bool)
3372        self.are_discard = np.zeros(counter.n_cross, np.bool)
3373
3374        self._ids_node_sumo = self._net.nodes.ids_sumo
3375        self._ids_edge_sumo = self._net.edges.ids_sumo
3376        self._ids_edgelanes = self._net.edges.ids_lanes
3377
3378    def startElement(self, name, attrs):
3379        # print 'startElement',name
3380
3381        if name == 'connection':
3382            # <connection from="153009994" to="153009966#1" fromLane="0" toLane="0" pass="1"/>
3383
3384            if attrs.has_key('to'):
3385                self._ind_con += 1
3386                i = self._ind_con
3387                # print 'startElement',name,i
3388                id_fromedge = self._ids_edge_sumo.get_id_from_index(attrs['from'])
3389                id_toedge = self._ids_edge_sumo.get_id_from_index(attrs['to'])
3390
3391                #id_fromlane = self._ids_edgelanes[id_fromedge][int(attrs.get('fromLane',0))]
3392                #id_tolane = self._ids_edgelanes[id_toedge][int(attrs.get('toLane',0))]
3393
3394                # print '  id_sumo fromedge', attrs['from'],len(self._ids_edgelanes[id_fromedge]) ,  int(attrs['fromLane'])
3395                self.ids_fromlane[i] = self._ids_edgelanes[id_fromedge][int(attrs['fromLane'])]
3396                self.ids_tolane[i] = self._ids_edgelanes[id_toedge][int(attrs['toLane'])]
3397                self.are_passes[i] = int(attrs.get('pass', 0))
3398                self.are_keep_clear[i] = int(attrs.get('keepClear ', 1))
3399                self.positions_cont[i] = float(attrs.get('contPos ', 0.0))
3400                self.are_uncontrolled[i] = int(attrs.get('uncontrolled', 0))
3401            else:
3402                id_fromedge = self._ids_edge_sumo.get_id_from_index(attrs['from'])
3403
3404        if name == 'crossing':
3405            self._ind_cross += 1
3406            i = self._ind_cross
3407            # print 'startElement',name
3408
3409            self.ids_node[i] = self._ids_node_sumo.get_id_from_index(attrs['node'])
3410            self.ids_edges[i] = self._ids_edge_sumo.get_ids_from_indices(attrs['edges'].split(' '))
3411            self.widths[i] = float(attrs.get('width ', 4.0))
3412            self.are_priority[i] = int(attrs.get('priority ', 0))
3413            self.are_discard[i] = int(attrs.get('discard', 0))
3414
3415    def write_to_net(self):
3416
3417        # print 'write_to_net'
3418        ids_con = self._net.add_connections(
3419            ids_fromlane=self.ids_fromlane,
3420            ids_tolane=self.ids_tolane,
3421            sare_passes=self.are_passes,
3422            are_keep_clear=self.are_keep_clear,
3423            positions_cont=self.positions_cont,
3424            are_uncontrolled=self.are_uncontrolled,
3425        )
3426
3427        ids_cross = self._net.add_crossings(
3428            ids_node=self.ids_node,
3429            ids_edges=self.ids_edges,
3430            widths=self.widths,
3431            are_priority=self.are_priority,
3432            are_discard=self.are_discard,
3433        )
3434
3435
3436class SumoNodeCounter(handler.ContentHandler):
3437    """Parses a SUMO edge XML file and counts edges and lanes."""
3438
3439    def __init__(self):
3440        self.n_node = 0
3441
3442    def startElement(self, name, attrs):
3443        # print 'startElement',name,self.n_edge,self.n_lane,self.n_roundabout
3444        if name == 'node':
3445            self.n_node += 1
3446
3447
3448class SumoNodeReader(handler.ContentHandler):
3449    """Parses a SUMO node XML file"""
3450
3451    def __init__(self, net, counter):
3452        self._net = net
3453
3454        # print 'SumoEdgeFastreader'
3455
3456        #self._ids_node_sumo = net.nodes.ids_sumo
3457        #self._nodecoords = net.nodes.coords
3458
3459        self._nodetypemap = self._net.nodes.types.choices
3460        self._tltypemap = self._net.nodes.types_tl.choices
3461        self.radius_default = self._net.nodes.radius_default.get_value()
3462        # node attrs
3463        self.ids_sumo = np.zeros(counter.n_node, np.object)
3464        self.types = np.zeros(counter.n_node, np.int32)
3465        self.coords = np.zeros((counter.n_node, 3), np.float32)
3466        self.types_tl = np.zeros(counter.n_node, np.int32)
3467        self.ids_sumo_tls = np.zeros(counter.n_node, np.object)
3468        self.turnradii = np.zeros(counter.n_node, np.float32)
3469        self.are_costum_shape = np.zeros(counter.n_node, np.bool)
3470        self.shapes = np.zeros(counter.n_node, np.object)
3471        self.are_keep_clear = np.zeros(counter.n_node, np.bool)
3472        self._ind_node = -1
3473        self.ids_sumo_controlled = np.zeros(counter.n_node, np.object)
3474        self.ids_sumo_controlled[:] = None
3475        self._offset_delta = np.array([0.0, 0.0])
3476        self._isNew = len(self._net.nodes) == 0
3477
3478    def write_to_net(self):
3479
3480        # print 'write_to_net'
3481        self.ids_node = self._net.add_nodes(
3482            ids_sumo=self.ids_sumo,
3483            types=self.types,
3484            coords=self.coords,
3485            shapes=self.shapes,
3486            are_costum_shape=self.are_costum_shape,
3487            types_tl=self.types_tl,
3488            turnradii=self.turnradii,
3489            are_keep_clear=self.are_keep_clear,
3490        )
3491        # attention:
3492        # attributes ids_sumo_tls and ids_sumo_controlled will be added later
3493        # when tls and edges are read
3494        # see write_to_net_post
3495
3496    def write_to_net_post(self):
3497        """
3498        To be called after edges and tls are read.
3499        """
3500        # print 'write_to_net_post'
3501        get_ids_edge = self._net.edges.ids_sumo.get_ids_from_indices
3502        ids_controlled = self._net.nodes.ids_controlled
3503        for id_node, ids_sumo_edge in zip(self.ids_node, self.ids_sumo_controlled):
3504            if ids_sumo_edge is not None:
3505                if len(ids_sumo_edge) == 0:
3506                    ids_controlled[id_node] = []
3507                else:
3508                    ids_controlled[id_node] = get_ids_edge(ids_sumo_edge)
3509
3510        # convert sumo ids into internal ids and set to nodes
3511        # print '  self.ids_sumo_tls',self.ids_sumo_tls
3512        # print '  self._net.tlss.ids_sumo',self._net.tlss.ids_sumo.value
3513        self._net.nodes.ids_tls[self.ids_node] = self._net.tlss.ids_sumo.get_ids_from_indices_save(self.ids_sumo_tls)
3514
3515    def startElement(self, name, attrs):
3516        # print 'startElement',name
3517        # if attrs.has_key('id'): print attrs['id']
3518        # elif (attrs.has_key('from')&attrs.has_key('to')): print 'from',attrs['from'],'to',attrs['to']
3519        # elif (attrs.has_key('from')&attrs.has_key('to')): print 'from',attrs['from'],'to',attrs['to']
3520        # else: print '.'
3521
3522        if name == 'nodes':
3523            version = self._net.get_version()
3524            if self._isNew | (version == attrs['version']):
3525                self._net.set_version(attrs['version'])
3526            else:
3527                print 'WARNING: merge with incompatible net versions %s versus %s.' % (version, attrs['version'])
3528
3529        elif name == 'location':  # j.s
3530            # print 'startElement',name,self._isNew
3531            netOffsetStrings = attrs['netOffset'].strip().split(",")
3532            offset = np.array([float(netOffsetStrings[0]), float(netOffsetStrings[1])])
3533            offset_prev = self._net.get_offset()
3534            if self._isNew:
3535                self._net.set_offset(offset)
3536                # print '  offset_prev,offset',offset_prev,offset,type(offset)
3537            else:
3538
3539                self._offset_delta = offset-offset_prev
3540                self._net.set_offset(offset)
3541                # print '  offset_prev,offset,self._offset_delta',offset_prev,offset,type(offset),self._offset_delta
3542
3543            convBoundaryStr = attrs['convBoundary'].strip().split(",")
3544            origBoundaryStr = attrs['origBoundary'].strip().split(",")
3545            # print '  convBoundaryStr',convBoundaryStr
3546            # print '  origBoundary',origBoundaryStr
3547
3548            if self._isNew:
3549                self._net.set_boundaries([float(convBoundaryStr[0]),
3550                                          float(convBoundaryStr[1]),
3551                                          float(convBoundaryStr[2]),
3552                                          float(convBoundaryStr[3])],
3553                                         [float(origBoundaryStr[0]),
3554                                          float(origBoundaryStr[1]),
3555                                          float(origBoundaryStr[2]),
3556                                          float(origBoundaryStr[3])]
3557                                         )
3558            else:
3559                self._net.merge_boundaries([float(convBoundaryStr[0]),
3560                                            float(convBoundaryStr[1]),
3561                                            float(convBoundaryStr[2]),
3562                                            float(convBoundaryStr[3])],
3563                                           [float(origBoundaryStr[0]),
3564                                            float(origBoundaryStr[1]),
3565                                            float(origBoundaryStr[2]),
3566                                            float(origBoundaryStr[3])]
3567                                           )
3568            if self._isNew:
3569                if attrs.has_key('projParameter'):
3570                    self._net.set_projparams(attrs['projParameter'])
3571            else:
3572                if attrs.has_key('projParameter'):
3573                    if self._net.get_projparams() != attrs['projParameter']:
3574                        print 'WARNING: merge with incompatible projections %s versus %s.' % (
3575                            self._net.getprojparams(), attrs['projparams'])
3576
3577        elif name == 'node':
3578            if attrs['id'][0] != ':':  # no internal node
3579                self._ind_node += 1
3580                i = self._ind_node
3581                x0, y0 = self._offset_delta
3582
3583                self.ids_sumo[i] = attrs['id']
3584                sumotypes_node = str(attrs.get('type', 'priority'))
3585
3586                self.types[i] = self._nodetypemap[sumotypes_node]
3587                x, y, z = float(attrs['x'])-x0, float(attrs['y'])-y0, float(attrs.get('z', 0.0))
3588                self.coords[i] = [x, y, z]
3589
3590                sumotype_tl = attrs.get('tlType', 'none')
3591                if sumotypes_node == 'traffic_light':
3592                    if sumotype_tl == 'none':
3593                        sumotype_tl = 'static'
3594
3595                self.types_tl[i] = self._tltypemap[sumotype_tl]
3596                self.ids_sumo_tls[i] = attrs.get('tl', None)
3597                self.turnradii[i] = attrs.get('radius', 1.5)
3598
3599                # not in nodes
3600
3601                shape = np.array(xm.process_shape(attrs.get('shape', ''), offset=self._offset_delta))
3602
3603                if len(shape) < 3:  # no or insufficient shape info
3604                    # shape should be a list of np array coords
3605
3606                    # generate some default shape
3607                    r0 = self.radius_default
3608                    shape = [np.array([x-r0, y-r0, z], dtype=np.float32),
3609                             np.array([x+r0, y-r0, z], dtype=np.float32),
3610                             np.array([x+r0, y+r0, z], dtype=np.float32),
3611                             np.array([x-r0, y+r0, z], dtype=np.float32),
3612                             ]
3613                    self.are_costum_shape[i] = False
3614
3615                else:
3616                    self.are_costum_shape[i] = True
3617
3618                self.shapes[i] = list(shape)
3619
3620                self.are_keep_clear[i] = bool(attrs.get('keepClear', True))
3621
3622                # 'controlledInner'
3623                # Edges which shall be controlled by a joined TLS
3624                # despite being incoming as well as outgoing to
3625                # the jointly controlled nodes
3626                # problem: we do not know yet the edge IDs
3627                #
3628                if attrs.has_key('controlledInner'):
3629                    self.ids_sumo_controlled[i] = attrs['controlledInner'].strip().split(' ')
3630                else:
3631                    self.ids_sumo_controlled[i] = []
3632
3633
3634# class SumoTllCounter(handler.ContentHandler):
3635#    """Parses a SUMO tll XML file and counts edges and lanes."""
3636#
3637#    def __init__(self):
3638#        self.n_tls = 0
3639#
3640#
3641#    def startElement(self, name, attrs):
3642#        #print 'startElement',name,self.n_tls
3643#        if name == 'tlLogic':
3644#            self.n_tls += 1
3645
3646class SumoTllReader(handler.ContentHandler):
3647    """Parses a SUMO tll XML file and reads it into net."""
3648
3649    def __init__(self, net):
3650        self.net = net
3651        self.connections = net.connections
3652        self.tlss = net.tlss
3653        # print 'SumoEdgeFastreader'
3654
3655        self.get_id_tls = net.nodes.ids_sumo.get_id_from_index
3656
3657        #n_tls = counter.n_tls
3658        self.ptypes_choices = self.tlss.tlls.value.ptypes.choices
3659        self.ids_sumo_tls = self.tlss.ids_sumo
3660
3661        self.reset_prog()
3662
3663        self.tlsconnections = {}
3664
3665    def reset_prog(self):
3666        self.id_sumo_tls = None
3667        self.durations = []
3668        self.durations_min = []
3669        self.durations_max = []
3670        self.states = []
3671
3672    def startElement(self, name, attrs):
3673
3674        if name == 'tlLogic':
3675            # print '\n startElement',name,attrs['id']
3676            self.id_sumo_tls = attrs['id']
3677            self.ptype = self.ptypes_choices.get(attrs.get('type', None), 1)
3678            self.id_prog = attrs.get('programID', None)
3679            self.offset = attrs.get('offset', None)
3680
3681        elif name == 'phase':
3682            # print 'startElement',name,self.id_sumo_tls
3683            if self.id_sumo_tls is not None:
3684                # print '  append',attrs.get('duration',None),attrs.get('state',None),len(attrs.get('state',''))
3685                duration = int(attrs.get('duration', 0))
3686                self.durations.append(duration)
3687                self.durations_min.append(int(attrs.get('minDur', duration)))
3688                self.durations_max.append(int(attrs.get('maxDur', duration)))
3689                self.states.append(attrs.get('state', None))
3690
3691        # elif name == 'tlLogics':
3692        #    pass
3693
3694        elif name == 'connection':
3695            id_sumo_tls = attrs['tl']
3696            # print 'startElement',name,id_sumo_tls,int(attrs['linkIndex'])
3697            # print '  self.tlsconnections',self.tlsconnections
3698
3699            if not self.tlsconnections.has_key(id_sumo_tls):
3700                self.tlsconnections[id_sumo_tls] = {}
3701
3702            id_con = self.connections.get_id_from_sumoinfo(attrs['from'],
3703                                                           attrs['to'], int(attrs['fromLane']), int(attrs['toLane']))
3704            if id_con >= 0:
3705                self.tlsconnections[id_sumo_tls][int(attrs['linkIndex'])] = id_con
3706
3707    def endElement(self, name):
3708        #edges = self._net.edges
3709        #lanes = self._net.lanes
3710        if name == 'tlLogic':
3711            # print 'endElement',name,self.id_sumo_tls
3712            # print '  ptype',self.ptype
3713            # print '  durations',self.durations
3714            # print '  durations_min',self.durations_min
3715            # print '  durations_max',self.durations_max
3716            # print '  states',self.states
3717            # print '  self.id_prog='+self.id_prog+'='
3718            self.tlss.make(self.id_sumo_tls,
3719                           id_prog=self.id_prog,
3720                           ptype=self.ptype,
3721                           offset=self.offset,
3722                           durations=self.durations,
3723                           durations_min=self.durations_min,
3724                           durations_max=self.durations_max,
3725                           states=self.states,
3726                           )
3727
3728            self.reset_prog()
3729
3730        elif name == 'tlLogics':
3731            # print 'endElement',name,len(self.tlss)
3732            # end of scanning. Write controlled connections to tlss
3733            # print '  tlsconnections',self.tlsconnections
3734
3735            for id_sumo_tls, conmap in self.tlsconnections.iteritems():
3736
3737                inds_con = np.array(conmap.keys(), dtype=np.int32)
3738
3739                ids_con = np.zeros(np.max(inds_con)+1, np.int32)
3740                # print '  cons for',id_sumo_tls,conmap
3741                # print '  inds',inds_con,len(ids_con)
3742                # print '  values',conmap.values(),len(ids_con)
3743                ids_con[inds_con] = conmap.values()  # <<<<<<<<<<<
3744
3745                id_tls = self.tlss.ids_sumo.get_id_from_index(id_sumo_tls)
3746                self.tlss.set_connections(id_tls, ids_con)
3747                #self.tlss.set_connections(self.get_id_tls(id_sumo_tls), ids_con)
3748
3749
3750class SumoEdgeCounter(handler.ContentHandler):
3751    """Parses a SUMO edge XML file and counts edges and lanes."""
3752
3753    def __init__(self):
3754        self.n_edge = 0
3755        self.n_lane = 0
3756        self.n_roundabout = 0
3757        self._n_edgelane = 0
3758        #self._net = net
3759        #self._ids_edge_sumo = net.edges.ids_sumo
3760        #self._ids_node_sumo = net.nodes.ids_sumo
3761
3762    def startElement(self, name, attrs):
3763        # print 'startElement',name,self.n_edge,self.n_lane,self.n_roundabout
3764        if name == 'edge':
3765            self.n_edge += 1
3766            self.n_lane += int(attrs['numLanes'])
3767
3768        elif name == 'roundabout':
3769            self.n_roundabout += 1
3770
3771
3772class SumoEdgeReader(handler.ContentHandler):
3773    """Parses a SUMO edge XML file and reads it into net."""
3774
3775    def __init__(self, net, counter, offset_delta=np.array([0.0, 0.0])):
3776        self._net = net
3777
3778        # print 'SumoEdgeFastreader'
3779
3780        self._ids_node_sumo = net.nodes.ids_sumo
3781        self._nodecoords = net.nodes.coords
3782        self._modenames = net.modes.names
3783        self._offset_delta = offset_delta
3784        #self._isNew = len(self._net.nodes)==0
3785
3786        # edge attrs
3787        self._ind_edge = -1
3788        # print '  n_edge',counter.n_edge
3789        self.ids_edge_sumo = np.zeros(counter.n_edge, np.object)  # net.edges.ids_sumo
3790        self.ids_edge_sumo[:] = None  # ??needed
3791
3792        self.ids_fromnode = np.zeros(counter.n_edge, np.int32)
3793        self.ids_tonode = np.zeros(counter.n_edge, np.int32)
3794        self.types_edge = np.zeros(counter.n_edge, np.object)
3795        self.widths = np.zeros(counter.n_edge, np.float32)  # used only for lane width if no lane data is given
3796        self.nums_lanes = np.zeros(counter.n_edge, np.int32)
3797        self.speeds_max = np.zeros(counter.n_edge, np.float32)
3798        self.priorities = np.zeros(counter.n_edge, np.int32)
3799        #length = 0.0,
3800        self.shapes = np.zeros(counter.n_edge, np.object)
3801        self.types_spread = np.zeros(counter.n_edge, np.int32)
3802        self.spread_choices = net.edges.types_spread.choices
3803        # "right": 0,
3804        # "center": 1,
3805        self.names = np.zeros(counter.n_edge, np.object)
3806        self.offsets_end = np.zeros(counter.n_edge, np.float32)
3807        self.widths_lanes_default = np.zeros(counter.n_edge, np.float32)
3808        self.widths_sidewalk = -1*np.ones(counter.n_edge, np.float32)
3809        self.inds_lanes_edges = np.zeros(counter.n_edge, np.object)
3810        #self.inds_lanes_edges[:] = None
3811        self._ind_lanes_edges = []
3812
3813        #self.ids_sumoedge_to_ind = {}
3814
3815        # lane attrs
3816        # print '  n_lane',counter.n_lane
3817        self._ind_lane = -1
3818        self.index_lanes = np.zeros(counter.n_lane, np.int32)
3819        self.width_lanes = np.zeros(counter.n_lane, np.float32)
3820        self.speed_max_lanes = np.zeros(counter.n_lane, np.float32)
3821        self.offset_end_lanes = np.zeros(counter.n_lane, np.float32)
3822        self.ids_modes_allow = np.zeros(counter.n_lane, np.object)
3823        self.ids_modes_disallow = np.zeros(counter.n_lane, np.object)
3824        self.ids_mode_lanes = np.zeros(counter.n_lane, np.int32)
3825        self.inds_edge_lanes = np.zeros(counter.n_lane, np.int32)
3826        #self.shapes_lanes = np.zeros(counter.n_lane,np.object)
3827
3828        # roundabout attrs
3829        # print '  n_roundabout',counter.n_roundabout
3830        self._ind_ra = -1
3831        self.ids_sumoedges_ra = np.zeros(counter.n_roundabout, np.object)
3832        self.ids_nodes_ra = np.zeros(counter.n_roundabout, np.object)
3833
3834        ############################
3835
3836    def startElement(self, name, attrs):
3837        # print 'startElement',name
3838        # if attrs.has_key('id'): print attrs['id']
3839        # elif (attrs.has_key('from')&attrs.has_key('to')): print 'from',attrs['from'],'to',attrs['to']
3840        # elif (attrs.has_key('from')&attrs.has_key('to')): print 'from',attrs['from'],'to',attrs['to']
3841        # else: print '.'
3842
3843        if name == 'edge':
3844            # if not attrs.has_key('function') or attrs['function'] != 'internal':
3845            #id_fromnode = nodes.ids_sumo.get_id_from_index(id_fromnode_sumo)
3846            #id_tonode = nodes.ids_sumo.get_id_from_index(id_tonode_sumo)
3847            self._ind_edge += 1
3848            ind = self._ind_edge
3849            # print 'startElement edge',ind,attrs['id']
3850            self.ids_edge_sumo[ind] = attrs['id']
3851
3852            id_fromnode = self._ids_node_sumo.get_id_from_index(str(attrs['from']))
3853            id_tonode = self._ids_node_sumo.get_id_from_index(str(attrs['to']))
3854            self.ids_fromnode[ind] = id_fromnode
3855            self.ids_tonode[ind] = id_tonode
3856
3857            self.types_edge[ind] = str(attrs.get('type', ''))
3858            self.nums_lanes[ind] = int(attrs.get('numLanes', 1))
3859
3860            # attention sumo width attribute is actually lane width!!
3861            # here we multiply with number of lanes
3862            # however, will be updated later as a sum of lanewidth
3863            self.widths[ind] = float(attrs.get('width', 3.5)) * self.nums_lanes[ind]
3864
3865            self.types_spread[ind] = self.spread_choices[str(attrs.get('spreadType', 'right'))]  # usually center
3866            # print '  ',self.types_spread[ind]
3867
3868            #length = 0.0,
3869            shape = np.array(xm.process_shape(attrs.get('shape', ''), offset=self._offset_delta))
3870
3871            if len(shape) < 2:  # insufficient shape data
3872                # shape should be a list of np array coords
3873                # ATTENTIOn: we need to copy here, otherwise the reference
3874                # to node coordinates will be kept!!
3875                shape = np.array([1.0*self._nodecoords[id_fromnode], 1.0*self._nodecoords[id_tonode]])
3876
3877                if self.types_spread[ind] == 1:  # center
3878                    angles_perb = get_angles_perpendicular(shape)
3879                    halfwidth = self.widths[ind]
3880                    shape[:, 0] += np.cos(angles_perb) * halfwidth
3881                    shape[:, 1] += np.sin(angles_perb) * halfwidth
3882
3883            self.shapes[ind] = shape
3884
3885            self.speeds_max[ind] = float(attrs.get('speed', 13.888))
3886            self.priorities[ind] = int(attrs.get('priority', 9))
3887            self.names[ind] = unicode(attrs.get('name', ''))
3888            self.offsets_end[ind] = float(attrs.get('endOffset', 0.0))
3889
3890            # this lanewidth will be used as default if no lane width attribute
3891            # is given
3892            self.widths_lanes_default[ind] = float(attrs.get('width', 3.0))
3893            #self.widths_sidewalk[ind] = float(attrs.get('sidewalkWidth',-1.0))
3894
3895            # check for some attributes that are actually lane attributes
3896
3897            self._allow_egdeattr = attrs.get('allow', None)
3898            self._disallow_egdeattr = attrs.get('disallow', None)
3899            #self._is_laneshape = True
3900            # print '  self._id_edge',self._id_edge
3901
3902        elif name == 'lane':
3903            self._ind_lane += 1
3904            ind = self._ind_lane
3905            ind_edge = self._ind_edge
3906            speed_max_default = -1
3907
3908            if attrs.has_key('allow'):
3909                ids_modes_allow = list(self._modenames.get_ids_from_indices(attrs['allow'].split(' ')))
3910
3911            # done in end element
3912            # elif self._allow_egdeattr is not None:
3913            #    ids_modes_allow = list(self._modenames.get_ids_from_indices(self._allow_egdeattr.split(' ')))
3914
3915            else:
3916                edgetype = self.types_edge[self._ind_edge]
3917
3918                if OSMEDGETYPE_TO_MODES.has_key(edgetype):
3919                    ids_modes_allow, speed_max_default = OSMEDGETYPE_TO_MODES[edgetype]
3920                else:
3921                    ids_modes_allow = []
3922
3923            if attrs.has_key('disallow'):
3924                ids_modes_disallow = list(self._modenames.get_ids_from_indices(attrs['disallow'].split(' ')))
3925
3926            # done in end element
3927            # elif self._disallow_egdeattr is not None:
3928            #    ids_modes_allow = list(self._modenames.get_ids_from_indices(self._disallow_egdeattr.split(' ')))
3929            #
3930            else:
3931                ids_modes_disallow = []
3932
3933            index = int(attrs.get('index', -1))
3934            # use defaults from edge
3935            width = float(attrs.get('width', -1))
3936            speed_max = float(attrs.get('speed', -1))
3937            offset_end_lane = float(attrs.get('endOffset', -1))
3938
3939            self.set_lane(ind, ind_edge, index, width, speed_max,
3940                          ids_modes_allow, ids_modes_disallow, offset_end_lane)
3941
3942        elif name == 'roundabout':
3943            self._ind_ra += 1
3944            self.ids_sumoedges_ra[self._ind_ra] = attrs.get('edges', '').split(' ')
3945            self.ids_nodes_ra[self._ind_ra] = self._ids_node_sumo.get_ids_from_indices(
3946                attrs.get('nodes', '').split(' '))
3947
3948    # def characters(self, content):
3949    #    if self._currentLane is not None:
3950    #        self._currentShape = self._currentShape + content
3951
3952    def endElement(self, name):
3953        #edges = self._net.edges
3954        #lanes = self._net.lanes
3955        if name == 'edge':
3956            # this is the number of lanes declared within the edge tag
3957            n_lane = self.nums_lanes[self._ind_edge]
3958
3959            # print 'SumoEdgeReader.endElement',self._ind_lane,n_lane
3960
3961            # this loop counts from the current number of pased lanes
3962            # (which may be zero) to the declared number of lanes.
3963            # This is necessary because it can happen that no lane
3964            # specifications are provided
3965            index = len(self._ind_lanes_edges)
3966            ind_edge = self._ind_edge
3967            while index < n_lane:
3968                # if len(self._ind_lanes_edges) ==0:
3969                # edge description provided no specific lane information
3970                # create n_lanes and us some properties from current edge
3971                self._ind_lane += 1
3972                ind = self._ind_lane
3973
3974                edgetype = self.types_edge[ind_edge]
3975
3976                if self._allow_egdeattr is not None:
3977                    ids_modes_allow = list(self._modenames.get_ids_from_indices(self._allow_egdeattr.split(' ')))
3978                else:
3979                    if OSMEDGETYPE_TO_MODES.has_key(edgetype):
3980                        ids_modes_allow, speed_max_default = OSMEDGETYPE_TO_MODES[edgetype]
3981                    else:
3982                        ids_modes_allow = []
3983
3984                if self._disallow_egdeattr is not None:
3985                    ids_modes_disallow = list(self._modenames.get_ids_from_indices(self._disallow_egdeattr.split(' ')))
3986                else:
3987                    ids_modes_disallow = []
3988
3989                # figure main mode
3990                if len(ids_modes_allow) == 1:
3991                    id_mode_main = ids_modes_allow[0]  # pick  as major mode
3992                else:
3993                    id_mode_main = -1  # no major mode specified
3994
3995                self.index_lanes[ind] = index
3996                # derive lane attributes from edge attributes
3997                self.width_lanes[ind] = self.widths_lanes_default[ind_edge]  # copy from edge attr
3998                self.speed_max_lanes[ind] = self.speeds_max[ind_edge]  # copy from edge attr
3999                self.offset_end_lanes[ind] = self.offset_end_lanes[ind_edge]  # copy from edge attr
4000
4001                self.ids_modes_allow[ind] = ids_modes_allow
4002                self.ids_modes_disallow[ind] = ids_modes_disallow
4003                self.inds_edge_lanes[ind] = ind_edge
4004                #self.shapes_lanes[ind]  = self.getShape(attrs.get('shape',''), offset = self._offset_delta)
4005                self.ids_mode_lanes[ind] = id_mode_main
4006                self._ind_lanes_edges.append(ind)
4007
4008                index += 1
4009
4010            self.inds_lanes_edges[self._ind_edge] = self._ind_lanes_edges
4011            self._ind_lanes_edges = []
4012
4013    def set_lane(self, ind, ind_edge, index, width, speed_max, ids_modes_allow, ids_modes_disallow, offset_end_lane):
4014
4015        if len(ids_modes_allow) == 1:
4016            id_mode_main = ids_modes_allow[0]  # pick  as major mode
4017            # elif len(ids_modes_allow) == 1:
4018
4019        else:
4020            id_mode_main = -1  # no major mode specified
4021
4022        is_sidewalk = False
4023        if index == 0:
4024            is_sidewalk = (MODES['pedestrian'] in ids_modes_allow)  # test for pedestrian sidewalk
4025
4026        if speed_max < 0:
4027            if (index == 0) & is_sidewalk:
4028                speed_max = 0.8  # default walk speed
4029            else:
4030                speed_max = self.speeds_max[ind_edge]  # copy from edge
4031
4032        if width < 0:
4033            if is_sidewalk:
4034                width = 1.0
4035            else:
4036                width = self.widths_lanes_default[ind_edge]  # copy from edge
4037
4038        if is_sidewalk:
4039            self.widths_sidewalk[ind_edge] = width
4040
4041        if offset_end_lane < 0:
4042            offset_end_lane = self.offsets_end[ind_edge]
4043
4044        self.index_lanes[ind] = index
4045        self.width_lanes[ind] = width
4046        self.speed_max_lanes[ind] = speed_max
4047        self.offset_end_lanes[ind] = offset_end_lane
4048        self.ids_modes_allow[ind] = ids_modes_allow
4049        self.ids_modes_disallow[ind] = ids_modes_disallow
4050        self.ids_mode_lanes[ind] = id_mode_main
4051        self.inds_edge_lanes[ind] = ind_edge
4052        #self.shapes_lanes[ind]  = self.getShape(attrs.get('shape',''), offset = self._offset_delta)
4053
4054        self._ind_lanes_edges.append(ind)
4055        # self._ids_lane.append(id_lane)
4056
4057    def write_to_net(self):
4058
4059        # print 'write_to_net'
4060        ids_edge = self._net.add_edges(
4061            ids_sumo=self.ids_edge_sumo,
4062            ids_fromnode=self.ids_fromnode,
4063            ids_tonode=self.ids_tonode,
4064            types=self.types_edge,
4065            nums_lanes=self.nums_lanes,
4066            speeds_max=self.speeds_max,
4067            priorities=self.priorities,
4068            #lengths = length,
4069            shapes=self.shapes,
4070            types_spread=self.types_spread,
4071            names=self.names,
4072            offsets_end=self.offsets_end,
4073            widths_lanes_default=self.widths_lanes_default,
4074            widths_sidewalk=self.widths_sidewalk,
4075        )
4076
4077        # print '  self.inds_edge_lanes',self.inds_edge_lanes
4078        ids_lanes = self._net.add_lanes(
4079            indexes=self.index_lanes,
4080            widths=self.width_lanes,
4081            speeds_max=self.speed_max_lanes,
4082            offsets_end=self.offset_end_lanes,
4083            ids_modes_allow=self.ids_modes_allow,
4084            ids_modes_disallow=self.ids_modes_disallow,
4085            ids_mode=self.ids_mode_lanes,  # main mode will be determined from other attributes
4086            ids_edge=ids_edge[self.inds_edge_lanes],
4087            # shapes = self.shapes_lanes, # lane shapes are not given -> must be derived from edge shape
4088        )
4089        #edges.update_lanes(self._id_edge, self._ids_lane)
4090        ids_edgelanes = self._net.edges.ids_lanes
4091        ind = 0
4092        for inds_lane in self.inds_lanes_edges:
4093            ids_edgelanes[ids_edge[ind]] = ids_lanes[inds_lane]
4094            # print '  id_edge,ids_lanes[inds_lane]',ids_edge[ind],ids_lanes[inds_lane]
4095            ind += 1
4096
4097        # roundaboutS
4098        ids_edge_sumo = self._net.edges.ids_sumo
4099        ids_roundabout = self._net.add_roundabouts(
4100            ids_nodes=self.ids_nodes_ra,
4101        )
4102        ids_edges_ra = self._net.roundabouts.ids_edges
4103        i = 0
4104        for id_roundabout in ids_roundabout:
4105            ids_edges_ra[id_roundabout] = ids_edge_sumo.get_ids_from_indices(self.ids_sumoedges_ra[i])
4106            i += 1
4107
4108
4109class SumonetImporter(CmlMixin, Process):
4110    def __init__(self, net, rootname=None, rootdirpath=None, netfilepath=None,
4111                 is_clean_nodes=False, logger=None, **kwargs):
4112
4113        self._init_common('sumonetimporter', name='SUMO net import',
4114                          logger=logger,
4115                          info='Converts a SUMO .net.xml file to nod.xml, edg.xml and con.xml file and reads into scenario.',
4116                          )
4117        self._net = net
4118
4119        self.init_cml('netconvert')
4120
4121        if rootname is None:
4122            rootname = net.parent.get_rootfilename()
4123
4124        if rootdirpath is None:
4125            if net.parent is not None:
4126                rootdirpath = net.parent.get_workdirpath()
4127            else:
4128                rootdirpath = os.getcwd()
4129
4130        if netfilepath is None:
4131            netfilepath = os.path.join(rootdirpath, rootname+'.net.xml')
4132
4133        attrsman = self.get_attrsman()
4134        self.add_option('netfilepath', netfilepath,
4135                        groupnames=['options'],  # this will make it show up in the dialog
4136                        cml='--sumo-net-file',
4137                        perm='rw',
4138                        name='Net file',
4139                        wildcards='Net XML files (*.net.xml)|*.net.xml',
4140                        metatype='filepath',
4141                        info='SUMO Net file in XML format.',
4142                        )
4143
4144        self.workdirpath = attrsman.add(cm.AttrConf('workdirpath', rootdirpath,
4145                                                    groupnames=['_private'],  # ['options'],#['_private'],
4146                                                    perm='r',
4147                                                    name='Workdir',
4148                                                    metatype='dirpath',
4149                                                    info='Working directory for this scenario.',
4150                                                    ))
4151
4152        self.rootname = attrsman.add(cm.AttrConf('rootname', rootname,
4153                                                 groupnames=['_private'],
4154                                                 perm='r',
4155                                                 name='Scenario shortname',
4156                                                 info='Scenario shortname is also rootname of converted files.',
4157                                                 ))
4158
4159        self.is_clean_nodes = attrsman.add(cm.AttrConf('is_clean_nodes', is_clean_nodes,
4160                                                       groupnames=['options'],
4161                                                       perm='rw',
4162                                                       name='Clean Nodes',
4163                                                       info='If set, then shapes around nodes are cleaned up.',
4164                                                       ))
4165
4166    def update_params(self):
4167        """
4168        Make all parameters consistent.
4169        example: used by import OSM to calculate/update number of tiles
4170        from process dialog
4171        """
4172        pass
4173        #self.workdirpath = os.path.dirname(self.netfilepath)
4174        #bn =  os.path.basename(self.netfilepath).split('.')
4175        # if len(bn)>0:
4176        #    self.rootname = bn[0]
4177
4178    def do(self):
4179        self.update_params()
4180        cml = self.get_cml()+' --plain-output-prefix '+filepathlist_to_filepathstring(os.path.join(self.workdirpath, self.rootname))
4181        # print 'SumonetImporter.do',cml
4182        #import_xml(self, rootname, dirname, is_clean_nodes = True)
4183        self.run_cml(cml)
4184        if self.status == 'success':
4185            self._net.import_xml(self.rootname, self.workdirpath, is_clean_nodes=self.is_clean_nodes)
4186
4187        # print 'do',self.newident
4188        # self._scenario = Scenario(  self.newident,
4189        #                                parent = None,
4190        #                                workdirpath = self.workdirpath,
4191        #                                logger = self.get_logger(),
4192        #                                )
4193        return True
4194
4195    def get_net(self):
4196        return self._net
4197
4198
4199class OsmImporter(netconvert.NetConvertMixin):
4200    def __init__(self, net=None,
4201                 osmfilepaths=None,
4202                 netfilepath=None,
4203                 logger=None,
4204                 **kwargs):
4205
4206        # All parameters and Default Values in NetconvertMixin
4207
4208        if net is None:
4209            self._net = Network()
4210        else:
4211            self._net = net
4212
4213        self.init_common_netconvert('osmimporter', net,  name='OSM import',
4214                                    logger=logger,
4215                                    info='Converts a OSM  file to SUMO nod.xml, edg.xml and con.xml file and reads into scenario.',
4216                                    )
4217
4218        # osm specific options
4219        if osmfilepaths is None:
4220            if net.parent is not None:
4221                rootname = net.parent.get_rootfilename()
4222                rootdirpath = net.parent.get_workdirpath()
4223            else:
4224                rootname = net.get_ident()
4225                rootdirpath = os.getcwd()
4226
4227            osmfilepaths = os.path.join(rootdirpath, rootname+'.osm.xml')
4228
4229        self.add_option('osmfilepaths', osmfilepaths,
4230                        groupnames=['options'],
4231                        cml='--osm-files',
4232                        perm='rw',
4233                        name='OSM files',
4234                        wildcards='OSM XML files (*.osm)|*.osm*',
4235                        metatype='filepaths',
4236                        info='Openstreetmap files to be imported.',
4237                        )
4238
4239        self.init_all(**kwargs)
4240        self.add_option('is_guess_signals_tls', kwargs.get('is_guess_signals_tls', False),
4241                        groupnames=['options', 'traffic lights'],
4242                        cml='--tls.guess-signals',
4243                        perm='rw',
4244                        name='Guess signals.',
4245                        info='Interprets tls nodes surrounding an intersection as signal positions for a  larger TLS. This is typical pattern for OSM-derived networks',
4246                        is_enabled=lambda self: self.is_guess_tls,
4247                        )
4248        self.add_option('dist_guess_signal_tls', kwargs.get('dist_guess_signal_tls', 20.0),
4249                        groupnames=['options', 'traffic lights'],
4250                        cml='--tls.guess-signals.dist',
4251                        perm='rw',
4252                        unit='m',
4253                        name='Signal guess dist.',
4254                        info='Distance for interpreting nodes as signal locations',
4255                        is_enabled=lambda self: self.is_guess_tls & self.is_guess_signals_tls,
4256                        )
4257
4258
4259if __name__ == '__main__':
4260    ###############################################################################
4261    # print 'sys.path',sys.path
4262    from agilepy.lib_wx.objpanel import objbrowser
4263    from agilepy.lib_base.logger import Logger
4264    #net = Network(logger = Logger())
4265    net = Network(logger=Logger())
4266    net.import_xml('facsp2', 'testnet')
4267
4268    objbrowser(net)
4269