text: """Contains the base network class."""
  
  from flow.core.params import InitialConfig
  from flow.core.params import TrafficLightParams
  from flow.core.params import SumoCarFollowingParams
  from flow.core.params import SumoLaneChangeParams
  import time
  import xml.etree.ElementTree as ElementTree
  from lxml import etree
  from collections import defaultdict
  
  # default sumo probability value  TODO (ak): remove
  DEFAULT_PROBABILITY = 0
  # default sumo vehicle length value (in meters) TODO (ak): remove
  DEFAULT_LENGTH = 5
  # default sumo vehicle class class TODO (ak): remove
  DEFAULT_VCLASS = 0
  
  
  class Network(object):
      """Base network class.
  
      Initializes a new network. Networks are used to specify features of
      a network, including the positions of nodes, properties of the edges
      and junctions connecting these nodes, properties of vehicles and
      traffic lights, and other features as well. These features can later be
      acquired  from this class via a plethora of get methods (see
      documentation).
  
      This class uses network specific features to generate the necessary network
      configuration files needed to initialize a simulation instance. The methods
      of this class are called by the base network class.
  
      The network files can be created in one of three ways:
  
      * Custom networks can be generated by defining the properties of the
        network's directed graph. This is done by defining the nodes and edges
        properties using the ``specify_nodes`` and ``specify_edges`` methods,
        respectively, as well as other properties via methods including
        ``specify_types``, ``specify_connections``, etc... For more on this,
        see the tutorial on creating custom networks or refer to some of the
        available networks.
  
      * Network data can be collected from an OpenStreetMap (.osm) file. The
        .osm file is specified in the NetParams object. For example:
  
          >>> from flow.core.params import NetParams
          >>> net_params = NetParams(osm_path='/path/to/osm_file.osm')
  
        In this case, no ``specify_nodes`` and ``specify_edges`` methods are
        needed. However, a ``specify_routes`` method is still needed to specify
        the appropriate routes vehicles can traverse in the network.
  
      * Network data can be collected from an sumo-specific network (.net.xml)
        file. This file is specified in the NetParams object. For example:
  
          >>> from flow.core.params import NetParams
          >>> net_params = NetParams(template='/path/to/template')
  
        In this case, no ``specify_nodes`` and ``specify_edges`` methods are
        needed. However, a ``specify_routes`` method is still needed to specify
        the appropriate routes vehicles can traverse in the network.
  
      This class can be instantiated once and reused in multiple experiments.
      Note that this function stores all the relevant parameters. The
      generate() function still needs to be called separately.
  
      Attributes
      ----------
      orig_name : str
          the variable provided under the `name` parameter to this object upon
          instantiation
      name : str
          the variable provided under the `name` parameter to this object upon
          instantiation, appended with a timestamp variable. This timestamp is
          meant to differentiate generated network files during parallelism
      vehicles : flow.core.params.VehicleParams
          vehicle specific parameters, used to specify the types and number of
          vehicles at the start of a simulation
      net_params : flow.core.params.NetParams
          network specific parameters, used primarily to identify properties of a
          network such as the lengths of edges and the number of lanes in each
          edge. This attribute is very network-specific, and should contain the
          variables denoted by the `ADDITIONAL_NET_PARAMS` dict in each network
          class file
      initial_config : flow.core.params.InitialConfig
          specifies parameters that affect the positioning of vehicle in the
          network at the start of a simulation. For more, see flow/core/params.py
      traffic_lights : flow.core.params.TrafficLightParams
          used to describe the positions and types of traffic lights in the
          network. For more, see flow/core/params.py
      nodes : list of dict or None
          list of nodes that are assigned to the network via the `specify_nodes`
          method. All nodes in this variable are expected to have the following
          properties:
  
          * **name**: a unique identifier for the node
          * **x**: x-coordinate of the node, in meters
          * **y**: y-coordinate of the node, in meters
  
          If the network is meant to generate the network from an OpenStreetMap
          or template file, this variable is set to None
      edges : list of dict or None
          edges that are assigned to the network via the `specify_edges` method.
          This include the shape, position, and properties of all edges in the
          network. These properties include the following mandatory properties:
  
          * **id**: name of the edge
          * **from**: name of the node the edge starts from
          * **to**: the name of the node the edges ends at
          * **length**: length of the edge
  
          In addition, either the following properties need to be specifically
          defined or a **type** variable property must be defined with equivalent
          attributes in `self.types`:
  
          * **numLanes**: the number of lanes on the edge
          * **speed**: the speed limit for vehicles on the edge
  
          Moreover, the following attributes may optionally be available:
  
          * **shape**: the positions of intermediary nodes used to define the
            shape of an edge. If no shape is specified, then the edge will appear
            as a straight line.
  
          Note that, if the network is meant to generate the network from an
          OpenStreetMap or template file, this variable is set to None
      types : list of dict or None
          A variable used to ease the definition of the properties of various
          edges. Each element in the list consists of a dict consisting of the
          following property:
  
          * **id**: name of the edge type. Edges in the `self.edges` attribute
            with a similar value under the "type" key will adopt the properties
            of other components of this list, such as "speed" and "numLanes".
  
          If the type variable is None, then no types are available within the
          network. Furthermore, a proper example of this variable being used can
          be found under `specify_types` in flow/networks/loop.py.
  
          Note that, if the network is meant to generate the network from an
          OpenStreetMap or template file, this variable is set to None
      connections : list of dict or None
          A variable used to describe how any specific node's incoming and
          outgoing edges/lane pairs are connected. If no connections are
          specified, sumo generates default connections.
  
          If the connections attribute is set to None, then the connections
          within the network will be specified by the simulator.
  
          Note that, if the network is meant to generate the network from an
          OpenStreetMap or template file, this variable is set to None
      routes : dict
          A variable whose keys are the starting edge of a specific route, and
          whose values are the list of edges a vehicle is meant to traverse
          starting from that edge. These are only applied at the start of a
          simulation; vehicles are allowed to reroute within the environment
          immediately afterwards.
      edge_starts : list of (str, float)
          a list of tuples in which the first element of the tuple is the name of
          the edge/intersection/internal_link, and the second value is the
          distance of the link from some global reference, i.e. [(link_0, pos_0),
          (link_1, pos_1), ...]
      internal_edge_starts : list of (str, float)
          A variable similar to `edge_starts` but for junctions within the
          network. If no junctions are available, this variable will return the
          default variable: `[(':', -1)]` needed by sumo simulations.
      intersection_edge_starts : list of (str, float)
          A variable similar to `edge_starts` but for intersections within
          the network. This variable will be deprecated in future releases.
  
      Example
      -------
      The following examples are derived from the `RingNetwork` Network class
      located in flow/networks/ring.py, and should serve as an example of the
      types of outputs to be expected from the different variables of a network
      class.
  
      First of all, the ring road network class can be instantiated by running
      the following commands (note if this this unclear please refer to Tutorial
      1):
  
      >>> from flow.networks import RingNetwork
      >>> from flow.core.params import NetParams, VehicleParams
      >>>
      >>> network = RingNetwork(
      >>>     name='test',
      >>>     vehicles=VehicleParams(),
      >>>     net_params=NetParams(
      >>>         additional_params={
      >>>             'length': 230,
      >>>             'lanes': 1,
      >>>             'speed_limit': 30,
      >>>             'resolution': 40,
      >>>         }
      >>>     )
      >>> )
  
      The various attributes then look as follows:
  
      >>> print(network.nodes)
      >>> [{'id': 'bottom', 'x': '0', 'y': '-36.60563691113593'},
      >>>  {'id': 'right', 'x': '36.60563691113593', 'y': '0'},
      >>>  {'id': 'top', 'x': '0', 'y': '36.60563691113593'},
      >>>  {'id': 'left', 'x': '-36.60563691113593', 'y': '0'}]
  
  
      >>> print(network.edges)
      >>> [
      >>>     {'id': 'bottom',
      >>>      'type': 'edgeType',
      >>>      'from': 'bottom',
      >>>      'to': 'right',
      >>>      'length': '57.5',
      >>>      'shape': '0.00,-36.61 1.47,-36.58 2.95,-36.49 4.41,-36.34 '
      >>>               '5.87,-36.13 7.32,-35.87 8.76,-35.54 10.18,-35.16 '
      >>>               '11.59,-34.72 12.98,-34.23 14.35,-33.68 15.69,-33.07 '
      >>>               '17.01,-32.41 18.30,-31.70 19.56,-30.94 20.79,-30.13 '
      >>>               '21.99,-29.26 23.15,-28.35 24.27,-27.40 25.36,-26.40 '
      >>>               '26.40,-25.36 27.40,-24.27 28.35,-23.15 29.26,-21.99 '
      >>>               '30.13,-20.79 30.94,-19.56 31.70,-18.30 32.41,-17.01 '
      >>>               '33.07,-15.69 33.68,-14.35 34.23,-12.98 34.72,-11.59 '
      >>>               '35.16,-10.18 35.54,-8.76 35.87,-7.32 36.13,-5.87 '
      >>>               '36.34,-4.41 36.49,-2.95 36.58,-1.47 36.61,0.00'
      >>>     },
      >>>     {'id': 'right',
      >>>      'type': 'edgeType',
      >>>      'from': 'right',
      >>>      'to': 'top',
      >>>      'length': '57.5',
      >>>      'shape': '36.61,0.00 36.58,1.47 36.49,2.95 36.34,4.41 36.13,5.87 '
      >>>               '35.87,7.32 35.54,8.76 35.16,10.18 34.72,11.59 '
      >>>               '34.23,12.98 33.68,14.35 33.07,15.69 32.41,17.01 '
      >>>               '31.70,18.30 30.94,19.56 30.13,20.79 29.26,21.99 '
      >>>               '28.35,23.15 27.40,24.27 26.40,25.36 25.36,26.40 '
      >>>               '24.27,27.40 23.15,28.35 21.99,29.26 20.79,30.13 '
      >>>               '19.56,30.94 18.30,31.70 17.01,32.41 15.69,33.07 '
      >>>               '14.35,33.68 12.98,34.23 11.59,34.72 10.18,35.16 '
      >>>               '8.76,35.54 7.32,35.87 5.87,36.13 4.41,36.34 2.95,36.49 '
      >>>               '1.47,36.58 0.00,36.61'
      >>>     },
      >>>     {'id': 'top',
      >>>      'type': 'edgeType',
      >>>      'from': 'top',
      >>>      'to': 'left',
      >>>      'length': '57.5',
      >>>      'shape': '0.00,36.61 -1.47,36.58 -2.95,36.49 -4.41,36.34 '
      >>>               '-5.87,36.13 -7.32,35.87 -8.76,35.54 -10.18,35.16 '
      >>>               '-11.59,34.72 -12.98,34.23 -14.35,33.68 -15.69,33.07 '
      >>>               '-17.01,32.41 -18.30,31.70 -19.56,30.94 -20.79,30.13 '
      >>>               '-21.99,29.26 -23.15,28.35 -24.27,27.40 -25.36,26.40 '
      >>>               '-26.40,25.36 -27.40,24.27 -28.35,23.15 -29.26,21.99 '
      >>>               '-30.13,20.79 -30.94,19.56 -31.70,18.30 -32.41,17.01 '
      >>>               '-33.07,15.69 -33.68,14.35 -34.23,12.98 -34.72,11.59 '
      >>>               '-35.16,10.18 -35.54,8.76 -35.87,7.32 -36.13,5.87 '
      >>>               '-36.34,4.41 -36.49,2.95 -36.58,1.47 -36.61,0.00'
      >>>     },
      >>>     {'id': 'left',
      >>>      'type': 'edgeType',
      >>>      'from': 'left',
      >>>      'to': 'bottom',
      >>>      'length': '57.5',
      >>>      'shape': '-36.61,0.00 -36.58,-1.47 -36.49,-2.95 -36.34,-4.41 '
      >>>               '-36.13,-5.87 -35.87,-7.32 -35.54,-8.76 -35.16,-10.18 '
      >>>               '-34.72,-11.59 -34.23,-12.98 -33.68,-14.35 '
      >>>               '-33.07,-15.69 -32.41,-17.01 -31.70,-18.30 '
      >>>               '-30.94,-19.56 -30.13,-20.79 -29.26,-21.99 '
      >>>               '-28.35,-23.15 -27.40,-24.27 -26.40,-25.36 '
      >>>               '-25.36,-26.40 -24.27,-27.40 -23.15,-28.35 '
      >>>               '-21.99,-29.26 -20.79,-30.13 -19.56,-30.94 '
      >>>               '-18.30,-31.70 -17.01,-32.41 -15.69,-33.07 '
      >>>               '-14.35,-33.68 -12.98,-34.23 -11.59,-34.72 '
      >>>               '-10.18,-35.16 -8.76,-35.54 -7.32,-35.87 -5.87,-36.13 '
      >>>               '-4.41,-36.34 -2.95,-36.49 -1.47,-36.58 -0.00,-36.61'
      >>>     }
      >>> ]
  
  
      >>> print(network.types)
      >>> [{'id': 'edgeType', 'numLanes': '1', 'speed': '30'}]
  
      >>> print(network.connections)
      >>> None
  
      >>> print(network.routes)
      >>> {
      >>>     'top': ['top', 'left', 'bottom', 'right'],
      >>>     'left': ['left', 'bottom', 'right', 'top'],
      >>>     'bottom': ['bottom', 'right', 'top', 'left'],
      >>>     'right': ['right', 'top', 'left', 'bottom']
      >>> }
  
  
      >>> print(network.edge_starts)
      >>> [('bottom', 0), ('right', 57.5), ('top', 115.0), ('left', 172.5)]
  
      Finally, the ring network does not contain any junctions or intersections,
      and as a result the `internal_edge_starts` and `intersection_edge_starts`
      attributes are both set to None. For an example of a network with junctions
      and intersections, please refer to: flow/networks/figure_eight.py.
  
      >>> print(network.internal_edge_starts)
      >>> [(':', -1)]
  
      >>> print(network.intersection_edge_starts)
      >>> []
      """
  
      def __init__(self,
                   name,
                   vehicles,
                   net_params,
                   initial_config=InitialConfig(),
                   traffic_lights=TrafficLightParams(),
                   pedestrians=None):
          """Instantiate the base network class.
  
          Attributes
          ----------
          name : str
              A tag associated with the network
          vehicles : flow.core.params.VehicleParams
              see flow/core/params.py
          net_params : flow.core.params.NetParams
              see flow/core/params.py
          initial_config : flow.core.params.InitialConfig
              see flow/core/params.py
          traffic_lights : flow.core.params.TrafficLightParams
              see flow/core/params.py
          """
          self.orig_name = name  # To avoid repeated concatenation upon reset
          self.name = name + time.strftime('_%Y%m%d-%H%M%S') + str(time.time())
  
          self.vehicles = vehicles
          self.net_params = net_params
          self.initial_config = initial_config
          self.traffic_lights = traffic_lights
          self.pedestrians = pedestrians
  
          # TODO(@nliu) find a better solution to passing in pedestrian kernel
          # to randomize pedestrian routes
          self.net_params.additional_params["pedestrian_kernel"] = pedestrians
          self.net_params.additional_params["vehicle_kernel"] = vehicles
          # specify routes vehicles and pedestrians can take
          self.routes = self.specify_routes(net_params)
  
          if net_params.template is None and net_params.osm_path is None:
              # specify the attributes of the nodes
              self.nodes = self.specify_nodes(net_params)
              # collect the attributes of each edge
              self.edges = self.specify_edges(net_params)
              # specify the types attributes (default is None)
              self.types = self.specify_types(net_params)
              # specify the connection attributes (default is None)
              self.connections = self.specify_connections(net_params)
  
              self.crossings = self.specify_crossings(net_params)
  
          # this is to be used if file paths other than the the network geometry
          # file is specified
  
          elif type(net_params.template) is dict:
  
              if 'rou' in net_params.template:
                  veh, rou = self._vehicle_infos(net_params.template['rou'])
  
                  vtypes = self._vehicle_type(net_params.template.get('vtype'))
                  cf = self._get_cf_params(vtypes)
                  lc = self._get_lc_params(vtypes)
  
                  # add the vehicle types to the VehicleParams object
                  for t in vtypes:
                      vehicles.add(veh_id=t, car_following_params=cf[t],
                                   lane_change_params=lc[t], num_vehicles=0)
  
                  # add the routes of the vehicles that will be departed later
                  # under the name of the vehicle. This will later be identified
                  # by k.vehicles._add_departed
                  self.routes = rou
  
                  # vehicles to be added with different departure times
                  self.template_vehicles = veh
  
              self.types = None
              self.nodes = None
              self.edges = None
              self.connections = None
  
          # osm_path or template as type str
          else:
              self.nodes = None
              self.edges = None
              self.types = None
              self.connections = None
  
          # optional parameters, used to get positions from some global reference
          self.edge_starts = self.specify_edge_starts()
          self.internal_edge_starts = self.specify_internal_edge_starts()
          self.intersection_edge_starts = []  # this will be deprecated
  
      # TODO: convert to property
      def specify_edge_starts(self):
          """Define edge starts for road sections in the network.
  
          This is meant to provide some global reference frame for the road
          edges in the network.
  
          By default, the edge starts are specified from the network
          configuration file. Note that, the values are arbitrary but do not
          allow the positions of any two edges to overlap, thereby making them
          compatible with all starting position methods for vehicles.
  
          Returns
          -------
          list of (str, float)
              list of edge names and starting positions,
              ex: [(edge0, pos0), (edge1, pos1), ...]
          """
          return None
  
      def randomize_pedestrian_routes(self, pedestrians):
          return None
  
      # TODO: convert to property
      def specify_internal_edge_starts(self):
          """Define the edge starts for internal edge nodes.
  
          This is meant to provide some global reference frame for the internal
          edges in the network.
  
          These edges are the result of finite-length connections between road
          sections. This methods does not need to be specified if "no-internal-
          links" is set to True in net_params.
  
          By default, all internal edge starts are given a position of -1. This
          may be overridden; however, in general we do not worry about internal
          edges and junctions in large networks.
  
          Returns
          -------
          list of (str, float)
              list of internal junction names and starting positions,
              ex: [(internal0, pos0), (internal1, pos1), ...]
          """
          return [(':', -1)]
  
      # TODO: convert to property
      def specify_nodes(self, net_params):
          """Specify the attributes of nodes in the network.
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          list of dict
  
              A list of node attributes (a separate dict for each node). Nodes
              attributes must include:
  
              * id {string} -- name of the node
              * x {float} -- x coordinate of the node
              * y {float} -- y coordinate of the node
  
          Other attributes may also be specified. See:
          http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Node_Descriptions
          """
          raise NotImplementedError
  
  
      def specify_crossings(self, net_params):
          """Specify the attributes of intersection crossings for pedestrians.
  
          Defaults to returning an empty list, indicating no pedestrian crossings
          are to be generated.
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          list of dict
  
              A list of crossing attributes (a separate dict for each crossing).
              Crossing attributes must include:
  
              * node {string} -- name of the interesection/junction
              * edges {string} -- the name of the two edges to be connected by
                the crossing separated by a space in order of the departure and
                arrival edges (ie. to go from edge_1 to edge_2 we pass in the string
                "edge_1 edge_2".
  
          Other attributes may also be specified. See:
          https://sumo.dlr.de/docs/Networks/PlainXML.html#pedestrian_crossings
          """
          return []
  
      # TODO: convert to property
      def specify_edges(self, net_params):
          """Specify the attributes of edges connecting pairs on nodes.
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          list of dict
  
              A list of edges attributes (a separate dict for each edge). Edge
              attributes must include:
  
              * id {string} -- name of the edge
              * from {string} -- name of node the directed edge starts from
              * to {string} -- name of the node the directed edge ends at
  
              In addition, the attributes must contain at least one of the
              following:
  
              * "numLanes" {int} and "speed" {float} -- the number of lanes and
                speed limit of the edge, respectively
              * type {string} -- a type identifier for the edge, which can be
                used if several edges are supposed to possess the same number of
                lanes, speed limits, etc...
  
          Other attributes may also be specified. See:
          http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Edge_Descriptions
          """
          raise NotImplementedError
  
      # TODO: convert to property
      def specify_types(self, net_params):
          """Specify the attributes of various edge types (if any exist).
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          list of dict
              A list of type attributes for specific groups of edges. If none are
              specified, no .typ.xml file is created.
  
          For information on type attributes, see:
          http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Type_Descriptions
          """
          return None
  
      # TODO: convert to property
      def specify_connections(self, net_params):
          """Specify the attributes of connections.
  
          These attributes are used to describe how any specific node's incoming
          and outgoing edges/lane pairs are connected. If no connections are
          specified, sumo generates default connections.
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          list of dict
              A list of connection attributes. If none are specified, no .con.xml
              file is created.
  
          For information on type attributes, see:
          http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Connection_Descriptions
          """
          return None
  
      # TODO: convert to property
      def specify_routes(self, net_params):
          """Specify the routes vehicles can take starting from any edge.
  
          Routes can be specified in one of three ways:
  
          * In this case of deterministic routes (as is the case in the ring road
            network), the routes can be specified as dictionary where the key
            element represents the starting edge and the element is a single list
            of edges the vehicle must traverse, with the first edge corresponding
            to the edge the vehicle begins on. Note that the edges must be
            connected for the route to be valid.
  
            For example (from flow/networks/ring.py):
  
            >>> def specify_routes(self, net_params):
            >>>     return {
            >>>         "top": ["top", "left", "bottom", "right"],
            >>>         "left": ["left", "bottom", "right", "top"],
            >>>         "bottom": ["bottom", "right", "top", "left"],
            >>>         "right": ["right", "top", "left", "bottom"]
            >>>     }
  
          * Alternatively, if the routes are meant to be stochastic, each element
            can consist of a list of (route, probability) tuples, where the first
            element in the tuple is one of the routes a vehicle can take from a
            specific starting edge, and the second element is the probability
            that vehicles will choose that route. Note that, in this case, the
            sum of probability values for each dictionary key must sum up to one.
  
            For example, if we were to imagine the edge "right" in the ring road
            examples where split into two edges, "right_0" and "right_1", the
            routes for vehicles in this network in the probabilistic setting can
            be:
  
            >>> def specify_routes(self, net_params):
            >>>     return {
            >>>         "top": [
            >>>             (["top", "left", "bottom", "right_0"], 0.9),
            >>>             (["top", "left", "bottom", "right_1"], 0.1)
            >>>         ],
            >>>         "left": [
            >>>             (["left", "bottom", "right_0", "top"], 0.3),
            >>>             (["left", "bottom", "right_1", "top"], 0.7)
            >>>         ],
            >>>         "bottom": [
            >>>             (["bottom", "right_0", "top", "left"], 0.5),
            >>>             (["bottom", "right_1", "top", "left"], 0.5)
            >>>         ],
            >>>         "right_0": [
            >>>             (["right_0", "top", "left", "bottom"], 1)
            >>>         ],
            >>>         "right_1": [
            >>>             (["right_1", "top", "left", "bottom"], 1)
            >>>         ]
            >>>     }
  
          * Finally, if you would like to assign a specific starting edge and
            route to a vehicle with a specific ID, you can do so by adding a
            element into the dictionary whose key is the name of the vehicle and
            whose content is the list of edges the vehicle is meant to traverse
            as soon as it is introduced to the network.
  
            As an example, assume we have 4 vehicles named 'human_0', 'human_1',
            'human_2', and 'human_3' in the original ring road. Then, an
            appropriate definition of the routes may look something like:
  
            >>> def specify_routes(self, net_params):
            >>>     return {
            >>>         "human_0": ["top", "left", "bottom", "right"],
            >>>         "human_1": ["left", "bottom", "right", "top"],
            >>>         "human_2": ["bottom", "right", "top", "left"],
            >>>         "human_3": ["right", "top", "left", "bottom"]
            >>>     }
  
            **Note**: This feature is experimental, and may not always work as
            expected (for example if the starting positions and routes of a
            specific vehicle do not match).
  
          The `define_routes` method is optional, and need not be defined. If it
          is not implemented, vehicles that enter a network are assigned routes
          consisting solely on their current edges, and exit the network once
          they reach the end of their edge. Routes, however, can be reassigned
          during simulation via a routing controller (see
          flow/controllers/routing_controllers.py).
  
          Parameters
          ----------
          net_params : flow.core.params.NetParams
              see flow/core/params.py
  
          Returns
          -------
          dict
              Key = name of the starting edge
              Element = list of edges a vehicle starting from this edge must
              traverse *OR* a list of (route, probability) tuples for each
              starting edge
          """
          return None
  
      @staticmethod
      def gen_custom_start_pos(cls, net_params, initial_config, num_vehicles):
          """Generate a user defined set of starting positions.
  
          Parameters
          ----------
          cls : flow.core.kernel.network.BaseKernelNetwork
              flow network kernel, with all the relevant methods implemented
          net_params : flow.core.params.NetParams
              network-specific parameters
          initial_config : flow.core.params.InitialConfig
              see flow/core/params.py
          num_vehicles : int
              number of vehicles to be placed on the network
  
          Returns
          -------
          list of tuple (float, float)
              list of start positions [(edge0, pos0), (edge1, pos1), ...]
          list of int
              list of start lanes
          list of float
              list of start speeds
          """
          raise NotImplementedError
  
      @staticmethod
      def _vehicle_infos(file_names):
          """Import of vehicle from a configuration file.
  
          This is a utility function for computing vehicle information. It
          imports a network configuration file, and returns the information on
          the vehicle and add it into the Vehicle object.
  
          Parameters
          ----------
          file_names : list of str
              path to the xml file to load
  
          Returns
          -------
          dict <dict>
  
              * Key = id of the vehicle
              * Element = dict of departure speed, vehicle type, depart Position,
                depart edges
          """
          # this is meant to deal with the case that there is only one rou file
          if isinstance(file_names, str):
              file_names = [file_names]
  
          vehicle_data = dict()
          routes_data = dict()
          type_data = defaultdict(int)
  
          for filename in file_names:
              # import the .net.xml file containing all edge/type data
              parser = etree.XMLParser(recover=True)
              tree = ElementTree.parse(filename, parser=parser)
              root = tree.getroot()
  
              # collect the departure properties and routes and vehicles whose
              # properties are instantiated within the .rou.xml file. This will
              # only apply if such data is within the file (it is not implemented
              # by networks in Flow).
              for vehicle in root.findall('vehicle'):
                  # collect the edges the vehicle is meant to traverse
                  route = vehicle.find('route')
                  route_edges = route.attrib["edges"].split(' ')
  
                  # collect the names of each vehicle type and number of vehicles
                  # of each type
                  type_vehicle = vehicle.attrib['type']
                  type_data[type_vehicle] += 1
  
                  vehicle_data[vehicle.attrib['id']] = {
                      'departSpeed': vehicle.attrib['departSpeed'],
                      'depart': vehicle.attrib['depart'],
                      'typeID': type_vehicle,
                      'departPos': vehicle.attrib['departPos'],
                  }
  
                  routes_data[vehicle.attrib['id']] = route_edges
  
              # collect the edges the vehicle is meant to traverse for the given
              # sets of routes that are not associated with individual vehicles
              for route in root.findall('route'):
                  route_edges = route.attrib["edges"].split(' ')
                  routes_data[route.attrib['id']] = route_edges
  
          return vehicle_data, routes_data
  
      @staticmethod
      def _vehicle_type(filename):
          """Import vehicle type data from a *.add.xml file.
  
          This is a utility function for outputting all the type of vehicle.
  
          Parameters
          ----------
          filename : str
              path to the vtypes.add.xml file to load
  
          Returns
          -------
          dict or None
              the key is the vehicle_type id and the value is a dict we've type
              of the vehicle, depart edges, depart Speed, departPos. If no
              filename is provided, this method returns None as well.
          """
          if filename is None:
              return None
  
          parser = etree.XMLParser(recover=True)
          tree = ElementTree.parse(filename, parser=parser)
  
          root = tree.getroot()
          veh_type = {}
  
          # this hack is meant to support the LuST network and Flow networks
          root = [root] if len(root.findall('vTypeDistribution')) == 0 \
              else root.findall('vTypeDistribution')
  
          for r in root:
              for vtype in r.findall('vType'):
                  # TODO: make for everything
                  veh_type[vtype.attrib['id']] = {
                      'vClass': vtype.attrib.get('vClass', DEFAULT_VCLASS),
                      'accel': vtype.attrib['accel'],
                      'decel': vtype.attrib['decel'],
                      'sigma': vtype.attrib['sigma'],
                      'length': vtype.attrib.get('length', DEFAULT_LENGTH),
                      'minGap': vtype.attrib['minGap'],
                      'maxSpeed': vtype.attrib['maxSpeed'],
                      'probability': vtype.attrib.get(
                          'probability', DEFAULT_PROBABILITY),
                      'speedDev': vtype.attrib['speedDev']
                  }
  
          return veh_type
  
      @staticmethod
      def _get_cf_params(vtypes):
          """Return the car-following sumo params from vtypes."""
          ret = {}
          for typ in vtypes:
              # TODO: add vClass
              ret[typ] = SumoCarFollowingParams(
                  speed_mode='all_checks',
                  accel=float(vtypes[typ]['accel']),
                  decel=float(vtypes[typ]['decel']),
                  sigma=float(vtypes[typ]['sigma']),
                  length=float(vtypes[typ]['length']),
                  min_gap=float(vtypes[typ]['minGap']),
                  max_speed=float(vtypes[typ]['maxSpeed']),
                  probability=float(vtypes[typ]['probability']),
                  speed_dev=float(vtypes[typ]['speedDev'])
              )
  
          return ret
  
      @staticmethod
      def _get_lc_params(vtypes):
          """Return the lane change sumo params from vtypes."""
          ret = {}
          for typ in vtypes:
              ret[typ] = SumoLaneChangeParams(lane_change_mode=1621)
  
          return ret
  
      def __str__(self):
          """Return the name of the network and the number of vehicles."""
          return 'Network ' + self.name + ' with ' + \
                 str(self.vehicles.num_vehicles) + ' vehicles.'
  