# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
# Copyright (C) 2016-2026 German Aerospace Center (DLR) and others.
# hybridPY module
# Copyright (C) 2012-2026 University of Bologna - DICAM, Technical University of Munich
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0/
# This Source Code may also be made available under the following Secondary
# Licenses when the conditions for such availability set forth in the Eclipse
# Public License 2.0 are satisfied: GNU General Public License, version 2
# or later which is available at
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later

# @file    virtualpop.py
# @author  Joerg Schweizer
# @author  Fabian Schuhmann
# @author  Ngoc An Nguyen
# @author  Cristian Poliziani
# @date    2012

import os
import sys
import numpy as np
from numpy import random
import agilepy.lib_base.classman as cm
import agilepy.lib_base.arrayman as am
import agilepy.lib_base.xmlman as xm
from agilepy.lib_base.misc import random_choice, get_inversemap
from agilepy.lib_base.processes import Process

# from coremodules.modules_common import *
from coremodules.network.network import SumoIdsConf, MODES
from coremodules.network import routing
from coremodules.simulation import results as res
from .demandbase import *
from . import virtualpop_results as res


GENDERS = {'male': 0, 'female': 1, 'unknown': -1}

OCCUPATIONS = {'unknown': -1,
               'worker': 1,
               'student': 2,
               'employee': 3,
               'public employee': 4,
               'selfemployed': 5,
               'pensioneer': 6,
               'other': 7
               }


class Activities(am.ArrayObjman):
    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
    def __init__(self, ident, virtualpop, **kwargs):

        self._init_objman(ident=ident, parent=virtualpop, name='Activities',
                          info='Activity database of persons contains type, time, duration and location of activities.',
                          version=0.0,
                          **kwargs)

        self._init_attributes()

    def _init_attributes(self):

        # activy types now in demand
        activitytypes = self.parent.get_demand().activitytypes
        self.add_col(am.IdsArrayConf('ids_activitytype', activitytypes,
                                     groupnames=['parameters'],
                                     choices=activitytypes.names.get_indexmap(),
                                     name='Type',
                                     info='Type of activity performed during the stop.',
                                     # xmltag = 'actType',
                                     # xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                     ))

        # attention, this may cause trouble durung init if
        # facilities are not yet initialized
        self.add_col(am.IdsArrayConf('ids_facility', self.parent.get_scenario().landuse.facilities,
                                     groupnames=['parameters'],
                                     name='ID fac.',
                                     info='Facility ID where activity takes place.',
                                     # activitytype = 'home',
                                     ))

        # self.add_col(am.ArrayConf( 'descriptions', '',
        #                            dtype = np.object_,
        #                            perm='rw',
        #                            is_index = True,
        #                            name = 'Description',
        #                            info = 'Description of activity.',
        #                            ))

        # self.add_col(am.IdlistsArrayConf( 'ids_landusetypes', self.parent.get_landuse().landusetypes,
        #                                name = 'Landuse types',
        #                                info = "Landuse type IDs, eher this activity type can take place.",
        #                                ))

        self.add_col(am.ArrayConf('hours_begin_earliest', 0.0,
                                  dtype=np.float32,
                                  groupnames=['parameters'],
                                  perm='rw',
                                  name='Earliest hour begin',
                                  unit='h',
                                  info='Earliest hour when this activity can begin.',
                                  ))

        self.add_col(am.ArrayConf('hours_begin_latest', 1.0,
                                  dtype=np.float32,
                                  groupnames=['parameters'],
                                  perm='rw',
                                  name='Latest begin hour',
                                  unit='h',
                                  info='Latest hour when this activity can begin.',
                                  ))

        self.add_col(am.ArrayConf('durations_min', 6.0,
                                  dtype=np.float32,
                                  groupnames=['parameters'],
                                  perm='rw',
                                  name='Min. Duration',
                                  unit='h',
                                  info='Minimum activity duration for a person within a day.',
                                  ))

        self.add_col(am.ArrayConf('durations_max', 8.0,
                                  dtype=np.float32,
                                  groupnames=['parameters'],
                                  perm='rw',
                                  name='Max. Duration',
                                  unit='h',
                                  info='Maximum activity duration for a person within a day.',
                                  ))

    def get_hours_end_earliest(self, ids):
        return self.hours_begin_earliest[ids]+self.durations_min[ids]

    def get_hours_end_latest(self, ids):
        return self.hours_begin_latest[ids]+self.durations_max[ids]

    def get_durations(self, ids, pdf='unit'):
        durations = np.zeros(len(ids), dtype=np.float32)
        i = 0
        for time_start, time_end in zip(
                np.array(self.durations_min[ids]*3600, dtype=np.int32),
                np.array(self.durations_max[ids]*3600, dtype=np.int32)):

            durations[i] = np.random.randint(time_start, time_end, 1)
            i += 1
        return durations

    def get_times_end(self, ids, pdf='unit'):
        """
        Returns an array with activity ending time for the
        given activity IDs. 
        The ending time is calculated by drawing random samples
        from the departure interval.
        The random samples are drawn according to the given probability
        density function, pdf.

        Input arguments:
            ids: integer array with activity IDs
            pdf: probability density function 'unit'|'normal'

        Returned arguments:
            times_end: integer array with departure times
        """
        times_end = np.zeros(len(ids), dtype=np.float32)
        i = 0
        for time_start, time_end in zip(
                np.array(self.get_hours_end_earliest(ids)*3600, dtype=np.int32),
                np.array(self.get_hours_end_latest(ids)*3600, dtype=np.int32)):

            times_end[i] = np.random.randint(time_start, time_end, 1)
            i += 1

        return times_end

    def get_times_begin(self, ids, pdf='unit'):
        """
        Returns an array with beginning time for the
        given activity IDs. 
        The begin time is calculated by drawing random samples
        from the departure interval.
        The random samples are drawn according to the given probability
        density function, pdf.

        Input arguments:
            ids: integer array with activity IDs
            pdf: probability density function 'unit'|'normal'

        Returned arguments:
            times_begin: integer array with departure times
        """
        times_begin = np.zeros(len(ids), dtype=np.float32)
        i = 0
        for time_start, time_end in zip(
                np.array(self.get_hours_begin_earliest(ids)*3600, dtype=np.int32),
                np.array(self.get_hours_begin_latest(ids)*3600, dtype=np.int32)):

            times_begin[i] = np.random.randint(time_start, time_end, 1)
            i += 1

        return times_begin

    def add_activities(self, ids_activitytype, ids_facility,
                       hours_begin_earliest=None,
                       hours_begin_latest=None,
                       durations_min=None,
                       durations_max=None):

        n_pers = len(ids_activitytype)
        activitytypes = self.ids_activitytype.get_linktab()

        if hours_begin_earliest is None:
            # np.ones(n_pers, dtype = np.float32)*
            hours_begin_earliest = activitytypes.hours_begin_earliest[ids_activitytype]

        if hours_begin_latest is None:
            hours_begin_latest = activitytypes.hours_begin_latest[ids_activitytype]

        if durations_min is None:
            durations_min = activitytypes.durations_min[ids_activitytype]

        if durations_max is None:
            durations_max = activitytypes.durations_max[ids_activitytype]

        ids = self.add_rows(n=n_pers,
                            ids_activitytype=ids_activitytype,
                            ids_facility=ids_facility,
                            hours_begin_earliest=hours_begin_earliest,
                            hours_begin_latest=hours_begin_latest,
                            durations_min=durations_min,
                            durations_max=durations_max,
                            )

        return ids


class IndividualAutos(am.ArrayObjman):

    def __init__(self, ident, population, **kwargs):
        # print 'individualvehicle vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
        self._init_objman(ident=ident,
                          parent=population,
                          name='Indiv. Autos',
                          info='Individual auto database. These are privately owned autos.',
                          **kwargs)

        self._init_attributes()
        self._init_constants()

    def _init_constants(self):

        self.do_not_save_attrs(['mode', 'mode_prefix',
                                '_edges', '_lanes', '_individualvehicle', '_ids_vtype_sumo', '_ids_edge_sumo',
                                '_id_mode', '_get_laneid_allowed', '_get_sumoinfo_from_id_lane',
                                '_space_access', '_parking', '_time_after_unboarding',
                                ])

    def def_mode(self):
        self.mode = 'passenger'
        self.mode_prefix = 'iauto'

    def _init_attributes(self):
        vtypes = self.parent.get_demand().vtypes
        self.def_mode()

        ids_vtype = vtypes.select_by_mode(mode=self.mode)

        self.add(cm.AttrConf('space_access', 0.5,
                             groupnames=['options'],
                             perm='rw',
                             name='Space access',
                             unit='m',
                             info='Space to access vehicles at parkings. This is typically less than the vehicle length.',
                             ))

        self.add(cm.AttrConf('time_after_unboarding', 5,
                             groupnames=['options'],
                             perm='rw',
                             name='time after unboarding',
                             unit='s',
                             info='Time the vehicle waits before disappearing after unboarding.',
                             ))

        self.add_col(am.IdsArrayConf('ids_vtype', vtypes,
                                     id_default=ids_vtype[0],
                                     groupnames=['state'],
                                     name='Veh. type',
                                     info='Vehicle type.',
                                     # xmltag = 'type',
                                     ))

        self.add_col(am.IdsArrayConf('ids_person', self.parent,
                                     groupnames=['state'],
                                     name='ID person',
                                     info='ID of person who ownes the vehicle.',
                                     ))

        self.add_col(am.ArrayConf('times_exec', 0.0,
                                  name='Exec time',
                                  info='Total route execution time from simulation run of last plan.',
                                  unit='s',
                                  ))

    def get_virtualpop(self):
        return self.parent

    def get_ids_veh_pop(self):
        """
        To be overridden by other individual vehicle types.
        """
        return self.get_virtualpop().ids_iauto

    def get_share(self, is_abs=False):
        n_veh = len(self)
        if is_abs:
            return n_veh
        else:
            return float(n_veh)/float(len(self.get_virtualpop()))

    def get_stagetable(self):
        return self.parent.get_plans().get_stagetable('autorides')

    def get_demand(self):
        return self.parent.parent

    def clear_vehicles(self):
        self.get_ids_veh_pop()[self.ids_person.get_value()] = -1
        self.clear()

    def assign_to_persons(self, ids_person):
        # print 'assign_to_persons',len(ids_person),self.mode
        # self.clear_vehicles()
        # ids_person_noveh = set(ids_person).difference(set(self.ids_person))
        n_new = len(ids_person)
        #
        # this call is selecting a veh id aof the specific mode
        # according to its share within this mode
        ids_vtype = self.get_demand().vtypes.generate_vtypes_for_mode(n_new, mode=self.mode)

        # print '  ids_vtype',ids_vtype
        ids_veh = self.add_rows(n=n_new,
                                ids_person=ids_person,
                                ids_vtype=ids_vtype,
                                )
        self.get_ids_veh_pop()[ids_person] = ids_veh
        return ids_veh

    def get_vtypes(self):
        """
        Returns a set with all used vehicle types.
        """
        # print 'Vehicles_individual.get_vtypes',self.cols.vtype
        return set(self.ids_vtype.get_value())

    def get_id_veh_xml(self, id_veh, id_stage):
        return self.mode_prefix + '.%s.%s' % (id_veh, id_stage)

    def get_id_line_xml(self, id_veh):
        return self.mode_prefix + '.%s' % (id_veh)

    def get_id_from_id_sumo(self, id_veh_sumo):
        # print 'get_id_from_id_sumo',id_veh_sumo,id_veh_sumo.split('.'),self.mode_prefix
        if len(id_veh_sumo.split('.')) == 3:
            prefix, id_veh, id_stage = id_veh_sumo.split('.')
            if prefix == self.mode_prefix:
                return int(id_veh)
            else:
                return -1
        return -1

    def get_info_from_id_sumo(self, id_veh_sumo):
        if len(id_veh_sumo.split('.')) == 3:
            prefix, id_veh, id_stage = id_veh_sumo.split('.')
            if prefix == self.mode_prefix:
                return int(id_veh), int(id_stage)
            else:
                return -1, -1
        return -1, -1

    # def append_ride(self, id_veh, id_ride):
    #    ids_ride = self.ids_rides[id_veh]
    #    if ids_ride  is None:
    #        self.ids_rides[id_veh] = [id_ride]
    #    else:
    #        ids_ride.append(id_ride)

    def prepare_write_xml(self, is_route=True, is_plain=False):
        """
        Prepare xml export. Must return export function.
        """
        virtualpop = self.get_virtualpop()
        scenario = virtualpop.get_scenario()
        # plans = virtualpop.get_plans()

        self._rides = self.get_stagetable()
        self._edges = scenario.net.edges
        self._lanes = scenario.net.lanes
        # self._individualvehicle = virtualpop.get_ibikes()
        self._ids_vtype_sumo = scenario.demand.vtypes.ids_sumo
        self._ids_edge_sumo = self._edges.ids_sumo
        self._id_mode = scenario.net.modes.get_id_mode(self.mode)
        self._get_laneid_allowed = self._edges.get_laneid_allowed
        self._get_sumoinfo_from_id_lane = scenario.net.lanes.get_sumoinfo_from_id_lane
        self._space_access = self.space_access.get_value()
        # self._time_veh_wait_after_stop = 3600
        self._parking = virtualpop.get_landuse().parking
        self._time_after_unboarding = self.time_after_unboarding.get_value()

        # TODO: here we'd need also a case with is_plain and is_route
        if is_plain:
            return self.write_xml_no_line

        elif is_route:
            return self.write_xml
        else:
            return self.write_xml_noroute

    def get_id_veh(self, id_stage):
        return self._rides.ids_iauto[id_stage]

    def write_xml_no_line(self,  fd, id_stage, time_begin, indent=2):
        # same as write_xml, but without id_line, route and stops

        rides = self._rides
        # lanes = self._lanes
        parking = self._parking

        id_veh = self.get_id_veh(id_stage)
        # individualvehicle = self._iveh
        id_vtype = self.ids_vtype[id_veh]

        id_parking_from = rides.ids_parking_from[id_stage]
        id_lane_from = parking.ids_lane[id_parking_from]
        # laneindex_from =  self._lanes.indexes[id_lane_from]
        pos_from = parking.positions[id_parking_from]

        id_parking_to = rides.ids_parking_to[id_stage]
        id_lane_to = parking.ids_lane[id_parking_to]
        # laneindex_to =  self._lanes.indexes[id_lane_to]
        pos_to = parking.positions[id_parking_to]

        # write unique veh ID to prevent confusion with other veh declarations
        fd.write(xm.start('vehicle id="%s"' % self.get_id_veh_xml(id_veh, id_stage), indent+2))

        # get start time of first stage of the plan
        # id_plan = rides.ids_plan[id_stage]
        # stages0, id_stage0 = self.get_plans().stagelists[id_plan][0]

        # this is the time when the vehicle appers in the scenario
        fd.write(xm.num('depart', '%.d' % max(1.0, rides.times_init[id_stage])))
        # fd.write(xm.num('depart', '%.d'%stages0.times_start[id_stage0]))

        fd.write(xm.num('type', self._ids_vtype_sumo[id_vtype]))

        # no line
        # fd.write(xm.num('line', self.get_id_line_xml(id_veh) ))

        fd.write(xm.num('from', self._ids_edge_sumo[rides.ids_edges[id_stage]][0]))
        fd.write(xm.num('to', self._ids_edge_sumo[rides.ids_edges[id_stage]][-1]))

        fd.write(xm.num('departPos', pos_from))
        fd.write(xm.num('departLane', self._lanes.indexes[id_lane_from]))

        fd.write(xm.stop())

        # write route
        fd.write(xm.start('route', indent+4))
        # print '  ids_edges',rides.ids_edges[id_stage]
        # print '  ids_sumo',self._ids_edge_sumo[rides.ids_edges[id_stage]]
        fd.write(xm.arr('edges', self._ids_edge_sumo[rides.ids_edges[id_stage]]))
        fd.write(xm.stopit())

        # no stops

        fd.write(xm.end('vehicle', indent+2))

    def write_xml(self,  fd, id_stage, time_begin, indent=2):

        # TODO: actually this should go in individualvehicle

        rides = self._rides

        parking = self._parking

        id_veh = self.get_id_veh(id_stage)

        id_vtype = self.ids_vtype[id_veh]

        id_parking_from = rides.ids_parking_from[id_stage]
        id_lane_from = parking.ids_lane[id_parking_from]

        pos_from = parking.positions[id_parking_from]

        id_parking_to = rides.ids_parking_to[id_stage]
        id_lane_to = parking.ids_lane[id_parking_to]

        pos_to = parking.positions[id_parking_to]

        # write unique veh ID to prevent confusion with other veh declarations
        fd.write(xm.start('vehicle id="%s"' % self.get_id_veh_xml(id_veh, id_stage), indent+2))

        # get start time of first stage of the plan
        # id_plan = rides.ids_plan[id_stage]
        # stages0, id_stage0 = self.get_plans().stagelists[id_plan][0]

        # this is the time when the vehicle appers in the scenario

        # negative and zero is throwing an error
        fd.write(xm.num('depart', '%.d' % max(1.0, rides.times_init[id_stage])))
        # fd.write(xm.num('depart', '%.d'%stages0.times_start[id_stage0]))

        fd.write(xm.num('type', self._ids_vtype_sumo[id_vtype]))
        fd.write(xm.num('line', self.get_id_line_xml(id_veh)))
        fd.write(xm.num('departPos', pos_from))
        fd.write(xm.num('departLane', self._lanes.indexes[id_lane_from]))

        fd.write(xm.stop())

        # write route
        fd.write(xm.start('route', indent+4))
        # print '  edgeindex[ids_edge]',edgeindex[ids_edge]
        fd.write(xm.arr('edges', self._ids_edge_sumo[rides.ids_edges[id_stage]]))

        # does not seem to have an effect, always starts at base????
        # fd.write(xm.num('departPos', pos_from))
        # fd.write(xm.num('departLane', laneindex_from ))
        fd.write(xm.stopit())

        # write depart stop
        fd.write(xm.start('stop', indent+4))
        # id_lane = self._lanes.ids_edge[id_lane_from]
        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane_from)))
        # in 0.31 the vehicle will wait until after this duration
        # so it will be removed unless it will get a timeout function
        # fd.write(xm.num('duration', time_veh_wait_after_stop))
        fd.write(xm.num('startPos', pos_from - parking.lengths[id_parking_from]))
        fd.write(xm.num('endPos', pos_from))
        fd.write(xm.num('triggered', "True"))

        # chrashes with parking=True in 0.30!
        # however if not parked the vhcle is blocking the traffic
        # while waiting: workaround: delay departure to be shure that person already arrived

        fd.write(xm.num('parking', "True"))  # in windows 0.30 parked vehicles do not depart!!
        # fd.write(xm.num('parking', "False"))
        fd.write(xm.stopit())

        # write arrival stop
        fd.write(xm.start('stop', indent+4))
        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane_to)))
        fd.write(xm.num('duration', self._time_after_unboarding))  # for unboarding only
        fd.write(xm.num('startPos', pos_to - parking.lengths[id_parking_to]))
        fd.write(xm.num('endPos', pos_to))
        # fd.write(xm.num('triggered', "True"))
        fd.write(xm.stopit())

        fd.write(xm.end('vehicle', indent+2))

    def write_xml_noroute(self,  fd, id_stage, time_begin, indent=2):

        # TODO: actually this should go in individualvehicle
        # time_veh_wait_after_stop = 3600
        # plans = self.get_plans()
        # walkstages = plans.get_stagetable('walks')
        # rides = plans.get_stagetable('autorides')
        # activitystages = plans.get_stagetable('activities')

        rides = self._rides
        # lanes = self._lanes
        parking = self._parking
        # net = self.get_net()
        # lanes = net.lanes
        # edges = net.edges
        # ind_ride = rides.get_inds(id_stage)
        id_veh = self.get_id_veh(id_stage)
        # individualvehicle = self._iveh
        id_vtype = self.ids_vtype[id_veh]

        # id_veh_ride,
        #                            ids_vtypes_iveh[id_veh],
        #                            ids_edges_rides_arr[ind_ride],
        #                            ids_parking_from_rides_arr[ind_ride],
        #                            ids_parking_to_rides_arr[ind_ride],

        id_parking_from = rides.ids_parking_from[id_stage]
        id_lane_from = parking.ids_lane[id_parking_from]
        # laneindex_from =  self._lanes.indexes[id_lane_from]
        pos_from = parking.positions[id_parking_from]

        id_parking_to = rides.ids_parking_to[id_stage]
        id_lane_to = parking.ids_lane[id_parking_to]
        # laneindex_to =  self._lanes.indexes[id_lane_to]
        pos_to = parking.positions[id_parking_to]

        # write unique veh ID to prevent confusion with other veh declarations
        fd.write(xm.start('trip id="%s"' % self.get_id_veh_xml(id_veh, id_stage), indent+2))

        # get start time of first stage of the plan
        # id_plan = rides.ids_plan[id_stage]
        # stages0, id_stage0 = self.get_plans().stagelists[id_plan][0]

        # this is the time when the vehicle appers in the scenario
        fd.write(xm.num('depart', '%.d' % max(1.0, rides.times_init[id_stage])))
        # fd.write(xm.num('depart', '%.d'%stages0.times_start[id_stage0]))

        fd.write(xm.num('type', self._ids_vtype_sumo[id_vtype]))
        fd.write(xm.num('line', self.get_id_line_xml(id_veh)))

        fd.write(xm.num('from', self._ids_edge_sumo[rides.ids_edges[id_stage]][0]))
        fd.write(xm.num('to', self._ids_edge_sumo[rides.ids_edges[id_stage]][-1]))

        fd.write(xm.num('departPos', pos_from))
        fd.write(xm.num('departLane', self._lanes.indexes[id_lane_from]))

        fd.write(xm.stop())

        # write route
        # fd.write(xm.start('route',indent+4))
        # print '  edgeindex[ids_edge]',edgeindex[ids_edge]
        # fd.write(xm.arr('edges',self._ids_edge_sumo[rides.ids_edges[id_stage]]))

        # does not seem to have an effect, always starts at base????
        # fd.write(xm.num('departPos', pos_from))
        # fd.write(xm.num('departLane', laneindex_from ))
        # fd.write(xm.stopit())

        # write depart stop
        fd.write(xm.start('stop', indent+4))
        # id_lane = self._lanes.ids_edge[id_lane_from]
        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane_from)))
        # in 0.31 the vehicle will wait until after this duration
        # so it will be removed unless it will get a timeout function
        # fd.write(xm.num('duration', time_veh_wait_after_stop))
        fd.write(xm.num('startPos', pos_from - parking.lengths[id_parking_from]))
        fd.write(xm.num('endPos', pos_from))
        fd.write(xm.num('triggered', "True"))

        # chrashes with parking=True in 0.30!
        # however if not parked the vhcle is blocking the traffic
        # while waiting: workaround: delay departure to be shure that person already arrived

        fd.write(xm.num('parking', "True"))  # in windows 0.30 parked vehicles do not depart!!
        # fd.write(xm.num('parking', "False"))
        fd.write(xm.stopit())

        # write arrival stop
        fd.write(xm.start('stop', indent+4))
        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane_to)))
        fd.write(xm.num('duration', self._time_after_unboarding))  # for unboarding only
        fd.write(xm.num('startPos', pos_to - parking.lengths[id_parking_to]))
        fd.write(xm.num('endPos', pos_to))
        # fd.write(xm.num('triggered', "True"))
        fd.write(xm.stopit())

        fd.write(xm.end('trip', indent+2))


class IndividualBikes(IndividualAutos):

    def __init__(self, ident, population, **kwargs):
        # print 'individualvehicle vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
        self._init_objman(ident=ident,
                          parent=population,
                          name='Indiv. Bikes',
                          info='Individual bike database. These are privately owned bikes.',
                          **kwargs)
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self):

        IndividualAutos._init_attributes(self)

    def _init_constants(self):

        self.do_not_save_attrs(['mode', 'mode_prefix',
                                '_edges', '_ids_vtype_sumo', '_ids_edge_sumo',
                                '_id_mode', '_get_laneid_allowed', '_get_sumoinfo_from_id_lane',
                                '_space_access',
                                ])

    def def_mode(self):
        self.mode = 'bicycle'
        self.mode_prefix = 'ibike'

    def get_ids_veh_pop(self):
        """
        To be overridden by other individual vehicle types.
        """
        return self.parent.ids_ibike

    def get_stagetable(self):
        return self.parent.get_plans().get_stagetable('bikerides')

    def get_id_veh(self, id_stage):
        return self._rides.ids_ibike[id_stage]

    def prepare_write_xml(self, is_route=True, is_plain=False):
        """
        Prepare xml export. Must return export function.
        """
        virtualpop = self.get_virtualpop()
        scenario = virtualpop.get_scenario()
        # plans = virtualpop.get_plans()
        self._rides = self.get_stagetable()
        self._edges = scenario.net.edges
        # self._individualvehicle = virtualpop.get_ibikes()
        self._ids_vtype_sumo = scenario.demand.vtypes.ids_sumo
        self._ids_edge_sumo = self._edges.ids_sumo
        self._id_mode = scenario.net.modes.get_id_mode(self.mode)
        self._get_laneid_allowed = self._edges.get_laneid_allowed
        self._get_sumoinfo_from_id_lane = scenario.net.lanes.get_sumoinfo_from_id_lane
        self._space_access = self.space_access.get_value()
        if is_plain:
            return self.write_xml_no_line
        else:
            return self.write_xml

    # def _limit_pos(self,pos,id_edge):

    def write_xml_no_line(self,  fd, id_stage, time_begin, indent=2):
        rides = self._rides
        id_veh = self.get_id_veh(id_stage)
        # print 'write_xml',id_stage, time_begin,self.get_id_veh_xml(id_veh, id_stage)
        # print '  ids_edge_from,ids_edge_to',rides.ids_edge_from[id_stage],rides.ids_edge_to[id_stage],self._get_laneid_allowed( rides.ids_edge_from[id_stage], self._id_mode),self._get_laneid_allowed( rides.ids_edge_to[id_stage], self._id_mode)

        # individualvehicle = self.get_ibikes()
        id_vtype = self.ids_vtype[id_veh]

        # write unique veh ID to prevent confusion with other veh declarations
        fd.write(xm.start('vehicle id="%s"' % self.get_id_veh_xml(id_veh, id_stage), indent+2))

        # get start time of first stage of the plan

        # this is the time when the vehicle appers in the scenario
        # fd.write(xm.num('depart', '%.d'%rides.times_init[id_stage]))
        fd.write(xm.num('depart', '%.d' % max(1.0, rides.times_init[id_stage])))
        fd.write(xm.num('type', self._ids_vtype_sumo[id_vtype]))

        fd.write(xm.num('from', self._ids_edge_sumo[rides.ids_edge_from[id_stage]]))
        fd.write(xm.num('to', self._ids_edge_sumo[rides.ids_edge_to[id_stage]]))
        pos_from = rides.positions_from[id_stage]
        pos_to = rides.positions_to[id_stage]
        fd.write(xm.num('departPos', pos_from))
        fd.write(xm.num('arrivalPos', pos_to))
        fd.write(xm.num('departLane', 'best'))

        fd.write(xm.stop())

        # write route
        fd.write(xm.start('route', indent+4))
        # print '  ids_edges',rides.ids_edges[id_stage]
        # print '  ids_sumo',self._ids_edge_sumo[rides.ids_edges[id_stage]]
        fd.write(xm.arr('edges', self._ids_edge_sumo[rides.ids_edges[id_stage]]))
        fd.write(xm.stopit())

        # no stops written here

        fd.write(xm.end('vehicle', indent+2))

    def write_xml(self,  fd, id_stage, time_begin, indent=2):
        rides = self._rides
        id_veh = self.get_id_veh(id_stage)
        # print 'write_xml',id_stage, time_begin,self.get_id_veh_xml(id_veh, id_stage)
        # print '  ids_edge_from,ids_edge_to',rides.ids_edge_from[id_stage],rides.ids_edge_to[id_stage],self._get_laneid_allowed( rides.ids_edge_from[id_stage], self._id_mode),self._get_laneid_allowed( rides.ids_edge_to[id_stage], self._id_mode)

        id_vtype = self.ids_vtype[id_veh]

        # write unique veh ID to prevent confusion with other veh declarations
        fd.write(xm.start('vehicle id="%s"' % self.get_id_veh_xml(id_veh, id_stage), indent+2))

        # get start time of first stage of the plan
        # id_plan = rides.ids_plan[id_stage]
        # stages0, id_stage0 = self.get_plans().stagelists[id_plan][0]

        # this is the time when the vehicle appers in the scenario

        fd.write(xm.num('depart', '%.d' % max(1.0, rides.times_init[id_stage])))

        # fd.write(xm.num('depart', '%.d'%max(1.0, time_begin)))

        fd.write(xm.num('type', self._ids_vtype_sumo[id_vtype]))
        fd.write(xm.num('line', self.get_id_line_xml(id_veh)))
        # fd.write(xm.num('departPos', pos_from))
        # fd.write(xm.num('departLane', laneindex_from ))
        fd.write(xm.num('from', self._ids_edge_sumo[rides.ids_edge_from[id_stage]]))
        fd.write(xm.num('to', self._ids_edge_sumo[rides.ids_edge_to[id_stage]]))
        pos_from = rides.positions_from[id_stage]
        pos_to = rides.positions_to[id_stage]
        fd.write(xm.num('departPos', pos_from))
        fd.write(xm.num('arrivalPos', pos_to))
        fd.write(xm.num('departLane', 'best'))

        fd.write(xm.stop())

        # write route
        fd.write(xm.start('route', indent+4))
        # print '  ids_edges',rides.ids_edges[id_stage]
        # print '  ids_sumo',self._ids_edge_sumo[rides.ids_edges[id_stage]]
        fd.write(xm.arr('edges', self._ids_edge_sumo[rides.ids_edges[id_stage]]))
        # fd.write(xm.arr('edges',edges.ids_sumo[rides.ids_edges[id_stage]]))

        fd.write(xm.stopit())

        # write depart stop
        fd.write(xm.start('stop', indent+4))
        id_lane = self._get_laneid_allowed(rides.ids_edge_from[id_stage], self._id_mode)
        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane)))
        # in 0.31 the vehicle will wait until after this duration
        # so it will be removed unless it will get a timeout function
        # fd.write(xm.num('duration', time_veh_wait_after_stop))
        if pos_from > self._space_access:
            fd.write(xm.num('startPos', pos_from - self._space_access))
            fd.write(xm.num('endPos', pos_from+self._space_access))
        else:
            fd.write(xm.num('startPos', 0.1*pos_from))
            fd.write(xm.num('endPos', pos_from+self._space_access))

        fd.write(xm.num('triggered', "True"))

        # chrashes with parking=True in 0.30!
        # however if not parked the vhcle is blocking the traffic
        # while waiting: workaround: delay departure to be shure that person already arrived

        fd.write(xm.num('parking', 'True'))  # in windows 0.30 parked vehicles do not depart!!
        # fd.write(xm.num('parking', "False"))
        fd.write(xm.stopit())

        # write arrival stop
        fd.write(xm.start('stop', indent+4))

        id_lane = self._get_laneid_allowed(rides.ids_edge_to[id_stage], self._id_mode)
        # id_edge = rides.ids_edge_to[id_stage]
        # print '  id_lane',id_lane,self._get_sumoinfo_from_id_lane(id_lane),'id_edge',id_edge,ids_edge_sumo[id_edge]

        fd.write(xm.num('lane', self._get_sumoinfo_from_id_lane(id_lane)))
        fd.write(xm.num('duration', 5))  # for unboarding only
        if pos_to > self._space_access:
            fd.write(xm.num('startPos', pos_to - self._space_access))
            fd.write(xm.num('endPos', pos_to))
        else:
            fd.write(xm.num('startPos', 0.1*pos_to))
            fd.write(xm.num('endPos', pos_to))
        # fd.write(xm.num('triggered', "True"))
        fd.write(xm.stopit())

        fd.write(xm.end('vehicle', indent+2))


class IndividualMotorcycles(IndividualBikes):

    def __init__(self, ident, population, **kwargs):
        # print 'individualvehicle vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
        self._init_objman(ident=ident,
                          parent=population,
                          name='Indiv. Moto',
                          info='Individual Motorcycle/moped database. These are privately owned motorcycles.',
                          **kwargs)
        IndividualBikes._init_attributes(self)
        IndividualBikes._init_constants(self)

    def def_mode(self):
        self.mode = 'motorcycle'
        self.mode_prefix = 'imoto'

    def get_ids_veh_pop(self):
        """
        To be overridden by other individual vehicle types.
        """
        return self.parent.ids_imoto

    def get_stagetable(self):
        return self.parent.get_plans().get_stagetable('motorides')

    def get_id_veh(self, id_stage):
        return self._rides.ids_imoto[id_stage]


class StrategyMixin(cm.BaseObjman):
    def __init__(self, ident, parent=None,
                 name='Strategy mixin', info='Info on strategy.',
                 **kwargs):
        """
        To be overridden.
        """
        # attention parent is the Strategies table
        self._init_objman(ident, parent, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

    def get_id_strategy(self):
        return self.parent.names.get_id_from_index(self.get_ident())

    def get_scenario(self):
        return self.parent.parent.get_scenario()

    def get_activities(self):
        return self.parent.parent.get_activities()

    def get_virtualpop(self):
        return self.parent.parent

    def get_plans(self):
        return self.parent.parent.plans

    def clip_positions(self, positions, ids_edge):
        lengths = self.get_scenario().net.edges.lengths[ids_edge]
        # print 'clip_positions',positions.shape,ids_edge.shape,lengths.shape
        positions_clip = np.clip(positions, self.dist_node_min*np.ones(len(positions),
                                 dtype=np.float32), lengths-self.dist_node_min)
        inds = lengths < 2*self.dist_node_min
        # print '  inds.shape',inds.shape,positions_clip.shape
        positions_clip[inds] = 0.5*lengths[inds]
        return positions_clip

    def _init_attributes_strategy(self, **kwargs):
        attrsman = self.get_attrsman()
        self.dist_node_min = attrsman.add(cm.AttrConf('dist_node_min', kwargs.get('dist_node_min', 40.0),
                                                      groupnames=['options'],
                                                      perm='rw',
                                                      name='Min. dist to nodes',
                                                      unit='m',
                                                      info='Minimum distance between starting position and node center.',
                                                      ))

    def get_utility_specific(self, id_plan):
        """Method returns a strategy specific Utility. Here simly a constant is returned. This constant needs to be defined as attribute of the specific strategy"""

        return self.utility_constant

    # def _init_constants_strategy(self):
    #    #print '_init_constants_strategy'
    #    modes = self.get_virtualpop().get_scenario().net.modes
    #    self._id_mode_bike = modes.get_id_mode('bicycle')
    #    self._id_mode_auto = modes.get_id_mode('passenger')
    #    self._id_mode_moto = modes.get_id_mode('motorcycle')
    #   self.get_attrsman().do_not_save_attrs([
    #                '_id_mode_bike','_id_mode_auto','_id_mode_moto',
    #                        ])
    # print '  _id_mode_auto',self._id_mode_auto

    # def are_feasible(self, ids_person):
    #    """
    #    Returns a bool vector, with True values for
    #    persons where this strategy can be applied.
    #    """
    #    return []

    # def is_feasible(self, id_person):
    #    """
    #    Returns True if this strategy is feasible for this person.
    #    Overriden by specific strategy.
    #    """
    #    return False

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        return zeros(len(ids_person), dtype=np.int32)

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        pass


class NoneStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='None strategy',
                 info='With this strategy, no mobility plan is generated.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))


class WalkStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='Walk Strategy',
                 info='With this strategy, the person walks to all destinations.',
                 version=0.1,
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', -0.0556),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for walk strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

        self.is_walk_filter = attrsman.add(cm.AttrConf('is_walk_filter', kwargs.get('is_walk_filter', False),
                                                       groupnames=['options'],
                                                       name='Walk Filter',
                                                       info=""" Enable the Walk filter.
                                                If it is True : Generate walk plans only for people who respect the max distance parameter. 
                                                If it is False: Generate walk plans for all virtual population.
                                    """,
                                                       ))

        # if hasattr(self,'max_dist_fac'):
        #    dist_fac_max_default = self.max_dist_fac
        #    attrsman.delete('max_dist_fac')
        # else:
        #    dist_fac_max_default = kwargs.get('dist_fac_max',500.0)

        self.dist_fac_max = attrsman.add(cm.AttrConf('dist_fac_max', kwargs.get('dist_fac_max', 500.0),
                                                     groupnames=['options'],
                                                     perm='rw',
                                                     name='Max distance facilities',
                                                     unit='m',
                                                     info="""Max distance between origin and destination facilities corrisponding to the first and second activity. 
                                    If the line of sight distance is greater than this distance, the walk plan will not be created.
                                    Will be only applied to those who have not walking as preferred mode.
                                    """,
                                                     ))

        # if self.get_version() < 0.1:
        #    self.set_version(0.1)

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_bike = modes.get_id_mode('bicycle')
        self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_moto = modes.get_id_mode('motorcycle')
        self._id_mode_bus = modes.get_id_mode('bus')
        self._id_mode_ped = modes.get_id_mode('pedestrian')
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto',
            '_id_mode_bus', '_id_mode_ped',
        ])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # TODO: here we could exclude by age or distance facilities-stops

        # put 0 for persons whose preference is not walking, but can walk (everybody can walk)
        preeval[persons.ids_mode_preferred[ids_person] != self._id_mode_ped] = 0

        # put 2 for persons  whose preference is  walking
        preeval[persons.ids_mode_preferred[ids_person] == self._id_mode_ped] = 2

        # in case
        if self.is_walk_filter:

            max_dist_fac_sqr = self.dist_fac_max**2

            vp = self.get_virtualpop()
            scenario = vp.get_scenario()

            facilities = scenario.landuse.facilities
            centroids = facilities.centroids
            activities = vp.get_activities()

            ids_pers_filter = ids_person[persons.ids_mode_preferred[ids_person] != self._id_mode_ped]
            for i,  ids_activity in zip(range(len(ids_pers_filter)), vp.activitypatterns[ids_pers_filter]):

                id_act1 = ids_activity[0]
                id_act2 = ids_activity[1]
                id_fac_from = activities.ids_facility[id_act1]
                id_fac_to = activities.ids_facility[id_act2]
                dist_fac_sqr = np.sum((centroids[id_fac_from] - centroids[id_fac_to])**2)
                if dist_fac_sqr > max_dist_fac_sqr:
                    preeval[i] = -1

        print('  WalkStrategy.preevaluate', len(np.flatnonzero(preeval >= 0)))
        return preeval

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        print('WalkStrategy.pan', len(ids_person))
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        demand = virtualpop.get_demand()
        # ptlines = demand.ptlines

        walkstages = plans.get_stagetable('walks')
        # transitstages = plans.get_stagetable('transits')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = demand.activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        net = scenario.net
        edges = net.edges
        lanes = net.lanes
        modes = net.modes

        # ptstops = net.ptstops

        ids_laneedge = net.lanes.ids_edge

        times_est_plan = plans.times_est
        # here we can determine edge weights for different modes

        # this could be centralized to avoid redundance
        plans.prepare_stagetables(['walks', 'activities'])

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in WalkStrategy.plan: no eligible persons found.')
            return False

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        # ids_plan_act = virtualpop.add_plans(ids_person_act, id_strategy = self.get_id_strategy())
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        n_plans = len(ids_person_act)

        # make initial activity stage
        # ids_edge_from = facilities.ids_roadedge_closest[map_ids_fac_from[ids_person_act]]
        # poss_edge_from = facilities.positions_roadedge_closest[map_ids_fac_from[ids_person_act]]

        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]

        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        ##

        ind_act = 0

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]

            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]  # map_ids_fac_from[ids_person_act]
            ids_fac_to = activities.ids_facility[ids_act_to]

            centroids_from = facilities.centroids[ids_fac_from]
            centroids_to = facilities.centroids[ids_fac_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]
            poss_edge_to = facilities.positions_roadedge_closest[ids_fac_to]

            # ids_stop_from = ptstops.get_closest(centroids_from)
            # ids_stop_to = ptstops.get_closest(centroids_to)

            # ids_stopedge_from = ids_laneedge[ids_stoplane[ids_stop_from]]
            # ids_stopedge_to = ids_laneedge[ids_stoplane[ids_stop_to]]

            # do random pos here
            # poss_stop_from = 0.5*(  ptstops.positions_from[ids_stop_from]\
            #                        +ptstops.positions_to[ids_stop_from])

            # poss_stop_to = 0.5*(  ptstops.positions_from[ids_stop_to]\
            #                            +ptstops.positions_to[ids_stop_to])

            i = 0.0
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, \
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to,  ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to):
                n_pers = len(ids_person_act)
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'_')
                print('  id_plan=%d, id_person=%d, id_act_from=%d id_act_to=%d' %
                      (id_plan, id_person, id_act_from, id_act_to))
                print('      id_edge_from', id_edge_from, 'id_edge_to', id_edge_to)

                id_stage_walk1, time = walkstages.append_stage(id_plan, time_from,
                                                               id_edge_from=id_edge_from,
                                                               position_edge_from=pos_edge_from,
                                                               id_edge_to=id_edge_to,
                                                               position_edge_to=pos_edge_to,  # -7.0,
                                                               )

                # update time for trips estimation for this plan
                plans.times_est[id_plan] += time-time_from

                # define current end time without last activity duration
                plans.times_end[id_plan] = time

                id_stage_act, time = activitystages.append_stage(
                    id_plan, time,
                    ids_activity=id_act_to,
                    names_activitytype=name_acttype_to,
                    durations=duration_act_to,
                    ids_lane=edges.ids_lanes[id_edge_to][0],
                    positions=pos_edge_to,
                )

                # store time for next iteration in case other activities are
                # following
                map_times[id_person] = time

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)


class AutoStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='Auto strategy',
                 info='With this strategy, the person uses his private auto as main transport mode.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', 0.0),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for auto strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

        modechoices = self.get_virtualpop().get_scenario().net.modes.names.get_indexmap()
        modechoices['No fallback'] = None
        # print '  modechoices',modechoices
        self.id_mode_fallback = attrsman.add(am.AttrConf('id_mode_fallback',  modechoices['taxi'],
                                                         groupnames=['options'],
                                                         choices=modechoices,
                                                         name='Fallback Mode for Auto strategy',
                                                         info="""Transport mode to be used instead of "passenger" mode
                                             in case the origin and destination cannot be connected by a route. 
                                             This is typically the case with origins or destinations 
                                             in traffic restricted zones. 
                                             Coose for example "taxi" to get access to traffic restricted Zones.
                                             """,
                                                         ))

        self.c_spread = attrsman.add(cm.AttrConf('c_spread', kwargs.get('c_spread', 2.0),
                                                 groupnames=['options'],
                                                 perm='rw',
                                                 name='Parking spread constant',
                                                 info='Constant to determin how far a person finds a parking from the opimal position in case parkings are occupied.',
                                                 ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_bike = modes.get_id_mode('bicycle')
        self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_moto = modes.get_id_mode('motorcycle')
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto',
        ])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        print('Autostrategy.preevaluate', n_pers, 'persons')
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # put -1 for persons without car access
        preeval[persons.ids_iauto[ids_person] == -1] = -1
        print('  persons without auto access', len(np.flatnonzero(persons.ids_iauto[ids_person] == -1)))

        # put 0 for persons with car but with a different preferred mode
        preeval[(persons.ids_iauto[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] != self._id_mode_auto)] = 0

        print('  persons with car but with a different preferred mode', len(np.flatnonzero(
            (persons.ids_iauto[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] != self._id_mode_auto))))

        # put 2 for persons with car access and who prefer the car
        preeval[(persons.ids_iauto[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] == self._id_mode_auto)] = 2
        print('  persons  with car access and who prefer the car', len(np.flatnonzero(
            (persons.ids_iauto[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] == self._id_mode_auto))))

        print(' check persons with car', len(np.flatnonzero((persons.ids_iauto[ids_person] > -1))))
        print(' check preferring car', len(np.flatnonzero(
            (persons.ids_mode_preferred[ids_person] == self._id_mode_auto))))
        return preeval

    # def are_feasible(self, ids_person):
    #    """
    #    Returns a bool vector, with True values for
    #    persons where this strategy can be applied.
    #    """
    #    persons = self.get_virtualpop()
    #
    #    # check if person has a car
    #    # one may also check if there is parking available
    #    # at all desinations
    #    return persons.ids_iautos[ids_person] >= 0

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for  person in list ids_person according to this strategie.
        """
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()
        walkstages = plans.get_stagetable('walks')
        ridestages = plans.get_stagetable('autorides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = virtualpop.get_demand().activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        parking = landuse.parking

        scenario = virtualpop.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        modes = scenario.net.modes
        demand = scenario.demand
        vtyes = demand.vtypes

        ids_mode = scenario.demand.vtypes.ids_mode
        iautos = virtualpop.get_iautos()

        # times_est_plan = plans.times_est

        # here we can determine edge weights for different modes
        plans.prepare_stagetables(['walks', 'autorides', 'activities'])

        # get initial travel times for persons.
        # initial travel times depend on the initial activity

        landuse.parking.clear_booking()

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in Autostrategy.plan: no eligible persons found.')
            return False

        # ok

        # temporary maps from ids_person to other parameters

        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        # ids_plan_act = virtualpop.add_plans(ids_person_act, id_strategy = self.get_id_strategy())
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        # err
        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        # err
        map_ids_parking_from = np.zeros(nm, dtype=np.int32)
        map_are_fallback = np.zeros(nm, dtype=bool)

        # err
        ids_parking_from, are_fallback = parking.get_closest_parkings(virtualpop.ids_iauto[ids_person_act],
                                                                      ids_mode[iautos.ids_vtype[virtualpop.ids_iauto[ids_person_act]]],
                                                                      facilities.centroids[activities.ids_facility[ids_act_from]],
                                                                      dists_walk_max=virtualpop.dists_walk_max[ids_person_act],
                                                                      id_mode_fallback=self.id_mode_fallback,
                                                                      c_spread=self.c_spread,
                                                                      )

        if len(ids_parking_from) == 0:
            return False

        map_ids_parking_from[ids_person_act] = ids_parking_from
        map_are_fallback[ids_person_act] = are_fallback

        n_plans = len(ids_person_act)
        print('AutoStrategy.plan n_plans=', n_plans)
        # print '  map_ids_parking_from[ids_person_act].shape',map_ids_parking_from[ids_person_act].shape
        # set initial activity
        # this is because the following steps start with travel
        # and set the next activity
        # names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        # for id_plan

        ind_act = 0

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan,
                time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],  # assumes pedestrian accessibility at leftmost lane
                positions=pos_edge_from,
            )

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]
            ids_veh = virtualpop.ids_iauto[ids_person_act]

            # ids_mode = vtypes.ids_mode[iautos.ids_vtype[ids_veh]]
            # print ids_veh
            # inds_pers = virtualpop.get_inds(ids_person)
            # self.persons.cols.mode_preferred[inds_pers]='private'

            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            # centroids_to = facilities.centroids[ids_fac_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]

            # this method will find and occupy parking space
            ids_parking_from = map_ids_parking_from[ids_person_act]

            # print '  ids_veh.shape',ids_veh.shape
            # print '  centroids_to.shape',centroids_to.shape
            ids_parking_to, are_fallback = parking.get_closest_parkings(ids_veh,
                                                                        ids_mode[iautos.ids_vtype[ids_veh]],
                                                                        facilities.centroids[ids_fac_to],
                                                                        dists_walk_max=virtualpop.dists_walk_max[ids_person_act],
                                                                        id_mode_fallback=self.id_mode_fallback,
                                                                        c_spread=self.c_spread,
                                                                        )
            # attention, this is only a suggestion for fallback mode
            # final decision is made while routing further down
            map_are_fallback[ids_person_act] |= are_fallback

            ids_lane_parking_from = parking.ids_lane[ids_parking_from]
            ids_edge_parking_from = lanes.ids_edge[ids_lane_parking_from]
            poss_edge_parking_from = parking.positions[ids_parking_from]

            # print '  ids_parking_to.shape',ids_parking_to.shape
            # print '  np.max(parking.get_ids()), np.max(ids_parking_to)',np.max(parking.get_ids()), np.max(ids_parking_to)
            ids_lane_parking_to = parking.ids_lane[ids_parking_to]
            ids_edge_parking_to = lanes.ids_edge[ids_lane_parking_to]
            poss_edge_parking_to = parking.positions[ids_parking_to]

            # Fallback mode, if an auto can't run between origin and destination parkings, then the vehicle is changed with a taxy
            # print '  prepare routing'
            # fstar = edges.get_fstar(is_ignor_connections=False)
            # times_primary = edges.get_times(id_mode= self._id_mode_auto, is_check_lanes=True)

            if self.id_mode_fallback is not None:
                ids_vtype_fallback, share_fallback = demand.vtypes.select_by_mode(self.id_mode_fallback, is_share=True)
                # print '  ids_vtype_fallback, share_fallback',ids_vtype_fallback, share_fallback

            # for id_edge_orig, id_edge_dest, id_veh, i in zip(ids_edge_parking_from, ids_edge_parking_to, ids_veh, range(len(ids_veh))):
            #    cost, route = routing.get_mincostroute_edge2edge(id_edge_orig,
            #                                                    id_edge_dest,
            #                                                    weights = times_primary,# mode dependent!
            #                                                    fstar=fstar
            #                                                    )
            #    if route == []:
            #        if self.id_mode_fallback != 1:
            #            print 'fallback mode for vehicle %d of person %d'%(id_veh, virtualpop.get_iautos().ids_person[id_veh])
            #        are_fallback[i] = True
            #            virtualpop.get_iautos().ids_vtype[id_veh] = np.random.choice(id_vtype_mode[0], p = id_vtype_mode[1])
            # print ids_veh

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]
            poss_edge_to = facilities.positions_roadedge_closest[ids_fac_to]

            i = 0.0
            n_pers = len(ids_person_act)
            # list of failed plans in the format [(id_person, id_plan),... ]
            plans_failed = []
            for id_person, id_mode, is_fallback, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_parking_from, pos_edge_parking_from, id_parking_from, id_parking_to, id_edge_parking_to, pos_edge_parking_to, id_edge_to, pos_edge_to\
                    in zip(ids_person_act, ids_mode[iautos.ids_vtype[ids_veh]], map_are_fallback[ids_person_act], ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_parking_from, poss_edge_parking_from, ids_parking_from, ids_parking_to, ids_edge_parking_to, poss_edge_parking_to, ids_edge_to, poss_edge_to):
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                # plans.set_row(id_plan, ids_person = id_person, ids_strategy = self.get_id_strategy())

                # start creating stages for activity
                id_stage_walk1, time = walkstages.append_stage(
                    id_plan, time_from,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=id_edge_parking_from,
                    position_edge_to=pos_edge_parking_from-1.5,  # wait 1.5 m before nose of parked car
                )

                print('-'*70)
                print('      plan for id_person %d/%d' % (id_person, n_pers),
                      'ind_act', ind_act, 'id_plan', id_plan, 'id_veh', id_veh)
                # ride from  car parking to road edge near activity
                id_stage_car, time, is_fallback = ridestages.append_stage(
                    id_plan,
                    id_mode,
                    is_fallback,
                    self.id_mode_fallback,
                    time_start=time,
                    id_veh=id_veh,
                    # delay to be sure that person arrived!(workaround in combination with parking=False)
                    time_init=time+30,  # time_from,
                    id_parking_from=id_parking_from,
                    id_parking_to=id_parking_to,
                    # TODO: here we could use id_edge_to as via edge to emulate search for parking
                )

                if is_fallback & (self.id_mode_fallback is not None):
                    # now we are sure fallback mode is needed
                    # set a fallback vtype for this id_veh
                    iautos.ids_vtype[id_veh] = np.random.choice(ids_vtype_fallback, p=share_fallback)
                    map_are_fallback[id_person] = True

                if id_stage_car > -1:
                    # print '  car ride successful'
                    id_stage_walk2, time = walkstages.append_stage(
                        id_plan, time,
                        id_edge_from=id_edge_parking_to,
                        position_edge_from=pos_edge_parking_to-1.5,  # ecessary?
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
                    # store time estimation for this plan
                    # note that these are the travel times, no activity time
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    # finally add activity and respective duration

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    map_times[id_person] = time
                    # return time
                else:
                    print('    planning failed id_stage_car', id_stage_car)
                    # print '  parking not connected or distance too short, modify first walk and go directly to activity'
                    # print '  id_stage_walk1',id_stage_walk1,type(id_stage_walk1)
                    # print '  id_edge_from',id_edge_from
                    # print '  position_edge_from',position_edge_from
                    # print '  id_edge_to',id_edge_to
                    # print '  position_edge_to',position_edge_to

                    plans_failed.append((id_person, id_plan))

            # prepare next activity
            map_ids_parking_from[ids_person_act] = ids_parking_to
            landuse.parking.clear_booking()

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                print('  remove failed plan for id_person', id_person, 'id_plan', id_plan)
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
            # update timing with (random) activity duration!!

        return True


class BikeStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='Bike strategy',
                 info='With this strategy, the person uses his private bike as main transport mode.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes(**kwargs)
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self._init_attributes_strategy(**kwargs)
        self.n_iter_bikeacces_max = attrsman.add(cm.AttrConf('n_iter_bikeacces_max', kwargs.get('n_iter_bikeacces_max', 5),
                                                             groupnames=['options'],
                                                             perm='rw',
                                                             name='Max. bike access search iterations',
                                                             info='Max. number of iterations while searching an edge with bike access.',
                                                             ))

        self.length_edge_min = attrsman.add(cm.AttrConf('length_edge_min', kwargs.get('length_edge_min', 20.0),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Min. edge length search',
                                                        unit='m',
                                                        info='Min. edge length when searching an edge with bike access.',
                                                        ))

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', -0.5604),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for bike strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_ped = modes.get_id_mode('pedestrian')
        self._id_mode_bike = modes.get_id_mode('bicycle')
        self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_moto = modes.get_id_mode('motorcycle')
        self._edges = self.get_virtualpop().get_scenario().net.edges
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto',
            '_id_mode_ped',
            '_edges'])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        print('BikeStrategy.preevaluate', n_pers, 'persons')
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # put -1 for persons without car access
        preeval[persons.ids_ibike[ids_person] == -1] = -1
        print('  persons having no bike', len(np.flatnonzero(persons.ids_ibike[ids_person] == -1)))

        # put 0 for persons with bike but with a different preferred mode
        preeval[(persons.ids_ibike[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] != self._id_mode_bike)] = 0

        print('  persons with bike but with a different preferred mode', len(np.flatnonzero(
            (persons.ids_ibike[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] != self._id_mode_bike))))

        # put 2 for persons with bike access and who prefer the bike
        preeval[(persons.ids_ibike[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] == self._id_mode_bike)] = 2
        print('  persons  with car access and who prefer the car', len(np.flatnonzero(
            (persons.ids_ibike[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] == self._id_mode_bike))))

        return preeval

    def get_edge_bikeaccess(self, id_edge, is_search_backward=False, is_get_route=False, is_star=False, bstar=0, fstar=0, n_iter_bikeacces_max=None):
        # get firts edge allowing bikes and pedestrian, saving route, frontward or backward. You can include bstar or fstar:
        # check only connected links. You can obtain also the route.
        # print 'get_edge_bikeaccess',id_edge, is_search_backward,'id_sumo',self._edges.ids_sumo[id_edge]

        if n_iter_bikeacces_max is None:
            n_iter_bikeacces_max = self.n_iter_bikeacces_max

        id_mode = self._id_mode_bike
        id_mode_ped = self._id_mode_ped
        get_accesslevel = self._edges.get_accesslevel
        if is_star:
            if is_search_backward:
                get_next = bstar
            else:
                get_next = fstar
        else:
            if is_search_backward:
                get_next = self._edges.get_incoming
            else:
                get_next = self._edges.get_outgoing

        edgelengths = self._edges.lengths
        # ids_tried = set()
        ids_current = [id_edge]
        id_bikeedge = -1
        pos = 0.0
        n = 0
        ids = [id_edge]
        coll_ids = [0]
        route = []
        while (id_bikeedge < 0) & (n < n_iter_bikeacces_max):
            n += 1

            ids_new = []
            for id_edge_test, is_long_enough in zip(ids_current, edgelengths[ids_current] > self.length_edge_min):
                # print '    check id',id_edge_test, is_long_enough,get_accesslevel(id_edge_test, id_mode)
                if is_long_enough & (get_accesslevel(id_edge_test, id_mode) >= 0) & (get_accesslevel(id_edge_test, id_mode_ped) >= 0):
                    id_bikeedge = id_edge_test

                    if is_get_route:
                        if n == 1:
                            route = []
                        else:
                            route.append(id_edge_test)
                            if n > 2:
                                route.append(coll_ids[ids.index(id_edge_test)])
                                if n > 3:
                                    for n in range(n-3):
                                        route.append(coll_ids[ids.index(route[-1])])

                        if is_search_backward is not True:
                            route.reverse()

                    # print '    found',id_bikeedge,self._edges.ids_sumo[id_bikeedge]
                    break
                else:

                    if is_star:
                        ids_new += get_next[id_edge_test]
                        if is_get_route:
                            ids += get_next[id_edge_test]
                            for i in range(len(get_next[id_edge_test])):
                                coll_ids.append(id_edge_test)

                    else:
                        ids_new += get_next(id_edge_test)
                        if is_get_route:
                            ids += get_next(id_edge_test)
                            for i in range(len(get_next(id_edge_test))):
                                coll_ids += id_edge_test

            ids_current = ids_new

        if id_bikeedge > -1:
            if is_search_backward:
                pos = edgelengths[id_bikeedge]-0.5*self.length_edge_min
            else:
                pos = 0.5*self.length_edge_min

        if id_bikeedge == -1:
            print('WARNING in get_edge_bikeaccess no access for', id_edge, self._edges.ids_sumo[id_edge])

        if is_get_route:
            return id_bikeedge, pos, route
        else:
            return id_bikeedge, pos

    def plan_bikeride(self, id_plan, time_from, id_veh,
                      id_edge_from, pos_edge_from,
                      id_edge_to, pos_edge_to,
                      dist_from_to, dist_walk_max,
                      walkstages, ridestages):

        # start creating stages
        id_stage_walk1 = -1
        id_stage_walk2 = -2
        id_stage_bike = -1
        time = 0.0
        id_edge_from_bike, pos_from_bike = self.get_edge_bikeaccess(id_edge_from)
        id_edge_to_bike, pos_to_bike = self.get_edge_bikeaccess(id_edge_to, is_search_backward=True)

        if (dist_from_to < dist_walk_max) | (id_edge_from_bike == -1) | (id_edge_to_bike == -1):
            print('    planning failed because distance is too short or no bike access',
                  dist_from_to, id_edge_from_bike, id_edge_to_bike)
            pass

        else:
            # print '    try to take the bike',id_veh
            # print '    id_edge_from_bike',edges.ids_sumo[id_edge_from_bike],pos_from_bike
            # print '    id_edge_to_bike',edges.ids_sumo[id_edge_to_bike],pos_to_bike

            if id_edge_from_bike != id_edge_from:
                print('    must first walk from origin to bikerack', time_from)

                id_stage_walk1, time = walkstages.append_stage(
                    id_plan, time_from,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=id_edge_from_bike,
                    position_edge_to=pos_from_bike,
                )

                if id_edge_to_bike != id_edge_to:
                    print('      try cycle from bikerack to dest bike rack', time)
                    id_stage_bike, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time-10,  # time_from,
                        id_edge_from=id_edge_from_bike,
                        position_edge_from=pos_from_bike,
                        id_edge_to=id_edge_to_bike,
                        position_edge_to=pos_to_bike,
                    )
                    if id_stage_bike > -1:
                        print('      succesful,  walk from dest bikerack to dest', time)
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_bike,
                            position_edge_from=pos_to_bike,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )

                else:
                    print('    cycle from bikerack to destination', time)
                    id_stage_bike, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time-10,  # time_from,
                        id_edge_from=id_edge_from_bike,
                        position_edge_from=pos_from_bike,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
            else:
                print('    cycle directly from orign edge', time_from)
                if id_edge_to_bike != id_edge_to:
                    print('    must cycle from origin to dest bikerack', time_from)

                    # pos_to_bike = 0.1*edges.lengths[id_edge_to_bike]
                    id_stage_bike, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time_from-10,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to_bike,
                        position_edge_to=pos_to_bike,
                    )
                    if id_stage_bike > -1:
                        print('      walk from dest bike rack to dest', time)
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_bike,
                            position_edge_from=pos_to_bike,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                else:
                    print('    must cycle from origin to destination', time_from)
                    id_stage_bike, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time_from-10,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )

            # here we should have created a bike ride
            # if not, for ehatever reason,
            # we walk from origin to destination
            if id_stage_bike == -1:
                print('    planning failed because no ride stage has been planned time_from', time_from, 'time', time)
                print('      id_stage_bike', id_stage_bike, 'id_stage_walk1',
                      id_stage_walk1, 'id_stage_walk2', id_stage_walk2)
                pass

        return id_stage_bike, time

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        walkstages = plans.get_stagetable('walks')
        ridestages = plans.get_stagetable('bikerides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = virtualpop.get_demand().activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        modes = scenario.net.modes

        # times_est_plan = plans.times_est

        # here we can determine edge weights for different modes
        plans.prepare_stagetables(['walks', 'bikerides', 'activities'])

        # get initial travel times for persons.
        # initial travel times depend on the initial activity

        # landuse.parking.clear_booking()

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in BikeStrategy.plan: no eligible persons found.')
            return False

        # ok

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        # map_ids_parking_from = np.zeros(nm, dtype = np.int32)
        # ids_parking_from, inds_vehparking = parking.get_closest_parkings( virtualpop.ids_iauto[ids_person_act],
        # facilities.centroids[activities.ids_facility[ids_act_from]])
        # if len(ids_parking_from)==0:
        #    return False

        # err

        # map_ids_parking_from[ids_person_act] = ids_parking_from

        n_plans = len(ids_person_act)
        print('BikeStrategy.plan n_plans=', n_plans)
        # print '  map_ids_parking_from[ids_person_act].shape',map_ids_parking_from[ids_person_act].shape
        # set initial activity
        # this is because the following steps start with travel
        # and set the next activity
        # names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        # for id_plan

        ind_act = 0

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]

        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        # do initial stage
        # could be common to all strategies
        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]
            ids_veh = virtualpop.ids_ibike[ids_person_act]
            dists_walk_max = virtualpop.dists_walk_max[ids_person_act]
            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]
            poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

            centroids_from = facilities.centroids[ids_fac_from]

            # this method will find and occupy parking space
            # ids_parking_from = map_ids_parking_from[ids_person_act]

            # print '  ids_veh.shape',ids_veh.shape
            # print '  centroids_to.shape',centroids_to.shape
            # ids_parking_to, inds_vehparking = parking.get_closest_parkings(ids_veh, centroids_to)

            # ids_lane_parking_from = parking.ids_lane[ids_parking_from]
            # ids_edge_parking_from = lanes.ids_edge[ids_lane_parking_from]
            # poss_edge_parking_from = parking.positions[ids_parking_from]

            # print '  ids_parking_to.shape',ids_parking_to.shape
            # print '  np.max(parking.get_ids()), np.max(ids_parking_to)',np.max(parking.get_ids()), np.max(ids_parking_to)
            # ids_lane_parking_to = parking.ids_lane[ids_parking_to]
            # ids_edge_parking_to = lanes.ids_edge[ids_lane_parking_to]
            # poss_edge_parking_to = parking.positions[ids_parking_to]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]

            poss_edge_to1 = facilities.positions_roadedge_closest[ids_fac_to]
            poss_edge_to = self.clip_positions(poss_edge_to1, ids_edge_to)
            centroids_to = facilities.centroids[ids_fac_to]

            # debug poscorrection..OK
            # for id_edge, id_edge_sumo, length, pos_to1, pos in zip(ids_edge_to, edges.ids_sumo[ids_edge_to],edges.lengths[ids_edge_to],poss_edge_to1, poss_edge_to):
            #    print '  ',id_edge, 'IDe%s'%id_edge_sumo, 'L=%.2fm'%length, '%.2fm'%pos_to1, '%.2fm'%pos

            dists_from_to = np.sqrt(np.sum((centroids_to - centroids_from)**2, 1))

            i = 0.0
            n_pers = len(ids_person_act)
            plans_failed = []
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, dist_from_to, dist_walk_max\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, dists_from_to, dists_walk_max):
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'*')
                print('      plan for id_person %d/%d' % (id_person, n_pers), 'ind_act', ind_act, 'id_plan', id_plan)

                print('         time_from', time_from, 'from', id_edge_from,
                      pos_edge_from, 'to', id_edge_to, pos_edge_to)
                print('         id_edge_from', edges.ids_sumo[id_edge_from], 'id_edge_to', edges.ids_sumo[id_edge_to])

                id_stage_bike, time = self.plan_bikeride(id_plan, time_from, id_veh,
                                                         id_edge_from, pos_edge_from,
                                                         id_edge_to, pos_edge_to,
                                                         dist_from_to, dist_walk_max,
                                                         walkstages, ridestages)

                ################
                if id_stage_bike != -1:

                    # store time estimation for this plan
                    # note that these are the travel times, no activity time
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    # finally add activity and respective duration
                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    map_times[id_person] = time

                else:
                    plans_failed.append((id_person, id_plan))

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
            # update timing with (random) activity duration!!

        return True


class TaxiStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='taxi strategy',
                 info='With this strategy, the person uses his private taxi as main transport mode.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes(**kwargs)
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self._init_attributes_strategy(**kwargs)
        self.n_iter_acces_max = attrsman.add(cm.AttrConf('n_iter_acces_max', kwargs.get('n_iter_acces_max', 5),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Max. access search iterations',
                                                         info='Max. number of iterations while searching an edge with taxi access.',
                                                         ))

        self.length_edge_min = attrsman.add(cm.AttrConf('length_edge_min', kwargs.get('length_edge_min', 20.0),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Min. edge length search',
                                                        unit='m',
                                                        info='Min. edge length when searching an edge with taxi access.',
                                                        ))

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', 0.0),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for taxi strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_ped = modes.get_id_mode('pedestrian')
        # self._id_mode_bike = modes.get_id_mode('bicycle')
        # self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_taxi = modes.get_id_mode('taxi')
        self._edges = self.get_virtualpop().get_scenario().net.edges
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_ped', '_id_mode_taxi',
            '_edges'])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # TODO: here we could exclude by age or distance facilities-stops

        # put 0 for persons whose preference is not public transport
        preeval[persons.ids_mode_preferred[ids_person] != self._id_mode_taxi] = 0

        # put 2 for persons with car access and who prefer cars
        preeval[persons.ids_mode_preferred[ids_person] == self._id_mode_taxi] = 2

        print('  TaxiStrategy.preevaluate', len(np.flatnonzero(preeval)))
        return preeval

    def get_edge_access(self, id_edge, is_search_backward=False, is_get_route=False, is_star=False, bstar=0, fstar=0, n_iter_acces_max=None):
        # get firts edge allowing bikes and pedestrian, saving route, frontward or backward. You can include bstar or fstar:
        # check only connected links. You can obtain also the route.
        # print 'get_edge_bikeaccess',id_edge, is_search_backward,'id_sumo',self._edges.ids_sumo[id_edge]
        print('get_edge_access id_edge', id_edge, 'is_search_backward',
              is_search_backward, 'n_iter_acces_max', self.n_iter_acces_max)
        if n_iter_acces_max is None:
            n_iter_acces_max = self.n_iter_acces_max

        id_mode = self._id_mode_taxi
        id_mode_ped = self._id_mode_ped
        get_accesslevel = self._edges.get_accesslevel
        if is_star:
            if is_search_backward:
                get_next = bstar
            else:
                get_next = fstar
        else:
            if is_search_backward:
                get_next = self._edges.get_incoming
            else:
                get_next = self._edges.get_outgoing

        edgelengths = self._edges.lengths
        # ids_tried = set()
        ids_current = [id_edge]
        id_modeedge = -1
        pos = 0.0
        n = 0
        ids = [id_edge]
        coll_ids = [0]
        route = []
        print('start  id_modeedge', id_modeedge, 'n', n, 'n_iter_acces_max',
              n_iter_acces_max, (id_modeedge < 0), (n < n_iter_acces_max))

        while (id_modeedge < 0) & (n < n_iter_acces_max):
            print('    while id_modeedge', id_modeedge, 'n', n)
            n += 1

            ids_new = []
            for id_edge_test, is_long_enough in zip(ids_current, edgelengths[ids_current] > self.length_edge_min):
                print('    check id', id_edge_test, is_long_enough, 'taxi access', get_accesslevel(
                    id_edge_test, id_mode), 'ped access', get_accesslevel(id_edge_test, id_mode_ped))
                if is_long_enough & (get_accesslevel(id_edge_test, id_mode) >= 0) & (get_accesslevel(id_edge_test, id_mode_ped) >= 0):
                    id_modeedge = id_edge_test

                    if is_get_route:
                        if n == 1:
                            route = []
                        else:
                            route.append(id_edge_test)
                            if n > 2:
                                route.append(coll_ids[ids.index(id_edge_test)])
                                if n > 3:
                                    for n in range(n-3):
                                        route.append(coll_ids[ids.index(route[-1])])
                        if is_search_backward is not True:
                            route.reverse()

                    print('    found', id_modeedge, self._edges.ids_sumo[id_modeedge])
                    break
                else:

                    if is_star:
                        ids_new += get_next[id_edge_test]
                        if is_get_route:
                            ids += get_next[id_edge_test]
                            for i in range(len(get_next[id_edge_test])):
                                coll_ids.append(id_edge_test)

                    else:
                        ids_new += get_next(id_edge_test)
                        if is_get_route:
                            ids += get_next(id_edge_test)
                            for i in range(len(get_next(id_edge_test))):
                                coll_ids += id_edge_test

            ids_current = ids_new

        if id_modeedge > -1:
            if is_search_backward:
                pos = edgelengths[id_modeedge]-0.5*self.length_edge_min
            else:
                pos = 0.5*self.length_edge_min

        if id_modeedge == -1:
            print('WARNING in TaxiStrategy.get_edge_access no access at id_edge',
                  id_edge, self._edges.ids_sumo[id_edge])

        if is_get_route:
            return id_modeedge, pos, route
        else:
            return id_modeedge, pos

    def plan_taxiride(self, id_plan, time_from, id_veh,
                      id_edge_from, pos_edge_from,
                      id_edge_to, pos_edge_to,
                      dist_from_to, dist_walk_max,
                      walkstages, ridestages):

        print('plan_taxiride id_plan', id_plan)
        # start creating stages
        id_stage_walk1 = -1
        id_stage_moto = -1

        id_edge_from_taxi, pos_from_taxi = self.get_edge_access(id_edge_from)
        id_edge_to_taxi, pos_to_taxi = self.get_edge_access(id_edge_to, is_search_backward=True)

        if (dist_from_to < dist_walk_max) | (id_edge_from_taxi == -1) | (id_edge_to_taxi == -1):
            print('    failed because distance is too short or no taxi access',
                  dist_from_to, id_edge_from_taxi, id_edge_to_taxi)
            return False, 0.0

        else:
            print('    try to take the taxi')
            # print '    id_edge_from_taxi',edges.ids_sumo[id_edge_from_taxi],pos_from_taxi
            # print '    id_edge_to_taxi',edges.ids_sumo[id_edge_to_taxi],pos_to_taxi

            if id_edge_from_taxi != id_edge_from:
                print('    must walk from origin to taxi accessible edge, time_from', time_from)
                # walk to taxi edge
                id_stage_walk1, time = walkstages.append_stage(
                    id_plan, time_from,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=id_edge_from_taxi,
                    position_edge_to=pos_from_taxi,
                )

                if id_edge_to_taxi != id_edge_to:
                    print('      must walk from taxi accessible edge to dest, time_from', time)
                    # take taxi
                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time-10,  # needed with taxi??
                        id_edge_from=id_edge_from_taxi,
                        position_edge_from=pos_from_taxi,
                        id_edge_to=id_edge_to_taxi,
                        position_edge_to=pos_to_taxi,
                    )
                    if id_stage_taxi > -1:
                        # walk to dest
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_taxi,
                            position_edge_from=pos_to_taxi,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                    else:
                        print('  Taxi ride from taxi stand failed. id_stage_taxi', id_stage_taxi)
                        # later ...walk from origin to dest if this happened

                else:
                    print('    ride taxi from taxi edge to destination', time)
                    # take taxi
                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time,  # time_from,
                        id_edge_from=id_edge_from_taxi,
                        position_edge_from=pos_from_taxi,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
                    if id_stage_taxi == -1:
                        print('  taxi ride from taxi edge to destination failed. id_stage_taxi', id_stage_taxi)
                        # treated later
                        pass
            else:
                print('    take taxi directly from edge of origin', time_from)
                if id_edge_to_taxi != id_edge_to:
                    print('      must walk from taxi accessible destination to destination', time_from)

                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time_from,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to_taxi,
                        position_edge_to=pos_to_taxi,
                    )
                    if id_stage_taxi > -1:
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_taxi,
                            position_edge_from=pos_to_taxi,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                    else:
                        print('  taxi ride from origin failed. id_stage_taxi', id_stage_taxi)
                        pass
                        # treated later
                else:
                    print('    take taxi directly from origin to destination', time_from)

                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time_from,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
                    if id_stage_taxi == -1:
                        print('  taxi ride from origin to dest edege failed. id_stage_taxi', id_stage_taxi)
                        pass
                        # treated later

            # here we should have created a taxi ride
            # if not, for whatever reason,
            # we walk from origin to destination
            if id_stage_taxi == -1:
                print('    failed because no ride stage has been planned time_from',
                      time_from, time_from == time, 'id_stage_walk1', id_stage_walk1)
                return False, 0.0

        return True, time

    def plan(self, ids_person, logger=None):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        walkstages = plans.get_stagetable('walks')
        ridestages = plans.get_stagetable('taxirides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = virtualpop.get_demand().activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        modes = scenario.net.modes

        # times_est_plan = plans.times_est

        # here we can determine edge weights for different modes
        plans.prepare_stagetables(['walks', 'taxirides', 'activities'])

        # get initial travel times for persons.
        # initial travel times depend on the initial activity

        # landuse.parking.clear_booking()

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in TaxiStrategy.plan: no eligible persons found.')
            return False

        # ok

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        # map_ids_parking_from = np.zeros(nm, dtype = np.int32)
        # ids_parking_from, inds_vehparking = parking.get_closest_parkings( virtualpop.ids_iauto[ids_person_act],
        # facilities.centroids[activities.ids_facility[ids_act_from]])
        # if len(ids_parking_from)==0:
        #    return False

        # err

        # map_ids_parking_from[ids_person_act] = ids_parking_from

        n_plans = len(ids_person_act)
        print('TaxiStrategy.plan n_plans=', n_plans)
        # print '  map_ids_parking_from[ids_person_act].shape',map_ids_parking_from[ids_person_act].shape
        # set initial activity
        # this is because the following steps start with travel
        # and set the next activity
        # names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        # for id_plan

        ind_act = 0

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]

        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        # do initial stage
        # could be common to all strategies
        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]
            ids_veh = virtualpop.ids_imoto[ids_person_act]
            dists_walk_max = virtualpop.dists_walk_max[ids_person_act]
            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]
            poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

            centroids_from = facilities.centroids[ids_fac_from]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]

            poss_edge_to1 = facilities.positions_roadedge_closest[ids_fac_to]
            poss_edge_to = self.clip_positions(poss_edge_to1, ids_edge_to)
            centroids_to = facilities.centroids[ids_fac_to]

            dists_from_to = np.sqrt(np.sum((centroids_to - centroids_from)**2, 1))

            i = 0.0
            n_pers = len(ids_person_act)
            plans_failed = []
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, dist_from_to, dist_walk_max\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, dists_from_to, dists_walk_max):
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'*')
                print('  plan id_plan', id_plan, 'time_from', time_from, 'from',
                      id_edge_from, pos_edge_from, 'to', id_edge_to, pos_edge_to)
                print('    id_edge_from', edges.ids_sumo[id_edge_from], 'id_edge_to', edges.ids_sumo[id_edge_to])
                print('    id_act_from', id_act_from, 'id_act_to', id_act_to)

                is_ride, time = self.plan_taxiride(id_plan, time_from, id_veh,
                                                   id_edge_from, pos_edge_from,
                                                   id_edge_to, pos_edge_to,
                                                   dist_from_to, dist_walk_max,
                                                   walkstages, ridestages)
                if not is_ride:
                    plans_failed.append((id_person, id_plan))
                else:
                    # store time estimation for this plan
                    # note that these are the travel times, no activity time
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    # finally add activity and respective duration

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    map_times[id_person] = time

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed
            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
            # update timing with (random) activity duration!!

        return True


class SAVStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='SAV strategy',
                 info='With this strategy, the person uses shared autonomous vehicles as their main transport mode.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes(**kwargs)
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self._init_attributes_strategy(**kwargs)
        self.n_iter_acces_max = attrsman.add(cm.AttrConf('n_iter_acces_max', kwargs.get('n_iter_acces_max', 5),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Max. access search iterations',
                                                         info='Max. number of iterations while searching an edge with taxi access.',
                                                         ))

        self.length_edge_min = attrsman.add(cm.AttrConf('length_edge_min', kwargs.get('length_edge_min', 20.0),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Min. edge length search',
                                                        unit='m',
                                                        info='Min. edge length when searching an edge with taxi access.',
                                                        ))

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', 0.0),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for taxi strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_ped = modes.get_id_mode('pedestrian')
        # self._id_mode_bike = modes.get_id_mode('bicycle')
        # self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_taxi = modes.get_id_mode('taxi')
        self._edges = self.get_virtualpop().get_scenario().net.edges
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_ped', '_id_mode_taxi',
            '_edges'])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # TODO: here we could exclude by age or distance facilities-stops

        # put 0 for persons whose preference is not public transport
        preeval[persons.ids_mode_preferred[ids_person] != self._id_mode_taxi] = 0

        # put 2 for persons with car access and who prefer cars
        preeval[persons.ids_mode_preferred[ids_person] == self._id_mode_taxi] = 2

        print('  SAVStrategy.preevaluate', len(np.flatnonzero(preeval)))
        return preeval

    def get_edge_access(self, id_edge, is_search_backward=False, is_get_route=False, is_star=False, bstar=0, fstar=0, n_iter_acces_max=None):
        # get firts edge allowing bikes and pedestrian, saving route, frontward or backward. You can include bstar or fstar:
        # check only connected links. You can obtain also the route.
        # print 'get_edge_bikeaccess',id_edge, is_search_backward,'id_sumo',self._edges.ids_sumo[id_edge]
        print('get_edge_access id_edge', id_edge, 'is_search_backward',
              is_search_backward, 'n_iter_acces_max', self.n_iter_acces_max)
        if n_iter_acces_max is None:
            n_iter_acces_max = self.n_iter_acces_max

        id_mode = self._id_mode_taxi
        id_mode_ped = self._id_mode_ped
        get_accesslevel = self._edges.get_accesslevel
        if is_star:
            if is_search_backward:
                get_next = bstar
            else:
                get_next = fstar
        else:
            if is_search_backward:
                get_next = self._edges.get_incoming
            else:
                get_next = self._edges.get_outgoing

        edgelengths = self._edges.lengths
        # ids_tried = set()
        ids_current = [id_edge]
        id_modeedge = -1
        pos = 0.0
        n = 0
        ids = [id_edge]
        coll_ids = [0]
        route = []
        print('start  id_modeedge', id_modeedge, 'n', n, 'n_iter_acces_max',
              n_iter_acces_max, (id_modeedge < 0), (n < n_iter_acces_max))

        while (id_modeedge < 0) & (n < n_iter_acces_max):
            print('    while id_modeedge', id_modeedge, 'n', n)
            n += 1

            ids_new = []
            for id_edge_test, is_long_enough in zip(ids_current, edgelengths[ids_current] > self.length_edge_min):
                print('    check id', id_edge_test, is_long_enough, 'taxi access', get_accesslevel(
                    id_edge_test, id_mode), 'ped access', get_accesslevel(id_edge_test, id_mode_ped))
                if is_long_enough & (get_accesslevel(id_edge_test, id_mode) >= 0) & (get_accesslevel(id_edge_test, id_mode_ped) >= 0):
                    id_modeedge = id_edge_test

                    if is_get_route:
                        if n == 1:
                            route = []
                        else:
                            route.append(id_edge_test)
                            if n > 2:
                                route.append(coll_ids[ids.index(id_edge_test)])
                                if n > 3:
                                    for n in range(n-3):
                                        route.append(coll_ids[ids.index(route[-1])])
                        if is_search_backward is not True:
                            route.reverse()

                    print('    found', id_modeedge, self._edges.ids_sumo[id_modeedge])
                    break
                else:

                    if is_star:
                        ids_new += get_next[id_edge_test]
                        if is_get_route:
                            ids += get_next[id_edge_test]
                            for i in range(len(get_next[id_edge_test])):
                                coll_ids.append(id_edge_test)

                    else:
                        ids_new += get_next(id_edge_test)
                        if is_get_route:
                            ids += get_next(id_edge_test)
                            for i in range(len(get_next(id_edge_test))):
                                coll_ids += id_edge_test

            ids_current = ids_new

        if id_modeedge > -1:
            if is_search_backward:
                pos = edgelengths[id_modeedge]-0.5*self.length_edge_min
            else:
                pos = 0.5*self.length_edge_min

        if id_modeedge == -1:
            print('WARNING in SAVStrategy.get_edge_access no access at id_edge', id_edge, self._edges.ids_sumo[id_edge])

        if is_get_route:
            return id_modeedge, pos, route
        else:
            return id_modeedge, pos

    def plan_taxiride(self, id_plan, time_from, id_veh,
                      id_edge_from, pos_edge_from,
                      id_edge_to, pos_edge_to,
                      dist_from_to, dist_walk_max,
                      walkstages, ridestages):

        print('plan_taxiride id_plan', id_plan)
        # start creating stages
        id_stage_walk1 = -1
        id_stage_moto = -1

        id_edge_from_taxi, pos_from_taxi = self.get_edge_access(id_edge_from)
        id_edge_to_taxi, pos_to_taxi = self.get_edge_access(id_edge_to, is_search_backward=True)

        if (dist_from_to < dist_walk_max) | (id_edge_from_taxi == -1) | (id_edge_to_taxi == -1):
            print('    failed because distance is too short or no taxi access',
                  dist_from_to, id_edge_from_taxi, id_edge_to_taxi)
            return False, 0.0

        else:
            print('    try to take the taxi')
            # print '    id_edge_from_taxi',edges.ids_sumo[id_edge_from_taxi],pos_from_taxi
            # print '    id_edge_to_taxi',edges.ids_sumo[id_edge_to_taxi],pos_to_taxi

            if id_edge_from_taxi != id_edge_from:
                print('    must walk from origin to taxi accessible edge, time_from', time_from)
                # walk to taxi edge
                id_stage_walk1, time = walkstages.append_stage(
                    id_plan, time_from,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=id_edge_from_taxi,
                    position_edge_to=pos_from_taxi,
                )

                if id_edge_to_taxi != id_edge_to:
                    print('      must walk from taxi accessible edge to dest, time_from', time)
                    # take taxi
                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time-10,  # needed with taxi??
                        id_edge_from=id_edge_from_taxi,
                        position_edge_from=pos_from_taxi,
                        id_edge_to=id_edge_to_taxi,
                        position_edge_to=pos_to_taxi,
                    )
                    if id_stage_taxi > -1:
                        # walk to dest
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_taxi,
                            position_edge_from=pos_to_taxi,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                    else:
                        print('  Taxi ride from taxi stand failed. id_stage_taxi', id_stage_taxi)
                        # later ...walk from origin to dest if this happened

                else:
                    print('    ride taxi from taxi edge to destination', time)
                    # take taxi
                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time,  # time_from,
                        id_edge_from=id_edge_from_taxi,
                        position_edge_from=pos_from_taxi,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
                    if id_stage_taxi == -1:
                        print('  taxi ride from taxi edge to destination failed. id_stage_taxi', id_stage_taxi)
                        # treated later
                        pass
            else:
                print('    take taxi directly from edge of origin', time_from)
                if id_edge_to_taxi != id_edge_to:
                    print('      must walk from taxi accessible destination to destination', time_from)

                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time_from,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to_taxi,
                        position_edge_to=pos_to_taxi,
                    )
                    if id_stage_taxi > -1:
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_taxi,
                            position_edge_from=pos_to_taxi,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                    else:
                        print('  taxi ride from origin failed. id_stage_taxi', id_stage_taxi)
                        pass
                        # treated later
                else:
                    print('    take taxi directly from origin to destination', time_from)

                    id_stage_taxi, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # could book taxi here in advance?
                        time_init=time_from,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
                    if id_stage_taxi == -1:
                        print('  taxi ride from origin to dest edege failed. id_stage_taxi', id_stage_taxi)
                        pass
                        # treated later

            # here we should have created a taxi ride
            # if not, for whatever reason,
            # we walk from origin to destination
            if id_stage_taxi == -1:
                print('    failed because no ride stage has been planned time_from',
                      time_from, time_from == time, 'id_stage_walk1', id_stage_walk1)
                return False, 0.0

        return True, time

    def plan(self, ids_person, logger=None):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        walkstages = plans.get_stagetable('walks')
        ridestages = plans.get_stagetable('SAVrides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = virtualpop.get_demand().activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        modes = scenario.net.modes

        # times_est_plan = plans.times_est

        # here we can determine edge weights for different modes
        plans.prepare_stagetables(['walks', 'SAVrides', 'activities'])

        # get initial travel times for persons.
        # initial travel times depend on the initial activity

        # landuse.parking.clear_booking()

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in SAVStrategy.plan: no eligible persons found.')
            return False

        # ok

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        # map_ids_parking_from = np.zeros(nm, dtype = np.int32)
        # ids_parking_from, inds_vehparking = parking.get_closest_parkings( virtualpop.ids_iauto[ids_person_act],
        # facilities.centroids[activities.ids_facility[ids_act_from]])
        # if len(ids_parking_from)==0:
        #    return False

        # err

        # map_ids_parking_from[ids_person_act] = ids_parking_from

        n_plans = len(ids_person_act)
        print('SAVStrategy.plan n_plans=', n_plans)
        # print '  map_ids_parking_from[ids_person_act].shape',map_ids_parking_from[ids_person_act].shape
        # set initial activity
        # this is because the following steps start with travel
        # and set the next activity
        # names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        # for id_plan

        ind_act = 0

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]

        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        # do initial stage
        # could be common to all strategies
        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]
            ids_veh = virtualpop.ids_imoto[ids_person_act]
            dists_walk_max = virtualpop.dists_walk_max[ids_person_act]
            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]
            poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

            centroids_from = facilities.centroids[ids_fac_from]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]

            poss_edge_to1 = facilities.positions_roadedge_closest[ids_fac_to]
            poss_edge_to = self.clip_positions(poss_edge_to1, ids_edge_to)
            centroids_to = facilities.centroids[ids_fac_to]

            dists_from_to = np.sqrt(np.sum((centroids_to - centroids_from)**2, 1))

            i = 0.0
            n_pers = len(ids_person_act)
            plans_failed = []
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, dist_from_to, dist_walk_max\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, dists_from_to, dists_walk_max):
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'*')
                print('  plan id_plan', id_plan, 'time_from', time_from, 'from',
                      id_edge_from, pos_edge_from, 'to', id_edge_to, pos_edge_to)
                print('    id_edge_from', edges.ids_sumo[id_edge_from], 'id_edge_to', edges.ids_sumo[id_edge_to])
                print('    id_act_from', id_act_from, 'id_act_to', id_act_to)

                is_ride, time = self.plan_taxiride(id_plan, time_from, id_veh,
                                                   id_edge_from, pos_edge_from,
                                                   id_edge_to, pos_edge_to,
                                                   dist_from_to, dist_walk_max,
                                                   walkstages, ridestages)
                if not is_ride:
                    plans_failed.append((id_person, id_plan))
                else:
                    # store time estimation for this plan
                    # note that these are the travel times, no activity time
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    # finally add activity and respective duration

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    map_times[id_person] = time

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed
            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
            # update timing with (random) activity duration!!

        return True


class MotorcycleStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='motorcycle strategy',
                 info='With this strategy, the person uses his private motorcycle as main transport mode.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes(**kwargs)
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self._init_attributes_strategy(**kwargs)
        self.n_iter_motoacces_max = attrsman.add(cm.AttrConf('n_iter_motoacces_max', kwargs.get('n_iter_motoacces_max', 5),
                                                             groupnames=['options'],
                                                             perm='rw',
                                                             name='Max. motorcycle access search iterations',
                                                             info='Max. number of iterations while searching an edge with motorcycle access.',
                                                             ))

        self.length_edge_min = attrsman.add(cm.AttrConf('length_edge_min', kwargs.get('length_edge_min', 20.0),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Min. edge length search',
                                                        unit='m',
                                                        info='Min. edge length when searching an edge with motorcycle access.',
                                                        ))

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', -0.0161),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for moto strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_ped = modes.get_id_mode('pedestrian')
        self._id_mode_bike = modes.get_id_mode('bicycle')
        self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_moto = modes.get_id_mode('motorcycle')
        self._edges = self.get_virtualpop().get_scenario().net.edges
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto',
            '_id_mode_ped',
            '_edges'])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        print('MotorcycleStrategy.preevaluate', n_pers, 'persons')
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # put -1 for persons without motorcycle access
        preeval[persons.ids_imoto[ids_person] == -1] = -1
        print('  persons having no motorcycle', len(np.flatnonzero(persons.ids_imoto[ids_person] == -1)))

        # put 0 for persons with motorcycle but with a different preferred mode
        preeval[(persons.ids_imoto[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] != self._id_mode_moto)] = 0

        print('  persons with motorcycle but with a different preferred mode', len(np.flatnonzero(
            (persons.ids_imoto[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] != self._id_mode_moto))))

        # put 2 for persons with motorcycle access and who prefer the car
        preeval[(persons.ids_imoto[ids_person] > -1)
                & (persons.ids_mode_preferred[ids_person] == self._id_mode_moto)] = 2
        print('  persons  with motorcycle access and who prefer the car', len(np.flatnonzero(
            (persons.ids_imoto[ids_person] > -1) & (persons.ids_mode_preferred[ids_person] == self._id_mode_moto))))

        return preeval

    def get_edge_motoaccess(self, id_edge, is_search_backward=False, is_get_route=False, is_star=False, bstar=0, fstar=0, n_iter_motoacces_max=None):
        # get firts edge allowing bikes and pedestrian, saving route, frontward or backward. You can include bstar or fstar:
        # check only connected links. You can obtain also the route.
        # print 'get_edge_bikeaccess',id_edge, is_search_backward,'id_sumo',self._edges.ids_sumo[id_edge]

        if n_iter_motoacces_max is None:
            n_iter_motoacces_max = self.n_iter_motoacces_max

        id_mode = self._id_mode_moto
        id_mode_ped = self._id_mode_ped
        get_accesslevel = self._edges.get_accesslevel
        if is_star:
            if is_search_backward:
                get_next = bstar
            else:
                get_next = fstar
        else:
            if is_search_backward:
                get_next = self._edges.get_incoming
            else:
                get_next = self._edges.get_outgoing

        edgelengths = self._edges.lengths
        # ids_tried = set()
        ids_current = [id_edge]
        id_motoedge = -1
        pos = 0.0
        n = 0
        ids = [id_edge]
        coll_ids = [0]
        route = []
        while (id_motoedge < 0) & (n < n_iter_motoacces_max):
            n += 1

            ids_new = []
            for id_edge_test, is_long_enough in zip(ids_current, edgelengths[ids_current] > self.length_edge_min):
                # print '    check id',id_edge_test, is_long_enough,get_accesslevel(id_edge_test, id_mode)
                if is_long_enough & (get_accesslevel(id_edge_test, id_mode) >= 0) & (get_accesslevel(id_edge_test, id_mode_ped) >= 0):
                    id_motoedge = id_edge_test

                    if is_get_route:
                        if n == 1:
                            route = []
                        else:
                            route.append(id_edge_test)
                            if n > 2:
                                route.append(coll_ids[ids.index(id_edge_test)])
                                if n > 3:
                                    for n in range(n-3):
                                        route.append(coll_ids[ids.index(route[-1])])
                        if is_search_backward is not True:
                            route.reverse()

                    # print '    found',id_motoedge,self._edges.ids_sumo[id_motoedge]
                    break
                else:

                    if is_star:
                        ids_new += get_next[id_edge_test]
                        if is_get_route:
                            ids += get_next[id_edge_test]
                            for i in range(len(get_next[id_edge_test])):
                                coll_ids.append(id_edge_test)

                    else:
                        ids_new += get_next(id_edge_test)
                        if is_get_route:
                            ids += get_next(id_edge_test)
                            for i in range(len(get_next(id_edge_test))):
                                coll_ids += id_edge_test

            ids_current = ids_new

        if id_motoedge > -1:
            if is_search_backward:
                pos = edgelengths[id_motoedge]-0.5*self.length_edge_min
            else:
                pos = 0.5*self.length_edge_min

        if id_motoedge == -1:
            print('WARNING in get_edge_motoaccess no access for', id_edge, self._edges.ids_sumo[id_edge])

        if is_get_route:
            return id_motoedge, pos, route
        else:
            return id_motoedge, pos

    def plan_motoride(self, id_plan, time_from, id_veh,
                      id_edge_from, pos_edge_from,
                      id_edge_to, pos_edge_to,
                      dist_from_to, dist_walk_max,
                      walkstages, ridestages):

        # start creating stages
        id_stage_walk1 = -1
        id_stage_moto = -1
        time = 0.0

        id_edge_from_moto, pos_from_moto = self.get_edge_motoaccess(id_edge_from)
        id_edge_to_moto, pos_to_moto = self.get_edge_motoaccess(id_edge_to, is_search_backward=True)

        if (dist_from_to < dist_walk_max) | (id_edge_from_moto == -1) | (id_edge_to_moto == -1):
            # print '    failed because distance is too short or no moto access',dist_from_to,id_edge_from_moto,id_edge_to_moto
            pass

        else:
            # print '    try to take the moto',id_veh
            # print '    id_edge_from_moto',edges.ids_sumo[id_edge_from_moto],pos_from_moto
            # print '    id_edge_to_moto',edges.ids_sumo[id_edge_to_moto],pos_to_moto

            if id_edge_from_moto != id_edge_from:
                # print '    must walk from origin to motorack',time_from

                id_stage_walk1, time = walkstages.append_stage(
                    id_plan, time_from,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=id_edge_from_moto,
                    position_edge_to=pos_from_moto,
                )

                if id_edge_to_moto != id_edge_to:
                    # print '    must ride from motorack to dest motorack',time
                    id_stage_moto, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time-10,  # time_from,
                        id_edge_from=id_edge_from_moto,
                        position_edge_from=pos_from_moto,
                        id_edge_to=id_edge_to_moto,
                        position_edge_to=pos_to_moto,
                    )
                    if id_stage_moto > -1:
                        # print '    must walk from dest motorack to dest',time
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_moto,
                            position_edge_from=pos_to_moto,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )

                else:
                    # print '    ride from motorack to destination',time
                    id_stage_moto, time = ridestages.append_stage(
                        id_plan, time,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time-10,  # time_from,
                        id_edge_from=id_edge_from_moto,
                        position_edge_from=pos_from_moto,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )
            else:
                # print '    cycle directly from orign edge',time_from
                if id_edge_to_moto != id_edge_to:
                    # print '    must ride from origin to motorack',time_from

                    # pos_to_bike = 0.1*edges.lengths[id_edge_to_bike]
                    id_stage_moto, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time_from-10,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to_moto,
                        position_edge_to=pos_to_moto,
                    )
                    if id_stage_moto > -1:
                        id_stage_walk2, time = walkstages.append_stage(
                            id_plan, time,
                            id_edge_from=id_edge_to_moto,
                            position_edge_from=pos_to_moto,
                            id_edge_to=id_edge_to,
                            position_edge_to=pos_edge_to,
                        )
                else:
                    # print '    must ride from origin to destination',time_from
                    id_stage_moto, time = ridestages.append_stage(
                        id_plan, time_from,
                        id_veh=id_veh,
                        # delay to be sure that person arrived!(workaround in combination with parking=False)
                        time_init=time_from-10,  # time_from,
                        id_edge_from=id_edge_from,
                        position_edge_from=pos_edge_from,
                        id_edge_to=id_edge_to,
                        position_edge_to=pos_edge_to,
                    )

            # here we should have created a moto ride
            # if not, for ehatever reason,
            # we walk from origin to destination
            if id_stage_moto == -1:
                # print '    failed  because no ride stage has been planned',time_from
                pass

        return id_stage_moto, time

    def plan(self, ids_person, logger=None):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        walkstages = plans.get_stagetable('walks')
        ridestages = plans.get_stagetable('motorides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = virtualpop.get_demand().activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        modes = scenario.net.modes

        # times_est_plan = plans.times_est

        # here we can determine edge weights for different modes
        plans.prepare_stagetables(['walks', 'motorides', 'activities'])

        # get initial travel times for persons.
        # initial travel times depend on the initial activity

        # landuse.parking.clear_booking()

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in MotorcycleStrategy.plan: no eligible persons found.')
            return False

        # ok

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        # map_ids_parking_from = np.zeros(nm, dtype = np.int32)
        # ids_parking_from, inds_vehparking = parking.get_closest_parkings( virtualpop.ids_iauto[ids_person_act],
        # facilities.centroids[activities.ids_facility[ids_act_from]])
        # if len(ids_parking_from)==0:
        #    return False

        # err

        # map_ids_parking_from[ids_person_act] = ids_parking_from

        n_plans = len(ids_person_act)
        print('MotorcycleStrategy.plan n_plans=', n_plans)
        # print '  map_ids_parking_from[ids_person_act].shape',map_ids_parking_from[ids_person_act].shape
        # set initial activity
        # this is because the following steps start with travel
        # and set the next activity
        # names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        # for id_plan

        ind_act = 0

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]

        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        # do initial stage
        # could be common to all strategies
        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]
            ids_veh = virtualpop.ids_imoto[ids_person_act]
            dists_walk_max = virtualpop.dists_walk_max[ids_person_act]
            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]
            poss_edge_from = self.clip_positions(poss_edge_from, ids_edge_from)

            centroids_from = facilities.centroids[ids_fac_from]

            # this method will find and occupy parking space
            # ids_parking_from = map_ids_parking_from[ids_person_act]

            # print '  ids_veh.shape',ids_veh.shape
            # print '  centroids_to.shape',centroids_to.shape
            # ids_parking_to, inds_vehparking = parking.get_closest_parkings(ids_veh, centroids_to)

            # ids_lane_parking_from = parking.ids_lane[ids_parking_from]
            # ids_edge_parking_from = lanes.ids_edge[ids_lane_parking_from]
            # poss_edge_parking_from = parking.positions[ids_parking_from]

            # print '  ids_parking_to.shape',ids_parking_to.shape
            # print '  np.max(parking.get_ids()), np.max(ids_parking_to)',np.max(parking.get_ids()), np.max(ids_parking_to)
            # ids_lane_parking_to = parking.ids_lane[ids_parking_to]
            # ids_edge_parking_to = lanes.ids_edge[ids_lane_parking_to]
            # poss_edge_parking_to = parking.positions[ids_parking_to]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]

            poss_edge_to1 = facilities.positions_roadedge_closest[ids_fac_to]
            poss_edge_to = self.clip_positions(poss_edge_to1, ids_edge_to)
            centroids_to = facilities.centroids[ids_fac_to]

            # debug poscorrection..OK
            # for id_edge, id_edge_sumo, length, pos_to1, pos in zip(ids_edge_to, edges.ids_sumo[ids_edge_to],edges.lengths[ids_edge_to],poss_edge_to1, poss_edge_to):
            #    print '  ',id_edge, 'IDe%s'%id_edge_sumo, 'L=%.2fm'%length, '%.2fm'%pos_to1, '%.2fm'%pos

            dists_from_to = np.sqrt(np.sum((centroids_to - centroids_from)**2, 1))

            i = 0.0
            plans_failed = []
            n_pers = len(ids_person_act)
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, dist_from_to, dist_walk_max\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, dists_from_to, dists_walk_max):
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'*')
                print('      plan for id_person %d/%d' % (id_person, n_pers), 'ind_act', ind_act, 'id_plan', id_plan)
                print('           time_from', time_from, 'from', id_edge_from,
                      pos_edge_from, 'to', id_edge_to, pos_edge_to)
                print('           id_edge_from', edges.ids_sumo[id_edge_from], 'id_edge_to', edges.ids_sumo[id_edge_to])

                id_stage_moto, time = self.plan_motoride(id_plan, time_from, id_veh,
                                                         id_edge_from, pos_edge_from,
                                                         id_edge_to, pos_edge_to,
                                                         dist_from_to, dist_walk_max,
                                                         walkstages, ridestages)

                if id_stage_moto == -1:
                    plans_failed.append((id_person, id_plan))
                else:
                    # store time estimation for this plan
                    # note that these are the travel times, no activity time
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    # finally add activity and respective duration

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    map_times[id_person] = time

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
            # update timing with (random) activity duration!!

        return True


class TransitStrategy(StrategyMixin):
    def __init__(self, ident, parent=None,
                 name='Public Transport Strategy',
                 info='With this strategy, the person uses public transport.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self, **kwargs):
        # print 'StrategyMixin._init_attributes'
        attrsman = self.get_attrsman()

        self.utility_constant = attrsman.add(cm.AttrConf('utility_constant', kwargs.get('utility_constant', 0.3727),
                                                         groupnames=['options'],
                                                         perm='rw',
                                                         name='Utility constant for transit strategy',
                                                         info='Constant to calculate utility: U = Const + value_of_time*time_exec',
                                                         ))

    def _init_constants(self):
        # virtualpop = self.get_virtualpop()
        # stagetables = virtualpop.get_stagetables()

        # self._walkstages = stagetables.get_stagetable('walks')
        # self._ridestages = stagetables.get_stagetable('rides')
        # self._activitystages = stagetables.get_stagetable('activities')

        # self._plans = virtualpop.get_plans()
        #
        # print 'AutoStrategy._init_constants'
        # print dir(self)
        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])

        modes = self.get_virtualpop().get_scenario().net.modes
        self._id_mode_bike = modes.get_id_mode('bicycle')
        self._id_mode_auto = modes.get_id_mode('passenger')
        self._id_mode_moto = modes.get_id_mode('motorcycle')
        self._id_mode_bus = modes.get_id_mode('bus')
        self.get_attrsman().do_not_save_attrs([
            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto', '_id_mode_bus'
        ])

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        # TODO: here we could exclude by age or distance facilities-stops

        # put 0 for persons whose preference is not public transport
        preeval[persons.ids_mode_preferred[ids_person] != self._id_mode_bus] = 0

        # put 2 for persons with car access and who prefer cars
        preeval[persons.ids_mode_preferred[ids_person] == self._id_mode_bus] = 2

        print('  TransitStrategy.preevaluate', len(np.flatnonzero(preeval)))
        return preeval

    def plan_transit(self, id_plan, time_from,
                     id_edge_from, pos_edge_from,
                     id_edge_to, pos_edge_to,
                     id_stop_from, id_stopedge_from, pos_stop_from,
                     id_stop_to, id_stopedge_to, pos_stop_to,
                     # dist_from_to, dist_walk_max,
                     walkstages, transitstages,
                     ptlines, ptfstar, pttimes,
                     stops_to_enter, stops_to_exit,
                     ids_laneedge, ids_stoplane, ptstops):

        edges = self.get_scenario().net.edges
        print('plan_transit id_plan', id_plan, 'id_edge_from %d (%s)' % (id_edge_from,
              edges.ids_sumo[id_edge_from]), 'id_edge_to %d (%s)' % (id_edge_to, edges.ids_sumo[id_edge_to]))

        # debug?
        is_debug = False  # id_plan == 14983

        if is_debug:

            print('  id_stop_from', id_stop_from, 'id_stop_to', id_stop_to)

        ptlinks = ptlines.get_ptlinks()
        ptlinktypes = ptlinks.types.choices
        type_enter = ptlinktypes['enter']
        type_transit = ptlinktypes['transit']
        type_board = ptlinktypes['board']
        type_alight = ptlinktypes['alight']
        type_transfer = ptlinktypes['transfer']
        type_walk = ptlinktypes['walk']
        type_exit = ptlinktypes['exit']

        if (id_edge_from == id_stopedge_from) & (abs(pos_edge_from-pos_stop_from) < 1.0):
            time = time_from
            id_stage_walk1 = None
            if is_debug:
                print('  no initial walk required.')
                print('    id_edge_from', id_edge_from, edges.ids_sumo[id_edge_from])
                print('    pos_edge_from', pos_edge_from)
        else:
            id_stage_walk1, time = walkstages.append_stage(id_plan, time_from,
                                                           id_edge_from=id_edge_from,
                                                           position_edge_from=pos_edge_from,
                                                           id_edge_to=id_stopedge_from,
                                                           position_edge_to=pos_stop_from,  # -7.0,
                                                           )
            if is_debug:
                print('  add initial walk stage')
                print('    id_edge_from', id_edge_from, edges.ids_sumo[id_edge_from])
                print('    pos_edge_from', pos_edge_from)
                print('     IIIInitial walk')
                print('    id_stopedge_from', id_stopedge_from, edges.ids_sumo[id_stopedge_from])
                print('    pos_stop_from', pos_stop_from)

        if is_debug:
            print('      TTTransit')
            print('    id_stopedge_to', id_stopedge_to, edges.ids_sumo[id_stopedge_to])
            print('    pos_stop_to', pos_stop_to)
            print(' ----------------------------------')
            print('    id_stop_from', id_stop_from)
            print('    id_stop_to', id_stop_to)

        durations, linktypes, ids_line, ids_fromstop, ids_tostop =\
            ptlinks.route(id_stop_from, id_stop_to,
                          fstar=ptfstar, times=pttimes,
                          stops_to_enter=stops_to_enter,
                          stops_to_exit=stops_to_exit)

        if is_debug:
            print('  routing done. make plan, success', len(linktypes) > 0)

        if is_debug:
            print('  ids_line    ', ids_line)
            print('  ids_fromstop', ids_fromstop)
            print('  ids_tostop  ', ids_tostop)

        if (type_transit in linktypes):  # is there any public transport line to take?

            # go though PT links and generate transits and walks to trasfer
            ids_stopedge_from = ids_laneedge[ids_stoplane[ids_fromstop]]
            ids_stopedge_to = ids_laneedge[ids_stoplane[ids_tostop]]
            poss_stop_from = 0.5*(ptstops.positions_from[ids_fromstop]
                                  + ptstops.positions_to[ids_fromstop])
            poss_stop_to = 0.5*(ptstops.positions_from[ids_tostop]
                                + ptstops.positions_to[ids_tostop])

            # this is wait time buffer to be added to the successive stage
            # as waiting is currently not modelled as an extra stage
            duration_wait = 0.0

            # check if initial walk needs to be modified
            if (id_stage_walk1 is not None) & (linktypes[0] == type_walk):
                if is_debug:
                    print('    Modify initial walk from stop fromedge %d (%s) toedge %d (%s)' % (id_edge_from,
                          edges.ids_sumo[id_edge_from], ids_stopedge_from[0], edges.ids_sumo[ids_stopedge_from[0]]))

                is_initial_walk_done = True
                time = walkstages.modify_stage(
                    id_stage_walk1,
                    id_edge_from=id_edge_from,
                    position_edge_from=pos_edge_from,
                    id_edge_to=ids_stopedge_to[0],
                    position_edge_to=poss_stop_to[0],
                )
            else:
                is_initial_walk_done = False

            # create stages for PT
            for i, linktype, id_line, duration, \
                id_stopedge_from, pos_fromstop, \
                id_stopedge_to, pos_tostop in\
                    zip(range(len(linktypes)),
                        linktypes,
                        ids_line,
                        durations,
                        ids_stopedge_from, poss_stop_from,
                        ids_stopedge_to, poss_stop_to,
                        ):
                if is_debug:
                    print('    ...........................')
                    print('    stage for linktype %2d fromedge %s toedge %s' %
                          (linktype, edges.ids_sumo[id_stopedge_from], edges.ids_sumo[id_stopedge_to]))
                    print('        id_stopedge_from,id_stopedge_to', id_stopedge_from, id_stopedge_to)

                if linktype == type_transit:  # transit!
                    # check if last link type has also been a transit
                    if i > 0:
                        if linktypes[i-1] == type_transit:
                            if is_debug:
                                print('    add inter-transit walk stage')
                            # introdice a walk stage to be sure that
                            # person gets to the middle of the stop
                            id_stage_transfer, time = walkstages.append_stage(
                                id_plan, time,
                                id_edge_from=ids_stopedge_to[i-1],
                                position_edge_from=poss_stop_to[i-1],
                                id_edge_to=ids_stopedge_to[i-1],
                                position_edge_to=poss_stop_to[i-1],
                                duration=0.0,  # moving within stop area
                            )

                    if is_debug:
                        print('    add transit stage id_line', id_line)
                    id_stage_transit, time = transitstages.append_stage(
                        id_plan, time,
                        id_line=id_line,
                        duration=duration+duration_wait,
                        id_fromedge=id_stopedge_from,
                        id_toedge=id_stopedge_to,
                    )
                    duration_wait = 0.0

                elif (linktype == type_walk) & ((i > 0) | (not is_initial_walk_done)):
                    # walk to transfer, no initial walk if done
                    if is_debug:
                        print('    add walk to transfer')
                    id_stage_transfer, time = walkstages.append_stage(
                        id_plan, time,
                        id_edge_from=id_stopedge_from,
                        position_edge_from=pos_fromstop,
                        id_edge_to=id_stopedge_to,
                        position_edge_to=pos_tostop,
                        duration=duration+duration_wait,
                    )
                    duration_wait = 0.0

                else:  # all other link time are no modelld
                    # do not do anything , just add wait time to next stage
                    if is_debug:
                        print('    do noting add duration', duration)
                    duration_wait += duration

            # walk from final stop to activity
            i = len(linktypes)-1  # last link index

            if is_debug:
                print('  linktypes', linktypes, 'i', i, 'linktypes[i]', linktypes[i], linktypes[i] == type_walk)

            if linktypes[i] == type_walk:
                if is_debug:
                    print('    Modify final walk from stop fromedge %d (%s) toedge %d (%s)' %
                          (id_stopedge_to, edges.ids_sumo[id_stopedge_to], id_edge_to, edges.ids_sumo[id_edge_to]))

                time = walkstages.modify_stage(
                    id_stage_transfer,
                    id_edge_from=ids_stopedge_from[i],
                    position_edge_from=poss_stop_from[i],
                    id_edge_to=id_edge_to,
                    position_edge_to=pos_edge_to,
                )

            else:
                if is_debug:
                    print('    Add final walk stage  fromedge %d (%s) toedge %d (%s)' %
                          (id_stopedge_to, edges.ids_sumo[id_stopedge_to], id_edge_to, edges.ids_sumo[id_edge_to]))

                id_stage_walk2, time = walkstages.append_stage(id_plan, time,
                                                               id_edge_from=id_stopedge_to,
                                                               position_edge_from=pos_tostop,
                                                               id_edge_to=id_edge_to,
                                                               position_edge_to=pos_edge_to,
                                                               )
            return True, time

        else:
            # there is no public transport line linking these nodes.
            if is_debug:
                print('  planning failed, No PT lines used create direct walk stage')

            return False, 0.0

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        print('TransitStrategy.plan', len(ids_person))
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        demand = virtualpop.get_demand()
        ptlines = demand.ptlines

        walkstages = plans.get_stagetable('walks')
        transitstages = plans.get_stagetable('transits')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = demand.activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        parking = landuse.parking

        scenario = virtualpop.get_scenario()
        net = scenario.net
        edges = net.edges
        lanes = net.lanes
        modes = net.modes

        ptstops = net.ptstops

        # print '   demand',demand
        # print '   demand.ptlines',demand.ptlines,dir(demand.ptlines)
        # print '   demand.ptlines.get_ptlinks()',demand.ptlines.get_ptlinks()
        # print '   demand.virtualpop',demand.virtualpop,dir(demand.virtualpop)
        # print '   demand.trips',demand.trips,dir(demand.trips)
        if len(ptlines) == 0:
            print('WARNING in TrasitStrategy.plan: no transit services available. Create public trasit services by connecting stops.')
            return False

        ptlinks = ptlines.get_ptlinks()
        if len(ptlinks) == 0:
            print('WARNING in TrasitStrategy.plan: no public transport links. Run methods: "create routes" and "build links" from public trasport services.')
            return False

        ptlinktypes = ptlinks.types.choices

        ptfstar = ptlinks.get_fstar()
        pttimes = ptlinks.get_times()
        stops_to_enter, stops_to_exit = ptlinks.get_stops_to_enter_exit()

        ids_stoplane = ptstops.ids_lane
        ids_laneedge = net.lanes.ids_edge

        times_est_plan = plans.times_est
        # here we can determine edge weights for different modes

        # this could be centralized to avoid redundance
        plans.prepare_stagetables(['walks', 'transits', 'activities'])

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in TrasitStrategy.plan: no eligible persons found.')
            return False

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        # ids_plan_act = virtualpop.add_plans(ids_person_act, id_strategy = self.get_id_strategy())
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        n_plans = len(ids_person_act)
        print('TrasitStrategy.plan n_plans=', n_plans)

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        ##

        ind_act = 0

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]

            times_from = map_times[ids_person_act]

            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            centroids_from = facilities.centroids[ids_fac_from]
            centroids_to = facilities.centroids[ids_fac_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]
            poss_edge_to = facilities.positions_roadedge_closest[ids_fac_to]

            ids_stop_from = ptstops.get_closest(centroids_from)
            ids_stop_to = ptstops.get_closest(centroids_to)

            ids_stopedge_from = ids_laneedge[ids_stoplane[ids_stop_from]]
            ids_stopedge_to = ids_laneedge[ids_stoplane[ids_stop_to]]

            # do random pos here
            poss_stop_from = 0.5*(ptstops.positions_from[ids_stop_from]
                                  + ptstops.positions_to[ids_stop_from])

            poss_stop_to = 0.5*(ptstops.positions_from[ids_stop_to]
                                + ptstops.positions_to[ids_stop_to])

            i = 0.0
            plans_failed = []
            n_pers = len(ids_person_act)
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, id_stop_from, id_stopedge_from, pos_stop_from, id_stop_to, id_stopedge_to, pos_stop_to\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to,  ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, ids_stop_from, ids_stopedge_from, poss_stop_from, ids_stop_to, ids_stopedge_to, poss_stop_to):
                n_pers = len(ids_person_act)
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'_')
                print('      plan for id_person %d/%d' % (id_person, n_pers), 'ind_act', ind_act, 'id_plan', id_plan)

                is_transit, time = self.plan_transit(id_plan, time_from,
                                                     id_edge_from, pos_edge_from,
                                                     id_edge_to, pos_edge_to,
                                                     id_stop_from, id_stopedge_from, pos_stop_from,
                                                     id_stop_to, id_stopedge_to, pos_stop_to,
                                                     # dist_from_to, dist_walk_max,
                                                     walkstages, transitstages,
                                                     ptlines, ptfstar, pttimes,
                                                     stops_to_enter, stops_to_exit,
                                                     ids_laneedge, ids_stoplane, ptstops)

                if not is_transit:
                    plans_failed.append((id_person, id_plan))
                else:
                    # update time for trips estimation for this plan
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    # store time for next iteration in case other activities are
                    # following
                    map_times[id_person] = time

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)


class BikeTransitStrategy(BikeStrategy, TransitStrategy):
    def __init__(self, ident, parent=None,
                 name='Bike+Public Transport Strategy',
                 info='With this strategy, the person combines bike and public transport.',
                 **kwargs):

        self._init_objman(ident, parent, name=name, info=info, **kwargs)
        attrsman = self.set_attrsman(cm.Attrsman(self))
        # specific init
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self):
        # print 'StrategyMixin._init_attributes'
        BikeStrategy._init_attributes(self)
        TransitStrategy._init_attributes(self)

    def _init_constants(self):

        BikeStrategy._init_constants(self)
        TransitStrategy._init_constants(self)

    def preevaluate(self, ids_person):
        """
        Preevaluation strategies for person IDs in vector ids_person.

        Returns a preevaluation vector with a preevaluation value 
        for each person ID. The values of the preevaluation vector are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        n_pers = len(ids_person)
        persons = self.get_virtualpop()
        preeval = np.zeros(n_pers, dtype=np.int32)

        inds_prefer = (persons.ids_mode_preferred[ids_person] == self._id_mode_bus)\
            | (persons.ids_mode_preferred[ids_person] == self._id_mode_bike)\

        inds_avail = persons.ids_ibike[ids_person] > -1
        preeval[np.logical_not(inds_avail)] = -1
        # TODO: here we could exclude by age or distance facilities-stops

        # put 0 for persons whose preference is not public transport
        preeval[inds_avail & np.logical_not(inds_prefer)] = 0

        # put 2 for persons with bike access and who prefer bike or bus
        preeval[inds_avail & inds_prefer] = 1

        print('  BikeTransitStrategy.preevaluate', len(np.flatnonzero(preeval)))
        return preeval

    def plan(self, ids_person, logger=None, **kwargs):
        """
        Generates a plan for these person according to this strategie.
        Overriden by specific strategy.
        """
        print('TransitStrategy.plan', len(ids_person))
        # make_plans_private(self, ids_person = None, mode = 'passenger')
        # routing necessary?
        virtualpop = self.get_virtualpop()
        plans = virtualpop.get_plans()  # self._plans
        demand = virtualpop.get_demand()
        ptlines = demand.ptlines

        walkstages = plans.get_stagetable('walks')
        transitstages = plans.get_stagetable('transits')
        ridestages = plans.get_stagetable('bikerides')
        activitystages = plans.get_stagetable('activities')

        activities = virtualpop.get_activities()
        activitytypes = demand.activitytypes
        landuse = virtualpop.get_landuse()
        facilities = landuse.facilities
        # parking = landuse.parking

        scenario = virtualpop.get_scenario()
        net = scenario.net
        edges = net.edges
        lanes = net.lanes
        modes = net.modes

        ptstops = net.ptstops

        # print '   demand',demand
        # print '   demand.ptlines',demand.ptlines,dir(demand.ptlines)
        # print '   demand.ptlines.get_ptlinks()',demand.ptlines.get_ptlinks()
        # print '   demand.virtualpop',demand.virtualpop,dir(demand.virtualpop)
        # print '   demand.trips',demand.trips,dir(demand.trips)
        ptlinks = ptlines.get_ptlinks()
        # print '  len(ptlinks)',len(ptlinks),'len(ptlines)',len(ptlines)
        if (len(ptlines) == 0) | (len(ptlinks) == 0):
            print('WARNING in TrasitStrategy.plan: no transit services available.')
            return False

        ptlinktypes = ptlinks.types.choices

        ptfstar = ptlinks.get_fstar()
        pttimes = ptlinks.get_times()
        stops_to_enter, stops_to_exit = ptlinks.get_stops_to_enter_exit()

        ids_stoplane = ptstops.ids_lane
        ids_laneedge = net.lanes.ids_edge

        times_est_plan = plans.times_est
        # here we can determine edge weights for different modes

        # this could be centralized to avoid redundance
        plans.prepare_stagetables(['walks', 'bikerides', 'transits', 'activities'])

        ids_person_act, ids_act_from, ids_act_to\
            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)

        if len(ids_person_act) == 0:
            print('WARNING in TrasitStrategy.plan: no eligible persons found.')
            return False

        # temporary maps from ids_person to other parameters
        nm = np.max(ids_person_act)+1
        map_ids_plan = np.zeros(nm, dtype=np.int32)
        # ids_plan_act = virtualpop.add_plans(ids_person_act, id_strategy = self.get_id_strategy())
        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())

        map_times = np.zeros(nm, dtype=np.int32)
        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')

        # set start time to plans (important!)
        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]

        # map_ids_fac_from = np.zeros(nm, dtype = np.int32)
        # map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]

        n_plans = len(ids_person_act)
        print('TrasitStrategy.plan n_plans=', n_plans)

        # make initial activity stage
        ids_edge_from = facilities.ids_roadedge_closest[activities.ids_facility[ids_act_from]]
        poss_edge_from = facilities.positions_roadedge_closest[activities.ids_facility[ids_act_from]]
        # this is the time when first activity starts
        # first activity is normally not simulated

        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
        durations_act_from = activities.get_durations(ids_act_from)
        times_from = map_times[ids_person_act]-durations_act_from
        # times_from = activities.get_times_end(ids_act_from, pdf = 'unit')

        for id_plan, \
            time, \
            id_act_from, \
            name_acttype_from, \
            duration_act_from, \
            id_edge_from, \
            pos_edge_from  \
            in zip(map_ids_plan[ids_person_act],
                   times_from,
                   ids_act_from,
                   names_acttype_from,
                   durations_act_from,
                   ids_edge_from,
                   poss_edge_from):

            id_stage_act, time = activitystages.append_stage(
                id_plan, time,
                ids_activity=id_act_from,
                names_activitytype=name_acttype_from,
                durations=duration_act_from,
                ids_lane=edges.ids_lanes[id_edge_from][0],
                positions=pos_edge_from,
            )

        ##

        ind_act = 0

        # main loop while there are persons performing
        # an activity at index ind_act
        while len(ids_person_act) > 0:
            ids_plan = map_ids_plan[ids_person_act]

            times_from = map_times[ids_person_act]

            ids_veh = virtualpop.ids_ibike[ids_person_act]
            dists_walk_max = virtualpop.dists_walk_max[ids_person_act]
            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
            durations_act_to = activities.get_durations(ids_act_to)

            ids_fac_from = activities.ids_facility[ids_act_from]
            ids_fac_to = activities.ids_facility[ids_act_to]

            centroids_from = facilities.centroids[ids_fac_from]
            centroids_to = facilities.centroids[ids_fac_to]

            # origin edge and position
            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]

            # destination edge and position
            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]
            poss_edge_to = facilities.positions_roadedge_closest[ids_fac_to]

            ids_stop_from = ptstops.get_closest(centroids_from)
            ids_stop_to = ptstops.get_closest(centroids_to)

            ids_stopedge_from = ids_laneedge[ids_stoplane[ids_stop_from]]
            ids_stopedge_to = ids_laneedge[ids_stoplane[ids_stop_to]]

            centroids_stop_from = ptstops.centroids[ids_stop_from]
            centroids_stop_to = ptstops.centroids[ids_stop_to]
            # do random pos here
            poss_stop_from = 0.5*(ptstops.positions_from[ids_stop_from]
                                  + ptstops.positions_to[ids_stop_from])

            poss_stop_to = 0.5*(ptstops.positions_from[ids_stop_to]
                                + ptstops.positions_to[ids_stop_to])

            dists_from_to = np.sqrt(np.sum((centroids_to - centroids_from)**2, 1))
            dists_from_stop_from = np.sqrt(np.sum((centroids_stop_from - centroids_from)**2, 1))
            dists_stop_to_to = np.sqrt(np.sum((centroids_to - centroids_stop_to)**2, 1))

            i = 0.0
            plans_failed = []
            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_veh, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, id_stop_from, id_stopedge_from, pos_stop_from, id_stop_to, id_stopedge_to, pos_stop_to, dists_from_to, dist_from_stop_from, dist_stop_to_to, dist_walk_max\
                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to, ids_veh, ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, ids_stop_from, ids_stopedge_from, poss_stop_from, ids_stop_to, ids_stopedge_to, poss_stop_to, dists_from_to, dists_from_stop_from, dists_stop_to_to, dists_walk_max):
                n_pers = len(ids_person_act)
                if logger:
                    logger.progress(i/n_pers*100)
                i += 1.0
                print(79*'_')
                print('  id_plan=%d, id_person=%d, ' % (id_plan, id_person))
                # dist_from_stop_from, dist_stop_to_to

                id_stage_bike1, time = self.plan_bikeride(id_plan, time_from, id_veh,
                                                          id_edge_from, pos_edge_from,
                                                          id_stopedge_from, pos_stop_from,
                                                          dist_from_stop_from, dist_walk_max,
                                                          walkstages, ridestages)

                is_transit, time = self.plan_transit(id_plan, time,
                                                     id_stopedge_from, pos_stop_from,
                                                     id_stopedge_to, pos_stop_to,
                                                     id_stop_from, id_stopedge_from, pos_stop_from,
                                                     id_stop_to, id_stopedge_to, pos_stop_to,
                                                     # dist_from_to, dist_walk_max,
                                                     walkstages, transitstages,
                                                     ptlines, ptfstar, pttimes,
                                                     stops_to_enter, stops_to_exit,
                                                     ids_laneedge, ids_stoplane, ptstops)

                id_stage_bike2, time = self.plan_bikeride(id_plan, time, id_veh,
                                                          id_stopedge_to, pos_stop_to,
                                                          id_edge_to, pos_edge_to,
                                                          dist_stop_to_to, dist_walk_max,
                                                          walkstages, ridestages)

                if ((id_stage_bike1 == -1) & (id_stage_bike1 == -1)) | (not is_transit):
                    # bike is not used on both legs or no transit is used
                    # there is no point for this strategy
                    plans_failed.append((id_person, id_plan))

                else:
                    # update time for trips estimation for this plan
                    plans.times_est[id_plan] += time-time_from

                    # define current end time without last activity duration
                    plans.times_end[id_plan] = time

                    id_stage_act, time = activitystages.append_stage(
                        id_plan, time,
                        ids_activity=id_act_to,
                        names_activitytype=name_acttype_to,
                        durations=duration_act_to,
                        ids_lane=edges.ids_lanes[id_edge_to][0],
                        positions=pos_edge_to,
                    )

                    # store time for next iteration in case other activities are
                    # following
                    map_times[id_person] = time

            # remove failed plans here
            for id_person, id_plan in plans_failed:
                # print '  remove failed plan for id_person',id_person,'id_plan',id_plan
                ids_person_act.remove(id_person)
                # removes completely this plan with stages from the databases
                virtualpop.del_plan(id_person, id_plan)

            del plans_failed

            # select persons and activities for next setp
            ind_act += 1
            ids_person_act, ids_act_from, ids_act_to\
                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)


class Strategies(am.ArrayObjman):
    def __init__(self, ident, virtualpop, **kwargs):
        self._init_objman(ident,
                          parent=virtualpop,
                          name='Mobility Strategies',
                          info="""Contains different mobility strategies. 
                            A strategy has methods to identify whether a strategy is applicaple to a person
                            and to make a plan fo a person.
                            """,
                          version=0.3,
                          **kwargs)

        self.add_col(am.ArrayConf('names',
                                  default='',
                                  dtype='object',
                                  perm='r',
                                  is_index=True,
                                  name='Short name',
                                  info='Strategy name. Must be unique, used as index.',
                                  ))
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self):

        self.add_col(cm.ObjsConf('strategies',
                                 groupnames=['state'],
                                 name='Strategy',
                                 info='Strategy object.',
                                 ))

        if hasattr(self, 'vot'):  # self.get_version() < 0.3:
            value_of_time_default = self.vot.get_value()
            self.delete('vot')
        else:
            value_of_time_default = 0.07/60  # here in euro per second

        self.add(cm.AttrConf('value_of_time', value_of_time_default,
                             groupnames=['parameters'],
                             name='Value of time (VOT)',
                             unit='1/s',
                             perm='rw',
                             info="This parameter weights the travel time in each strategies utility function: utility = utility_strategy_specific + value_of_time*time_exec +noise",
                             ))

        self.add_col(am.ArrayConf('colors', np.ones(4, np.float32),
                                  dtype=np.float32,
                                  groupnames=['parameters'],
                                  metatype='color',
                                  perm='rw',
                                  name='Color',
                                  info="Route color. Color as RGBA tuple with values from 0.0 to 1.0",
                                  xmltag='color',
                                  ))

        if self.get_version() < 0.2:
            self._set_colors_default()

        if self.get_version() < 0.3:
            self.set_version(0.3)

    def format_ids(self, ids):
        return ', '.join(self.names[ids])

    def get_id_from_formatted(self, idstr):
        return self.names.get_id_from_index(idstr)

    def get_ids_from_formatted(self, idstrs):
        return self.names.get_ids_from_indices_save(idstrs.split(','))

    def add_default(self):

        self.add_strategy('walk', WalkStrategy)
        self.add_strategy('auto', AutoStrategy)
        self.add_strategy('bike', BikeStrategy)
        self.add_strategy('transit', TransitStrategy)
        self.add_strategy('biketransit', BikeTransitStrategy)
        self.add_strategy('motorcycle', MotorcycleStrategy)
        self.add_strategy('taxi', TaxiStrategy)
        self.add_strategy('SAV', SAVStrategy)
        self._set_colors_default()

    def _set_colors_default(self):
        colors_default = {'walk': np.array([160, 72, 0, 220], np.float32)/255,
                          'auto': np.array([250, 213, 0, 220], np.float32)/255,
                          'bike': np.array([8, 191, 73, 210], np.float32)/255,
                          'transit': np.array([8, 77, 191, 220], np.float32)/255,
                          'biketransit': np.array([8, 201, 223, 220], np.float32)/255,
                          'motorcycle': np.array([170, 200, 0, 220], np.float32)/255,
                          'taxi': np.array([40, 240, 240, 220], np.float32)/255,
                          }
        ids = self.names.get_ids_from_indices_save(list(colors_default.keys()))
        self.colors[ids] = list(colors_default.values())  # np.array(colors_default.values(), np.float32).reshape(-1,4)

        # self.colors.indexset(colors_default.keys(), colors_default.values())

    def add_strategy(self, ident, Strategyclass, color=(0.0, 0.0, 0.0, 1.0)):
        # print 'add_strategy', ident
        if not self.names.has_index(ident):
            strategy = Strategyclass(ident, self)
            self.add_row(names=ident,
                         strategies=strategy,
                         colours=color,
                         )
            return strategy
        else:
            # print 'WARNING in add_strategy: strategy %s already initialized'%ident
            return self.strategies[self.names.get_id_from_index(ident)]

    def preevaluate(self, ids_person):
        """
        Preevaluation of strategies for person IDs in vector ids_person.

        Returns a vector with strategy IDs and a preevaluation matrix.
        The rows of the matrix corrispond to each person ID.
        The columns corrsopond to reck strategy ID.
        The values of the preevaluation matrix are as follows:
            -1 : Strategy cannot be applied
            0  : Stategy can be applied, but the preferred mode is not used
            1  : Stategy can be applied, and preferred mode is part of the strategy
            2  : Strategy uses predomunantly preferred mode

        """
        print('preevaluate strategies')
        ids_strat = self.get_ids()
        n_pers = len(ids_person)
        n_strat = len(ids_strat)

        preeval = np.zeros((n_pers, n_strat), dtype=np.int32)
        for i, strategy in zip(list(range(n_strat)), self.strategies[ids_strat]):
            print('  preevaluate strategiy', strategy.ident)
            preeval[i, :] = strategy.preevaluate(ids_person)

        return ids_strat, preeval


class StageTypeMixin(am.ArrayObjman):
    def init_stagetable(self, ident, stages,  name='', info="Stage of Plan"):

        self._init_objman(ident=ident, parent=stages, name=name,
                          info=info,
                          version=0.2,
                          )

        self.add_col(am.IdsArrayConf('ids_plan', self.get_plans(),
                                     # groupnames = ['state'],
                                     name='ID plan',
                                     info='ID of plan.',
                                     xmltag='type',
                                     ))

        self.add_col(am.ArrayConf('times_start', -1.0,
                                  groupnames=['parameters'],    # new
                                  name='Start time',
                                  unit='s',
                                  info='Planned or estimated time when this stage starts. Value -1 means unknown.',
                                  ))

        self.add_col(am.ArrayConf('durations', -1.0,
                                  name='Duration',
                                  groupnames=['parameters'],  # new
                                  unit='s',
                                  info='Planned or estimated duration for this stage starts. Value -1 means unknown.',
                                  xmltag='type',
                                  ))

    def _init_constants(self):
        self._fstarmap = {}
        self._timesmap = {}
        self._distancesmap = {}
        self.get_attrsman().do_not_save_attrs(['_fstarmap', '_timesmap', '_distancesmap'])
        self._init_constants_specific()

    def _init_constants_specific(self):
        # to be overridden
        pass

    def get_plans(self):
        return self.parent

    def get_ids_from_ids_plan(self, ids_plan):
        """
        Returns only stage IDs which are part of the given plans IDs. 
        """
        ids = self.get_ids()
        ids_common = set(self.ids_plan[ids]).intersection(ids_plan)
        print('get_ids_from_ids_plan for', self, type(ids_plan), len(ids_plan),
              'len(ids)', len(ids), 'len(ids_common)', len(ids_common))

        id_probe = 714843
        print('  ids_plan min', np.min(ids_plan), 'max', np.max(ids_plan), 'arr', ids_plan)
        print('  self.ids_plan[1:10]', self.ids_plan[1:10])

        print('  test', id_probe, id_probe in ids_plan, id_probe in self.ids_plan[ids])

        ids_common = set(self.ids_plan[ids]).intersection(ids_plan)
        # ids_plan_part = self.ids_plan[ids]
        are_selected = np.zeros(len(ids), dtype=np.bool_)
        for ind, id_plan_part in zip(range(len(ids)), self.ids_plan[ids]):
            are_selected[ind] = id_plan_part in ids_plan

        return ids[are_selected]
        # return  np.array(list(ids_common),dtype = np.int32)#
        # print '  ids_plan_part',type(ids_plan_part),ids_plan_part
        # print '  ids_selected',type(ids_plan_part.intersection(ids_plan)),ids_plan_part.intersection(ids_plan)
        # return np.array(list(ids_plan_part.intersection(ids_plan)), dtype = np.int32)

    def get_virtualpop(self):
        # print 'get_virtualpop  '
        return self.parent.parent

    def prepare_planning(self):
        pass

    def append_stage(self, id_plan, time_start,  **kwargs):

        # try to fix timing
        # if time_start<0:
        #    time_start_prev, duration_prev = self.parent.plans.get_timing_laststage(id_plan)
        #    if (duration_prev>=0)&(time_start_prev>=0):
        #        time_start = time_start_prev+duration_prev

        id_stage = self.add_row(ids_plan=id_plan,  times_start=time_start, **kwargs)
        # print 'STAGE.appended stage %s id_plan=%d, id_stage=%d, t=%d'%(self.get_name(),id_plan,id_stage,time_start)
        # for key in kwargs.keys():
        #    print '    %s=%s'%(key,kwargs[key])
        # print '    --id_plan, self, id_stage',id_plan, self, id_stage#,self.ids_plan.get_linktab()

        self.parent.append_stage(id_plan, self, id_stage)
        # print '  plan appended',id_plan, self, id_stage

        return id_stage, time_start+self.durations[id_stage]

    def modify_stage(self, id_stage, time_start=None,  **kwargs):
        if time_start is None:
            time_start = self.times_start[id_stage]
        self.set_row(id_stage, **kwargs)
        return time_start+self.durations[id_stage]

    def del_stage(self, id_stage):
        """Deletes a stage with id_stage. 
        Attention, the id_stage will not be deleted from the plan.
        """
        self.del_row(id_stage)

    def get_timing(self, id_stage):
        # ind = self.get_ind(id_stage)
        return self.times_start[id_stage], self.durations[id_stage]

    def get_times(self, id_mode):
        """
        Returns a vector mapping id_edge to edge travel times
        for given mode id_mode
        """
        print('get_times', list(self._timesmap.keys()))

        if id_mode not in self._timesmap:
            edges = self.get_virtualpop().get_scenario().net.edges
            self._timesmap[id_mode] = edges.get_times(id_mode=id_mode,
                                                      is_check_lanes=True,
                                                      )

        return self._timesmap[id_mode]

    def get_distances(self, id_mode):
        """
        Returns a vector mapping id_edge to edge distances
        for given mode id_mode
        """
        print('get_distances', list(self._distancesmap.keys()))

        if id_mode not in self._distancesmap:
            edges = self.get_virtualpop().get_scenario().net.edges
            self._distancesmap[id_mode] = edges.get_distances(id_mode=id_mode,
                                                              is_check_lanes=True,
                                                              )

        return self._distancesmap[id_mode]

    def get_fstar(self, id_mode,  is_return_arrays=True, is_ignor_connections=False):
        """
        Returns a fstar
        for given mode id_mode
        """
        print('get_fstar', list(self._fstarmap.keys()))

        if id_mode not in self._fstarmap:
            edges = self.get_virtualpop().get_scenario().net.edges
            self._fstarmap[id_mode] = edges.get_fstar(id_mode=id_mode,
                                                      is_ignor_connections=is_ignor_connections,
                                                      is_return_arrays=is_return_arrays,
                                                      )
        return self._fstarmap[id_mode]

    def to_xml(self, id_stage, fd, indent=0):
        """
        To be overridden by specific stage class.
        """
        pass


class TransitStages(StageTypeMixin):
    def __init__(self, ident, stages, name='Transit rides', info='Ride on a single public transport line (no transfers).'):
        self.init_stagetable(ident,
                             stages, name=name,
                             info=info,
                             )
        self._init_attributes()

    def _init_attributes(self):
        # ptstops =  self.parent.get_ptstops()
        edges = self.get_virtualpop().get_net().edges
        if hasattr(self, 'ids_fromstop'):
            self.delete('ids_fromstop')
            self.delete('ids_tostop')

        self.add_col(am.IdsArrayConf('ids_line', self.get_virtualpop().get_ptlines(),
                                     groupnames=['parameters'],
                                     name='ID line',
                                     info='ID of public transport line.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_fromedge', edges,
                                     groupnames=['parameters'],
                                     name='Edge ID from',
                                     info='Edge ID of departure bus stop or station.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_toedge', edges,
                                     groupnames=['parameters'],
                                     name='Edge ID to',
                                     info='Edge ID of destination bus stop or station.',
                                     ))

    def _init_constants_specific(self):
        self.get_attrsman().do_not_save_attrs(['_costs', '_fstar', ])

    def prepare_planning(self):
        ptlinks = self.ids_line.get_linktab().ptlinks.get_value()
        if len(ptlinks) == 0:
            # no links built...built them
            ptlinks.build()

        self._costs = ptlinks.get_times()
        self._fstar = ptlinks.get_fstar()

    def append_stage(self, id_plan, time_start=-1.0,
                     id_line=-1, duration=0.0,
                     id_fromedge=-1, id_toedge=-1, **kwargs):
        """
        Appends a transit stage to plan id_plan.

        """
        # print 'TransitStages.append_stage',id_stage

        id_stage, time_end = StageTypeMixin.append_stage(self,
                                                         id_plan,
                                                         time_start,
                                                         durations=duration,
                                                         ids_line=id_line,
                                                         ids_fromedge=id_fromedge,
                                                         ids_toedge=id_toedge,
                                                         )

        # add this stage to the vehicle database
        # ind_ride gives the index of this ride (within the same plan??)
        # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
        return id_stage, time_end

    def to_xml(self, id_stage, fd, indent=0):
        # <ride from="1/3to0/3" to="0/4to1/4" lines="train0"/>
        net = self.get_virtualpop().get_net()
        # ids_stoplane = net.ptstops.ids_lane
        # ids_laneedge = net.lanes.ids_edge
        ids_sumoedge = net.edges.ids_sumo

        # ind = self.get_ind(id_stage)
        fd.write(xm.start('ride', indent=indent))
        fd.write(xm.num('from', ids_sumoedge[self.ids_fromedge[id_stage]]))
        fd.write(xm.num('to', ids_sumoedge[self.ids_toedge[id_stage]]))
        fd.write(xm.num('lines', self.ids_line.get_linktab().linenames[self.ids_line[id_stage]]))
        # if self.cols.pos_edge_from[ind]>0:
        #    fd.write(xm.num('departPos', self.cols.pos_edge_from[ind]))
        # if self.cols.pos_edge_to[ind]>0:
        #    fd.write(xm.num('arrivalPos', self.cols.pos_edge_to[ind]))

        fd.write(xm.stopit())  # ends stage


class AutorideStages(StageTypeMixin):

    def __init__(self, ident, population,
                 name='Auto rides',
                 info='Rides with privately owned auto.',
                 version=1.1,
                 ):

        self.init_stagetable(ident, population, name=name, info=info)
        # print 'Rides.__init__',self.get_name()
        self._init_attributes()

    def _init_attributes(self):
        # TODO: this structure needs review: the private vehicle is part of a person, not a stage
        # street parking at home and work could be in stage. Private garage is part of person...
        # print '_init_attributes',self.parent.get_iautos(), self.ident,self.parent.get_landuse().parking

        self.add_col(am.IdsArrayConf('ids_iauto', self.get_virtualpop().get_iautos(),
                                     groupnames=['parameter'],
                                     name='ID vehicle',
                                     info='ID of private vehicle.',
                                     ))

        self.add_col(am.ArrayConf('times_init', -1.0,
                                  groupnames=['parameter'],
                                  name='Init. time',
                                  unit='s',
                                  info='Initialization time, which is the time when the vehicle appears in the scenario. Value -1 means unknown.',
                                  ))

        self.add_col(am.IdsArrayConf('ids_parking_from', self.get_virtualpop().get_landuse().parking,
                                     groupnames=['parameter'],
                                     name='ID dep. parking',
                                     info='Parking ID at the departure of the ride starts.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_parking_to', self.get_virtualpop().get_landuse().parking,
                                     groupnames=['parameter'],
                                     name='ID arr. parking',
                                     info='Parking ID  at the arrival of the ride.',
                                     ))

        self.add_col(am.IdlistsArrayConf('ids_edges', self.get_virtualpop().get_net().edges,
                                         groupnames=['parameter'],
                                         name='Route',
                                         info="The vehicle's route as a sequence of edge IDs.",
                                         ))

        if self.get_version() < 1.1:
            self.ids_edges.del_groupname('_private')

        self.add(cm.AttrConf('dist_ride_min', 400.0,
                             groupnames=['options'],
                             perm='rw',
                             name='Min ride dist.',
                             info='Minimum ride distance. If the distanve between parkings is less, then the person will walk.',
                             ))

        self.set_version(1.1)
        # if hasattr(self,'parking'):
        #    self.delete('parking')

    def prepare_planning(self):
        # now in _init_constants
        pass
        # net = self.get_virtualpop().get_net()
        # id_mode = net.modes.get_id_mode('passenger')
        # self._costs = {}
        # self._costs[id_mode] = net.edges.get_times( id_mode = id_mode,
        #                                        is_check_lanes = True)
        # self._fstars = {}
        # self._fstars[id_mode] =  net.edges.get_fstar(is_ignor_connections = False, id_mode = id_mode)
        # self._fstar = net.edges.get_fstar(is_ignor_connections = False)

    def append_stage(self, id_plan, id_mode,
                     is_fallback=False, id_mode_fallback=None,
                     time_start=-1.0, id_veh=-1, time_init=-1,
                     id_parking_from=.1, id_parking_to=-1, **kwargs):
        """
        Appends a ride stage to plan id_plan in case the ride is feasible.
        The ride is feasible if from parking and to parking are connected.

        If feasible, the stage ID and estimated time when stage is finished
        will be returned.

        If not feasible -1 and start time will be returned.
        """
        print('Rides.append_stage id_plan', id_plan, is_fallback)

        route, dist, duration, is_fallback = self.get_route_between_parking(
            id_parking_from, id_parking_to, id_mode,
            is_fallback, id_mode_fallback,)

        print('  after routing: is_fallback', is_fallback, 'route', route)

        if (len(route) > 0):  # |(dist > self.dist_ride_min.get_value()): # is there a connection
            # create stage
            id_stage, time_end = StageTypeMixin.append_stage(self,
                                                             id_plan,
                                                             time_start,
                                                             durations=duration,
                                                             times_init=time_init,
                                                             ids_iauto=id_veh,
                                                             ids_parking_from=id_parking_from,
                                                             ids_parking_to=id_parking_to,
                                                             ids_edges=route,
                                                             )

            # add this stage to the vehicle database
            # ind_ride gives the index of this ride (within the same plan??)
            # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
            return id_stage, time_end, is_fallback

        else:
            # not connected or too short of a distance
            return -1, time_start, is_fallback  # no stage creation took place

    def get_route_between_parking(self, id_parking_from, id_parking_to, id_mode,
                                  is_fallback=False, id_mode_fallback=None):
        """
        Return route and distance of ride with vehicle type  vtype
        between id_parking_from and id_parking_to


        """
        print('get_route_between_parking', id_parking_from, id_parking_to,
              'is_fallback', is_fallback, 'id_mode_fallback', id_mode_fallback)
        scenario = self.get_virtualpop().get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes

        # print   self.get_demand().getVehicles().cols.maxSpeed
        # v_max = self.get_demand().getVehicles().maxSpeed.get(vtype)
        parking = scenario.landuse.parking

        ids_lanes = parking.ids_lane[[id_parking_from, id_parking_to]]
        id_edge_from, id_edge_to = lanes.ids_edge[ids_lanes]
        pos_from, pos_to = parking.positions[[id_parking_from, id_parking_to]]

        if is_fallback & (id_mode_fallback is not None):
            print('   use id_mode_fallback', id_mode_fallback)
            id_mode_current = id_mode_fallback

        else:
            print('   use id_mode', id_mode)
            id_mode_current = id_mode

        # print '  id_edge_from, id_edge_to=',id_edge_from, id_edge_to
        duration_approx, route = routing.get_mincostroute_edge2edge(
            id_edge_from,
            id_edge_to,
            weights=self.get_times(id_mode_current),
            fstar=self.get_fstar(id_mode_current),
        )

        if (len(route) == 0) & (not is_fallback) & (id_mode_fallback is not None):
            is_fallback = True
            id_mode_current = id_mode_fallback
            print('  no route found with normal mode, try fallback', is_fallback)
            # now retry with fallback
            duration_approx, route = routing.get_mincostroute_edge2edge(
                id_edge_from,
                id_edge_to,
                weights=self.get_times(id_mode_current),
                fstar=self.get_fstar(id_mode_current),
            )
            if len(route) > 0:
                print('  fallback has been successful')

        # here is a big problem: starting with the successive node of edge_from
        # may result that the first edge of the route is not  connected with edge_from
        # And arriving at the preceding node of edge_to may result that from
        # the last edge in route the edge_to is not connected.

        # route = [edge_from]+route+[edge_to]
        dist = np.sum(edges.lengths[route])
        dist = dist - pos_from - (edges.lengths[id_edge_to] - pos_to)
        # if 0:
        #    if len(route)>0:
        #         print '  dist,duration',dist,duration_approx
        #    else:
        #         print '  no route found'

        return route, dist, duration_approx, is_fallback

    # def make_id_veh_ride(self, id_stage, i_ride):
    #    # make a unique vehicle ID for this stage
    #    self.inds_ride[id_stage] = i_ride
    #    return str(self.ids_veh[id_stage])+'.'+str(i_ride)

    def get_writexmlinfo(self, ids_plan, is_route=True, is_plain=False):
        print('AutorideStages.get_writexmlinfo', 'is_route', is_route, 'is_plain', is_plain)
        iautos = self.get_virtualpop().get_iautos()
        writefunc = iautos.prepare_write_xml(is_route, is_plain)

        # not all auto plans contain autoride stages!
        ids = self.get_ids_from_ids_plan(ids_plan)
        print('  found ', len(ids), 'AutorideStages from get_ids_from_ids_plan')
        writefuncs = np.zeros(len(ids), dtype=np.object_)
        writefuncs[:] = writefunc
        return self.times_init[ids], writefuncs, ids

    def to_xml(self, id_stage, fd, indent=0):
        # lanes = self.parent.get_scenario().net.lanes
        scenario = self.get_virtualpop().get_scenario()

        edges = scenario.net.edges
        edgeindex = edges.ids_sumo
        parking = scenario.landuse.parking

        ind = self.get_ind(id_stage)
        fd.write(xm.start('ride', indent=indent))

        id_edge_from, pos_from = parking.get_edge_pos_parking(self.ids_parking_from.value[ind])
        id_edge_to, pos = parking.get_edge_pos_parking(self.ids_parking_to.value[ind])

        # edgeindex.get_index_from_id(self.ids_edge_to.value[ind])
        fd.write(xm.num('from', edgeindex[id_edge_from]))
        fd.write(xm.num('to',  edgeindex[id_edge_to]))

        length = max(edges.lengths[id_edge_to]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))

        fd.write(xm.num('lines', self.ids_iauto.get_linktab().get_id_line_xml(
            self.ids_iauto[id_stage])))  # mode specific

        fd.write(xm.stopit())  # ends stage


class BikerideStages(StageTypeMixin):

    def __init__(self, ident, population,
                 name='Bike rides',
                 info='Rides with privately owned bike.',
                 version=1.0,
                 ):

        self.init_stagetable(ident, population, name=name, info=info)
        # print 'Rides.__init__',self.get_name()
        self._init_attributes()

    def _init_attributes(self):
        # TODO: this structure needs review: the private vehicle is part of a person, not a stage
        # street parking at home and work could be in stage. Private garage is part of person...
        # print '_init_attributes',self.parent.get_iautos(), self.ident,self.parent.get_landuse().parking

        self.add_col(am.IdsArrayConf('ids_ibike', self.get_virtualpop().get_ibikes(),
                                     groupnames=['state'],
                                     name='ID bike',
                                     info='ID of private, individual bike.',
                                     ))

        self.add_col(am.ArrayConf('times_init', -1.0,
                                  name='Init. time',
                                  unit='s',
                                  info='Initialization time, which is the time when the vehicle appears in the scenario. Value -1 means unknown.',
                                  ))

        edges = self.get_virtualpop().get_net().edges

        self.add_col(am.IdsArrayConf('ids_edge_from', edges,
                                     groupnames=['state'],
                                     name='ID Dep. edge',
                                     info='Edge ID at departure of walk.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_edge_to', edges,
                                     groupnames=['state'],
                                     name='ID Arr. edge',
                                     info='Edge ID where walk finishes.',
                                     ))

        self.add_col(am.ArrayConf('positions_from', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_DEPARTURE,
                                  perm='r',
                                  name='Depart pos',
                                  unit='m',
                                  info="Position on edge at the moment of departure.",
                                  # xmltag = 'departPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.ArrayConf('positions_to', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_ARRIVAL,
                                  perm='r',
                                  name='Arrival pos',
                                  unit='m',
                                  info="Position on edge at the moment of arrival.",
                                  # xmltag = 'arrivalPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.IdlistsArrayConf('ids_edges', self.get_virtualpop().get_net().edges,
                                         groupnames=['_private'],
                                         name='Route',
                                         info="The vehicle's route as a sequence of edge IDs.",
                                         ))

    def _init_constants_specific(self):
        net = self.get_virtualpop().get_net()
        self.id_mode_bike = net.modes.get_id_mode('bicycle')
        self.get_attrsman().do_not_save_attrs(['id_mode_bike', ])

    def prepare_planning(self):
        pass

    def append_stage(self, id_plan, time_start=-1.0, id_veh=-1,
                     time_init=-1,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.0, position_edge_to=0.0,
                     is_route=True, route=-1,  duration_approx=-1,
                     **kwargs):
        """
        Appends a ride stage to plan id_plan in case the ride is feasible.
        The ride is feasible if from parking and to parking are connected.

        If feasible, the stage ID and estimated time when stage is finished
        will be returned.

        If not feasible -1 and start time will be returned.
        """
        # print 'BikeRides.append_stage',id_plan,time_start,time_init
        # edges = self.get_virtualpop().get_net().edges
        # check feasibility
        # route, dist, duration = self.get_route_between_parking(id_parking_from, id_parking_to)

        # print '  id_edge_from, id_edge_to=',id_edge_from, id_edge_to

        if is_route:
            duration_approx, route = routing.get_mincostroute_edge2edge(
                id_edge_from,
                id_edge_to,
                weights=self.get_times(self.id_mode_bike),
                fstar=self.get_fstar(self.id_mode_bike),
            )

        # route = [edge_from]+route+[edge_to]

        # dist = np.sum(edges.lengths[route])
        # dist =  dist - pos_from - ( edges.lengths[id_edge_to] - pos_to)

        if (len(route) > 0):  # |(dist > self.dist_ride_min.get_value()): # is there a connection
            # create stage
            id_stage, time_end = StageTypeMixin.append_stage(self,
                                                             id_plan,
                                                             time_start,
                                                             durations=duration_approx,
                                                             times_init=time_init,
                                                             ids_ibike=id_veh,
                                                             ids_edge_from=id_edge_from,
                                                             positions_from=position_edge_from,
                                                             ids_edge_to=id_edge_to,
                                                             positions_to=position_edge_to,
                                                             ids_edges=route,
                                                             )

            # print '  route = ',route
            # print '  ids_edges = ',self.ids_edges[id_stage]
            # add this stage to the vehicle database
            # ind_ride gives the index of this ride (within the same plan??)
            # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
            return id_stage, time_end

        else:
            # not connected or too short of a distance
            return -1, time_start  # no stage creation took place

    def get_writexmlinfo(self, ids_plan, is_route=True, is_plain=False):
        print('BikerideStages.get_writexmlinfo', 'is_route', is_route, 'is_plain', is_plain, len(ids_plan), len(self))
        ibikes = self.get_virtualpop().get_ibikes()
        bikewritefunc = ibikes.prepare_write_xml(is_plain=is_plain)
        ids = self.get_ids_from_ids_plan(ids_plan)
        print('  found ', len(ids), 'BikerideStages from get_ids_from_ids_plan')
        bikewritefuncs = np.zeros(len(ids), dtype=np.object_)
        bikewritefuncs[:] = bikewritefunc
        return self.times_init[ids], bikewritefuncs, ids

    def to_xml(self, id_stage, fd, indent=0):
        scenario = self.get_virtualpop().get_scenario()

        edges = scenario.net.edges
        edgeindex = edges.ids_sumo

        fd.write(xm.start('ride', indent=indent))

        id_edge_from = self.ids_edge_from[id_stage]
        fd.write(xm.num('from', edgeindex[self.ids_edge_from[id_stage]]))
        fd.write(xm.num('to',  edgeindex[self.ids_edge_to[id_stage]]))

        pos = self.positions_to[id_stage]
        length = max(edges.lengths[self.ids_edge_to[id_stage]]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))

        fd.write(xm.num('lines', self.ids_ibike.get_linktab().get_id_line_xml(
            self.ids_ibike[id_stage])))  # mode specific

        fd.write(xm.stopit())  # ends stage


class MotorcyclerideStages(StageTypeMixin):

    def __init__(self, ident, population,
                 name='Motorcycle rides',
                 info='Rides with privately owned motorcycle.',
                 version=1.0,
                 ):

        self.init_stagetable(ident, population, name=name, info=info)
        # print 'Rides.__init__',self.get_name()
        self._init_attributes()

    def _init_attributes(self):
        # TODO: this structure needs review: the private vehicle is part of a person, not a stage
        # street parking at home and work could be in stage. Private garage is part of person...
        # print '_init_attributes',self.parent.get_iautos(), self.ident,self.parent.get_landuse().parking

        self.add_col(am.IdsArrayConf('ids_imoto', self.get_virtualpop().get_imotos(),
                                     groupnames=['state'],
                                     name='ID moto',
                                     info='ID of private, individual motorcycle.',
                                     ))

        self.add_col(am.ArrayConf('times_init', -1.0,
                                  name='Init. time',
                                  unit='s',
                                  info='Initialization time, which is the time when the vehicle appears in the scenario. Value -1 means unknown.',
                                  ))

        edges = self.get_virtualpop().get_net().edges

        self.add_col(am.IdsArrayConf('ids_edge_from', edges,
                                     groupnames=['state'],
                                     name='ID Dep. edge',
                                     info='Edge ID at departure of walk.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_edge_to', edges,
                                     groupnames=['state'],
                                     name='ID Arr. edge',
                                     info='Edge ID where walk finishes.',
                                     ))

        self.add_col(am.ArrayConf('positions_from', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_DEPARTURE,
                                  perm='r',
                                  name='Depart pos',
                                  unit='m',
                                  info="Position on edge at the moment of departure.",
                                  # xmltag = 'departPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.ArrayConf('positions_to', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_ARRIVAL,
                                  perm='r',
                                  name='Arrival pos',
                                  unit='m',
                                  info="Position on edge at the moment of arrival.",
                                  # xmltag = 'arrivalPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.IdlistsArrayConf('ids_edges', self.get_virtualpop().get_net().edges,
                                         groupnames=['_private'],
                                         name='Route',
                                         info="The vehicle's route as a sequence of edge IDs.",
                                         ))

    def _init_constants_specific(self):
        net = self.get_virtualpop().get_net()
        self.id_mode_moto = net.modes.get_id_mode('motorcycle')
        self.get_attrsman().do_not_save_attrs(['id_mode_moto', ])

    def prepare_planning(self):
        pass

    def append_stage(self, id_plan, time_start=-1.0, id_veh=-1,
                     time_init=-1,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.0, position_edge_to=0.0,
                     is_route=True, route=-1,  duration_approx=-1,
                     **kwargs):
        """
        Appends a ride stage to plan id_plan in case the ride is feasible.
        The ride is feasible if from parking and to parking are connected.

        If feasible, the stage ID and estimated time when stage is finished
        will be returned.

        If not feasible -1 and start time will be returned.
        """
        # print 'BikeRides.append_stage',id_plan,time_start,time_init
        # edges = self.get_virtualpop().get_net().edges
        # check feasibility
        # route, dist, duration = self.get_route_between_parking(id_parking_from, id_parking_to)

        # print '  id_edge_from, id_edge_to=',id_edge_from, id_edge_to
        if is_route:
            duration_approx, route = routing.get_mincostroute_edge2edge(
                id_edge_from,
                id_edge_to,
                weights=self.get_times(self.id_mode_moto),
                fstar=self.get_fstar(self.id_mode_moto),
            )

        # route = [edge_from]+route+[edge_to]

        # dist = np.sum(edges.lengths[route])
        # dist =  dist - pos_from - ( edges.lengths[id_edge_to] - pos_to)

        if (len(route) > 0):  # |(dist > self.dist_ride_min.get_value()): # is there a connection
            # create stage
            id_stage, time_end = StageTypeMixin.append_stage(self,
                                                             id_plan,
                                                             time_start,
                                                             durations=duration_approx,
                                                             times_init=time_init,
                                                             ids_imoto=id_veh,
                                                             ids_edge_from=id_edge_from,
                                                             positions_from=position_edge_from,
                                                             ids_edge_to=id_edge_to,
                                                             positions_to=position_edge_to,
                                                             ids_edges=route,
                                                             )

            # print '  route = ',route
            # print '  ids_edges = ',self.ids_edges[id_stage]
            # add this stage to the vehicle database
            # ind_ride gives the index of this ride (within the same plan??)
            # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
            return id_stage, time_end

        else:
            # not connected or too short of a distance
            return -1, time_start  # no stage creation took place

    def get_writexmlinfo(self, ids_plan, is_route=True, is_plain=False):
        print('MotorcyclerideStages.get_writexmlinfo', 'is_route', is_route, 'is_plain', is_plain)
        imotos = self.get_virtualpop().get_imotos()
        motowritefunc = imotos.prepare_write_xml(is_plain=is_plain)
        ids = self.get_ids_from_ids_plan(ids_plan)
        print('  found ', len(ids), 'MotorcyclerideStages from get_ids_from_ids_plan')
        motowritefuncs = np.zeros(len(ids), dtype=np.object_)
        motowritefuncs[:] = motowritefunc
        return self.times_init[ids], motowritefuncs, ids

    def to_xml(self, id_stage, fd, indent=0):
        # lanes = self.parent.get_scenario().net.lanes
        scenario = self.get_virtualpop().get_scenario()

        edges = scenario.net.edges
        edgeindex = edges.ids_sumo

        # parking = scenario.landuse.parking

        # ind = self.get_ind(id_stage)
        fd.write(xm.start('ride', indent=indent))

        # id_edge_from, pos_from = parking.get_edge_pos_parking(self.ids_parking_from.value[ind])
        # id_edge_to, pos_to = parking.get_edge_pos_parking(self.ids_parking_to.value[ind])

        # edgeindex.get_index_from_id(self.ids_edge_to.value[ind])
        id_edge_from = self.ids_edge_from[id_stage]
        fd.write(xm.num('from', edgeindex[self.ids_edge_from[id_stage]]))
        fd.write(xm.num('to',  edgeindex[self.ids_edge_to[id_stage]]))
        pos = self.positions_to[id_stage]
        length = max(edges.lengths[self.ids_edge_to[id_stage]]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))
        fd.write(xm.num('lines', self.ids_imoto.get_linktab().get_id_line_xml(
            self.ids_imoto[id_stage])))  # mode specific

        fd.write(xm.stopit())  # ends stage


class TaxirideStages(StageTypeMixin):

    def __init__(self, ident, population,
                 name='Taxi rides',
                 info='Rides with Taxi.',
                 version=1.0,
                 ):

        self.init_stagetable(ident, population, name=name, info=info)
        # print 'Rides.__init__',self.get_name()
        self._init_attributes()

    def _init_attributes(self):

        self.add_col(am.ArrayConf('times_init', -1.0,
                                  name='Init. time',
                                  unit='s',
                                  info='Initialization time, which is the time when the vehicle appears in the scenario. Value -1 means unknown.',
                                  ))

        edges = self.get_virtualpop().get_net().edges

        self.add_col(am.IdsArrayConf('ids_edge_from', edges,
                                     groupnames=['state'],
                                     name='ID Dep. edge',
                                     info='Edge ID at departure of walk.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_edge_to', edges,
                                     groupnames=['state'],
                                     name='ID Arr. edge',
                                     info='Edge ID where walk finishes.',
                                     ))

        self.add_col(am.ArrayConf('positions_from', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_DEPARTURE,
                                  perm='r',
                                  name='Depart pos',
                                  unit='m',
                                  info="Position on edge at the moment of departure.",
                                  # xmltag = 'departPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.ArrayConf('positions_to', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_ARRIVAL,
                                  perm='r',
                                  name='Arrival pos',
                                  unit='m',
                                  info="Position on edge at the moment of arrival.",
                                  # xmltag = 'arrivalPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

    def _init_constants_specific(self):
        net = self.get_virtualpop().get_net()
        self.id_mode_taxi = net.modes.get_id_mode('taxi')
        self.get_attrsman().do_not_save_attrs(['id_mode_taxi', ])

    def prepare_planning(self):
        pass

    def append_stage(self, id_plan, time_start=-1.0,
                     time_init=-1,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.0, position_edge_to=0.0,
                     duration_approx=-1,
                     is_route=True,
                     route=[],
                     **kwargs):
        """
        Appends a ride stage to plan id_plan in case the ride is feasible.


        If feasible, the stage ID and estimated time when stage is finished
        will be returned.

        If not feasible -1 and start time will be returned.
        """

        # print '  id_edge_from, id_edge_to=',id_edge_from, id_edge_to
        if is_route:
            duration_approx, route = routing.get_mincostroute_edge2edge(
                id_edge_from,
                id_edge_to,
                weights=self.get_times(self.id_mode_taxi),
                fstar=self.get_fstar(self.id_mode_taxi),
            )

        if (len(route) > 0):  # |(dist > self.dist_ride_min.get_value()): # is there a connection
            # create stage
            id_stage, time_end = StageTypeMixin.append_stage(self,
                                                             id_plan,
                                                             time_start,
                                                             durations=duration_approx,
                                                             times_init=time_init,
                                                             ids_edge_from=id_edge_from,
                                                             positions_from=position_edge_from,
                                                             ids_edge_to=id_edge_to,
                                                             positions_to=position_edge_to,
                                                             )

            # print '  route = ',route
            # print '  ids_edges = ',self.ids_edges[id_stage]
            # add this stage to the vehicle database
            # ind_ride gives the index of this ride (within the same plan??)
            # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
            return id_stage, time_end

        else:
            # not connected or too short of a distance
            return -1, time_start  # no stage creation took place

    def to_xml(self, id_stage, fd, indent=0):
        scenario = self.get_virtualpop().get_scenario()

        edges = scenario.net.edges
        edgeindex = edges.ids_sumo

        fd.write(xm.start('ride', indent=indent))

        id_edge_from = self.ids_edge_from[id_stage]
        fd.write(xm.num('from', edgeindex[self.ids_edge_from[id_stage]]))
        fd.write(xm.num('to',  edgeindex[self.ids_edge_to[id_stage]]))

        pos = self.positions_to[id_stage]
        length = max(edges.lengths[self.ids_edge_to[id_stage]]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))

        fd.write(xm.num('lines', 'taxi:fleetPrivate'))  # mode specific

        fd.write(xm.stopit())  # ends stage


class SAVrideStages(StageTypeMixin):

    def __init__(self, ident, population,
                 name='SAV rides',
                 info='Rides with SAVs.',
                 version=1.0,
                 ):

        self.init_stagetable(ident, population, name=name, info=info)
        # print 'Rides.__init__',self.get_name()
        self._init_attributes()

    def _init_attributes(self):

        self.add_col(am.ArrayConf('times_init', -1.0,
                                  name='Init. time',
                                  unit='s',
                                  info='Initialization time, which is the time when the vehicle appears in the scenario. Value -1 means unknown.',
                                  ))

        edges = self.get_virtualpop().get_net().edges

        self.add_col(am.IdsArrayConf('ids_edge_from', edges,
                                     groupnames=['state'],
                                     name='ID Dep. edge',
                                     info='Edge ID at departure of walk.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_edge_to', edges,
                                     groupnames=['state'],
                                     name='ID Arr. edge',
                                     info='Edge ID where walk finishes.',
                                     ))

        self.add_col(am.ArrayConf('positions_from', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_DEPARTURE,
                                  perm='r',
                                  name='Depart pos',
                                  unit='m',
                                  info="Position on edge at the moment of departure.",
                                  # xmltag = 'departPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.add_col(am.ArrayConf('positions_to', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_ARRIVAL,
                                  perm='r',
                                  name='Arrival pos',
                                  unit='m',
                                  info="Position on edge at the moment of arrival.",
                                  # xmltag = 'arrivalPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

    def _init_constants_specific(self):
        net = self.get_virtualpop().get_net()
        self.id_mode_taxi = net.modes.get_id_mode('taxi')
        self.get_attrsman().do_not_save_attrs(['id_mode_taxi', ])

    def prepare_planning(self):
        pass

    def append_stage(self, id_plan, time_start=-1.0,
                     time_init=-1,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.0, position_edge_to=0.0,
                     duration_approx=-1,
                     is_route=True,
                     route=[],
                     **kwargs):
        """
        Appends a ride stage to plan id_plan in case the ride is feasible.


        If feasible, the stage ID and estimated time when stage is finished
        will be returned.

        If not feasible -1 and start time will be returned.
        """

        # print '  id_edge_from, id_edge_to=',id_edge_from, id_edge_to
        if is_route:
            duration_approx, route = routing.get_mincostroute_edge2edge(
                id_edge_from,
                id_edge_to,
                weights=self.get_times(self.id_mode_taxi),
                fstar=self.get_fstar(self.id_mode_taxi),
            )

        if (len(route) > 0):  # |(dist > self.dist_ride_min.get_value()): # is there a connection
            # create stage
            id_stage, time_end = StageTypeMixin.append_stage(self,
                                                             id_plan,
                                                             time_start,
                                                             durations=duration_approx,
                                                             times_init=time_init,
                                                             ids_edge_from=id_edge_from,
                                                             positions_from=position_edge_from,
                                                             ids_edge_to=id_edge_to,
                                                             positions_to=position_edge_to,
                                                             )

            # print '  route = ',route
            # print '  ids_edges = ',self.ids_edges[id_stage]
            # add this stage to the vehicle database
            # ind_ride gives the index of this ride (within the same plan??)
            # ind_ride = self.parent.get_iautos().append_ride(id_veh, id_stage)
            return id_stage, time_end

        else:
            # not connected or too short of a distance
            return -1, time_start  # no stage creation took place

    def to_xml(self, id_stage, fd, indent=0):
        scenario = self.get_virtualpop().get_scenario()

        edges = scenario.net.edges
        edgeindex = edges.ids_sumo

        fd.write(xm.start('ride', indent=indent))

        id_edge_from = self.ids_edge_from[id_stage]
        fd.write(xm.num('from', edgeindex[self.ids_edge_from[id_stage]]))
        fd.write(xm.num('to',  edgeindex[self.ids_edge_to[id_stage]]))

        pos = self.positions_to[id_stage]
        length = max(edges.lengths[self.ids_edge_to[id_stage]]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))

        fd.write(xm.num('lines', 'taxi:fleetSAV'))  # mode specific

        fd.write(xm.stopit())  # ends stage


class WalkStages(StageTypeMixin):
    def __init__(self, ident, stages, name='WalkStages',
                 info='walk from a position on a lane to another position of another lane.'):

        self.init_stagetable(ident, stages, name=name, info=info)

        edges = self.get_virtualpop().get_scenario().net.edges
        self.add_col(am.IdsArrayConf('ids_edge_from', edges,
                                     groupnames=['state'],
                                     name='ID Dep. edge',
                                     info='Edge ID at departure of walk.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_edge_to', edges,
                                     groupnames=['state'],
                                     name='ID Arr. edge',
                                     info='Edge ID where walk finishes.',
                                     ))

        self.add_col(am.ArrayConf('positions_from', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_DEPARTURE,
                                  perm='r',
                                  name='Depart pos',
                                  unit='m',
                                  info="Position on edge at the moment of departure.",
                                  # xmltag = 'departPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))
        self.positions_from.set_xmltag(None)

        self.add_col(am.ArrayConf('positions_to', 0.0,
                                  dtype=np.float32,
                                  # choices = OPTIONMAP_POS_ARRIVAL,
                                  perm='r',
                                  name='Arrival pos',
                                  unit='m',
                                  info="Position on edge at the moment of arrival.",
                                  # xmltag = 'arrivalPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.positions_to.set_xmltag(None)

    def append_stage(self, id_plan, time_start=-1.0,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.1, position_edge_to=0.0,
                     **kwargs):
        # print 'WalkStages.append_stage',id_stage
        if 'duration' in kwargs:
            duration = kwargs['duration']
        else:
            dist, duration = self.plan_walk(id_edge_from, id_edge_to,
                                            position_edge_from, position_edge_to)

        id_stage, time_end = StageTypeMixin.append_stage(self,
                                                         id_plan,
                                                         time_start,
                                                         durations=duration,
                                                         ids_edge_from=id_edge_from,
                                                         ids_edge_to=id_edge_to,
                                                         positions_from=position_edge_from,
                                                         positions_to=position_edge_to,
                                                         )

        return id_stage, time_end

    def modify_stage(self, id_stage, time_start=None,
                     id_edge_from=-1, id_edge_to=-1,
                     position_edge_from=0.1, position_edge_to=0.0):

        dist, duration = self.plan_walk(id_edge_from, id_edge_to,
                                        position_edge_from, position_edge_to)

        time_end = StageTypeMixin.modify_stage(self, id_stage, time_start,
                                               durations=duration,
                                               ids_edge_from=id_edge_from,
                                               ids_edge_to=id_edge_to,
                                               positions_from=position_edge_from,
                                               positions_to=position_edge_to,
                                               )

        return time_end

    def plan_walk(self, id_edge_from, id_edge_to, pos_from, pos_to, id_mode=1):
        """
        Routing for pedestrians.
        Currently limited to estimation of line of sight distance
        and walk time.
        """
        # print 'plan_walk',id_edge_from, id_edge_to,id_mode, pos_from, pos_to
        scenario = self.get_virtualpop().get_scenario()
        edges = scenario.net.edges

        coord_from = edges.get_coord_from_pos(id_edge_from, pos_from)
        coord_to = edges.get_coord_from_pos(id_edge_to, pos_to)

        # from lanes, more precis, but less efficient and less robust
        # lanes = scenario.net.lanes
        # coord_from = lanes.get_coord_from_pos(edges.ids_lane[id_edge_from][0], pos_from)
        # coord_to = lanes.get_coord_from_pos(edges.ids_lane[id_edge_to][0], pos_to)

        # print '  coord_from',coord_from,type(coord_from)
        # print '  coord_to',coord_from,type(coord_to)
        # print '  delta',coord_to-coord_from
        # line of sight distance
        dist = np.sqrt(np.sum((coord_to-coord_from)**2))

        duration_approx = dist/scenario.net.modes.speeds_max[id_mode]
        # print '  dist,duration',dist,duration_approx,scenario.net.modes.speeds_max[id_mode]

        return dist, duration_approx

    def to_xml(self, id_stage, fd, indent=0):
        # scenario = self.parent.get_scenario()
        # edges = scenario.net.edges
        edges = self.ids_edge_from.get_linktab()
        edgeindex = edges.ids_sumo

        ind = self.get_ind(id_stage)

        id_edge_from = self.ids_edge_from.value[ind]
        id_edge_to = self.ids_edge_to.value[ind]
        fd.write(xm.start('walk', indent=indent))
        fd.write(xm.num('from', edgeindex[id_edge_from]))
        fd.write(xm.num('to', edgeindex[id_edge_to]))

        pos = self.positions_from.value[ind]
        length = max(edges.lengths[id_edge_from]-4.0, 0.0)

        pos = self.positions_to.value[ind]
        length = max(edges.lengths[id_edge_to]-4.0, 0.0)
        if (pos > 0) & (pos < length):
            fd.write(xm.num('arrivalPos', pos))

        elif pos < 0:
            fd.write(xm.num('arrivalPos', 0.0))

        else:
            fd.write(xm.num('arrivalPos', length))

        fd.write(xm.stopit())  # ends walk


class ActivityStages(StageTypeMixin):
    def __init__(self, ident, stages, name='Activities'):
        self.init_stagetable(ident, stages, name=name, info='Do some activity at a position of a lane.')

        self._init_attributes()

    def _init_attributes(self):

        # TODO: this structure needs review: the private vehicle is part of a person, not a stage
        # street parking at home and work could be in stage. Private garage is part of person...

        self.add(cm.AttrConf('is_until', True,
                             groupnames=['options',],
                             name='until terminator',
                             perm='rw',
                             info="If True, activity will terminate at an absolute  time, using SUMO's 'until' tag. Otherwisre the given duration will always be kept, regardless the start time of the activity.",
                             ))

        # temporary to correct a misconfiguration
        if 'parameters' in self.is_until.groupnames:
            self.is_until.del_groupname('parameters')

        activities = self.get_virtualpop().get_activities()
        self.add_col(am.IdsArrayConf('ids_activity', activities,
                                     groupnames=['parameters'],
                                     name='Activity ID',
                                     info='Scheduled activity ID. This is the activity which schould be carried out in this stage.',
                                     ))

        # this is redundant information but here for speed when writing xml
        self.add_col(am.ArrayConf('names_activitytype', '',
                                  groupnames=['parameters'],
                                  dtype=np.object_,
                                  perm='r',
                                  name='Type name',
                                  info="Name of activity type.",
                                  xmltag='actType',
                                  # xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                  ))

        # self.add_col(am.IdsArrayConf( 'ids_facility', self.get_virtualpop().get_landuse().facilities,
        #                                groupnames = ['parameters'],
        #                                name = 'ID facility',
        #                                info = 'Facility where activity takes place.',
        #                                ))

        # lane and position can be computed from facility
        self.add_col(am.IdsArrayConf('ids_lane', self.get_virtualpop().get_net().lanes,
                                     groupnames=['parameters'],
                                     name='Lane ID',
                                     info='Lane ID where activity takes place.',
                                     # xmltag = 'lane',
                                     ))
        # for update..can be removed
        self.ids_lane.set_xmltag(None)

        self.add_col(am.ArrayConf('positions', 0.0,
                                  groupnames=['parameters'],
                                  dtype=np.float32,
                                  perm='r',
                                  name='Lane pos',
                                  unit='m',
                                  info="Position on lane nearby where activity takes place.",
                                  # xmltag = 'startPos',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.positions.set_xmltag(None)

        self.add_col(am.ArrayConf('durations', 0.0,
                                  groupnames=['parameters'],
                                  dtype=np.int32,
                                  perm='r',
                                  name='Duration',
                                  unit='s',
                                  info="Duration of activity.",
                                  xmltag='duration',
                                  # xmlmap = get_inversemap(OPTIONMAP_POS_ARRIVAL),
                                  ))

        self.durations.set_xmltag('duration')

    def get_edges_positions(self, ids_stage):
        """
        Returns road edge and positions of activity.
        """
        return self.ids_lane.get_linktab().ids_edge[self.ids_lane[ids_stage]], \
            self.positions[ids_stage]

    def _init_constants(self):
        # self._activities  = self.get_virtualpop().get_activities()
        # self._activitytypes = self.get_virtualpop().get_demand().activitytypes
        # self.do_not_save_attrs(['_activities','_activitytypes'])
        pass

    # def del_row(self, id_stage):
    #    # actually this should no happen, activities should not be
    #    # copied for each plan
    #    print 'ActivityStages.del_row id_stage',id_stage,'id_activity',self.ids_activity[id_stage]
    #    self.get_virtualpop().get_activities().del_row(self.ids_activity[id_stage])
    #    am.ArrayObjman.del_row(self, id_stage)

    def to_xml(self, id_stage, fd, indent=0):
        # print 'to_xml id_stage',id_stage,self.to_xml

        fd.write(xm.start('stop', indent=indent))

        lanes = self.get_virtualpop().get_net().lanes
        id_lane = self.ids_lane[id_stage]
        # get all xml configs and damp to fd
        for attrconf in self.get_group('parameters'):

            if attrconf.attrname == 'durations':
                if self.is_until:
                    fd.write(xm.num('until', self.times_start[id_stage] + self.durations[id_stage]))
                else:
                    fd.write(xm.num('duration', self.durations[id_stage]))

            elif attrconf.attrname != 'is_until':  # is_until should actually NOT be in the parameters group!?
                # this will write only if a xmltag is defined
                attrconf.write_xml(fd, id_stage)

        fd.write(xm.num('lane', lanes.get_id_sumo(id_lane)))

        pos = self.positions[id_stage]
        length = max(lanes.get_lengths(id_lane)-4.0, 0.0)

        if (pos > 0) & (pos < length):
            fd.write(xm.num('startPos', pos))

        elif pos < 0:
            fd.write(xm.num('startPos', 0.0))

        else:
            fd.write(xm.num('startPos', length))

        # fd.write(xm.num('lane', self.cols.id_lane[ind]))
        # fd.write(xm.num('startPos', self.cols.pos_lane[ind]))
        # fd.write(xm.num('duration', self.cols.duration[ind]))
        # fd.write(xm.num('actType', self._activitytypes.names[self._activities.)

        fd.write(xm.stopit())  # ends activity


class Plans(am.ArrayObjman):
    def __init__(self, population,  **kwargs):
        """Plans database."""
        self._init_objman(ident='plans',
                          parent=population,
                          name='Plans',
                          info='Mobility plan for virtual population.',
                          # xmltag = ('plans','plan',None),
                          version=0.1,
                          **kwargs)

        self._init_attributes()
        self._init_constants()

    def _init_attributes(self):

        # upgrade
        if self.get_version() < 0.1:
            pass

        self.set_version(0.1)
        persons = self.parent

        # self.add(cm.ObjConf(StageTables('stagetables',self))   )

        self.add_stagetable('walks', WalkStages)
        self.add_stagetable('autorides', AutorideStages)
        self.add_stagetable('bikerides', BikerideStages)
        self.add_stagetable('motorides', MotorcyclerideStages)
        self.add_stagetable('transits', TransitStages)
        self.add_stagetable('taxirides', TaxirideStages)
        self.add_stagetable('SAVrides', SAVrideStages)
        self.add_stagetable('activities', ActivityStages)

        self.add_col(am.IdsArrayConf('ids_person', persons,
                                     groupnames=['links'],
                                     name='Person ID',
                                     info='Person ID to who this plan belongs to.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_strategy', persons.get_strategies(),
                                     groupnames=['links'],
                                     name='Stategy ID',
                                     info='Stategy ID with which this plan has been generated.',
                                     ))

        self.add_col(am.ArrayConf('times_begin', -np.inf,
                                  dtype=np.float32,
                                  name='Begin time',
                                  info='Time when active travelling begins. This is the time in the simulation when the person appears. The first activity is not simulated.',
                                  unit='s',
                                  ))

        self.add_col(am.ArrayConf('times_end', -np.inf,
                                  dtype=np.float32,
                                  name='End time',
                                  info='Time when active travelling ends. This is the time in the simulation when the person disappears. The last activity is not simulated.',
                                  unit='s',
                                  ))

        self.add_col(am.ArrayConf('times_est', 0.0,
                                  dtype=np.float32,
                                  name='Estim. time',
                                  info='Estimated time duration to execute travel plan. Activity times are excluded.',
                                  unit='s',
                                  ))

        self.add_col(am.ArrayConf('times_exec', 0.0,
                                  dtype=np.float32,
                                  name='Exec. time',
                                  info='Last plan execution time from simulation run.',
                                  unit='s',
                                  ))

        self.add_col(am.ArrayConf('utilities', 0.0,
                                  dtype=np.float32,
                                  name='utility',
                                  info='Utility of plan.',
                                  ))

        self.add_col(am.ArrayConf('probabilities', 1.0,
                                  dtype=np.float32,
                                  name='Probability',
                                  info='Probability that the plan is selected out of all plans available for one person.',
                                  ))

        self.add_col(am.TabIdListArrayConf('stagelists',
                                           name='Stages',
                                           info='Sequence of stages of this plan.',
                                           ))

    def _init_constants(self):
        # self._id_mode_bike = self.parent.get_scenario().net.modes.get_id_mode('bicycle')
        # self.do_not_save_attrs([])
        pass

    def clear_plans(self):
        print('Plans.clear_plans')
        for stagetable in self.get_stagetables():
            # print '  stagetable',stagetable
            stagetable.clear()

        self.clear_rows()
        # for attrconfig in  self.get_attrsman().get_colconfigs():
        #    print '  clear attrconfig',attrconfig.attrname
        #    attrconfig.clear()
        # no: self.clear()

    def add_stagetable(self, ident, StagesClass, **kwargs):
        if not hasattr(self, ident):
            self.add(cm.ObjConf(StagesClass(ident, self, **kwargs),
                                groupnames=['stagetables']))
        return getattr(self, ident).get_value()

    def get_stagetable(self, ident):
        return getattr(self, ident).get_value()

    def get_stagetables(self):
        """Return a list of with all stage objects"""
        stageobjs = []
        # print 'get_stagetables',self.get_group('stagetables')
        for stageobj in self.get_group('stagetables'):
            stageobjs.append(stageobj)
        return stageobjs

    def prepare_stagetables(self, idents_stagetable):
        # print 'prepare_stages',stagenames
        # ids = self.names.get_ids_from_indices_save(stagenames)
        # print '  ids',ids
        # print '  self.stagetables[ids]',self.stagetables[ids]
        for indent in idents_stagetable:
            self.get_stagetable(indent).prepare_planning()

    def get_stages(self, id_plan):
        stages = self.stagelists[id_plan]
        if stages is None:
            return []
        else:
            return stages

    def append_stage(self, id_plan, stage, id_stage):
        # test: stage = cm.TableEntry(stagetable, id_plan)
        # print ('Plans.append_stage',self,id_plan, stage, id_stage)

        if not isinstance(self.stagelists.get(id_plan), list):
            self.stagelists[id_plan] = []

        self.stagelists[id_plan].append((stage, id_stage))
        # old:
        # if self.stagelists[id_plan] is None:
        #     self.stagelists[id_plan]=[(stage, id_stage)]
        # else:
        #     self.stagelists[id_plan].append((stage, id_stage))
        #     print ('after append stagelists[id_plan]',type(self.stagelists[id_plan]),self.stagelists[id_plan])

    def del_plan(self, id_plan):
        """Deletes plan with id_plan.
        Attention, does not delete id_plan from the virtual population database.
        """
        for stage, id_stage in self.stagelists[id_plan]:
            stage.del_stage(id_stage)
        self.del_row(id_plan)

    # def prepare_stages(self,stagenames):
    #    self.get_stagetables().prepare_stages(stagenames)

    def get_timing_laststage(self, id_plan):
        """
        Return time_start and duration of last stage of plan id_plan
        """
        stages_current = self.stagelists[id_plan]

        if stages_current is not None:
            stage_last, id_stage_last = stages_current[-1]
            return stage_last.get_timing(id_stage_last)
        else:
            return -1, -1


class Virtualpopulation(DemandobjMixin, am.ArrayObjman):
    def __init__(self, ident, demand, **kwargs):
        self._init_objman(ident=ident,
                          parent=demand,
                          name='Virtual population',
                          info='Contains information of each individual of the virtual population.',
                          version=0.2,  # only for new scenarios
                          **kwargs)
        self._init_attributes()
        self._init_constants()

    def _init_attributes(self):

        # update here

        #
        self.set_version(0.2)

        demand = self.parent
        scenario = demand.get_scenario()

        # households tables

        self.add(cm.ObjConf(Virtualhouseholds('households', self)))

        # --------------------------------------------------------------------
        # individual vehicles tables

        self.add(cm.ObjConf(IndividualAutos('iautos', self)))
        self.add(cm.ObjConf(IndividualBikes('ibikes', self)))
        self.add(cm.ObjConf(IndividualMotorcycles('imotos', self)))

        # --------------------------------------------------------------------
        # activity  table
        # self.add(cm.ObjConf(ActivityTypes('activitytypes', self))   )
        self.add(cm.ObjConf(Activities('activities', self)))

        # --------------------------------------------------------------------
        # strategies table (must be before plans)

        self.add(cm.ObjConf(Strategies('strategies', self)))

        # --------------------------------------------------------------------
        # plans table
        self.add(cm.ObjConf(Plans(self)))

        self.get_strategies().add_default()

        # ===================================================================
        # Add person attributes

        # --------------------------------------------------------------------
        # socio economic parameters
        self.add_col(am.ArrayConf('identifications', '',
                                  dtype=np.object_,
                                  groupnames=['socioeconomic'],
                                  name='Name',
                                  info='Identification or name of person. Does not need to be unique.',
                                  ))

        self.add_col(am.IdsArrayConf('ids_household', self.get_households(),
                                     groupnames=['socioeconomic'],
                                     name='Household',
                                     info='Id of the household.',
                                     # xmltag = 'type',
                                     ))

        self.add_col(am.ArrayConf('ids_gender', default=-1,
                                  dtype=np.int32,
                                  groupnames=['socioeconomic'],
                                  choices=GENDERS,
                                  name='Gender',
                                  info='Gender of person.',
                                  ))

        self.add_col(am.ArrayConf('years_birth', default=-1,
                                  dtype=np.int32,
                                  groupnames=['socioeconomic'],
                                  name='Birth year',
                                  info='Year when person has been born.',
                                  ))

        self.add_col(am.ArrayConf('ids_occupation', default=OCCUPATIONS['unknown'],
                                  dtype=np.int32,
                                  choices=OCCUPATIONS,
                                  groupnames=['socioeconomic'],
                                  name='Occupation',
                                  info='Type of occupation.',
                                  ))

        # --------------------------------------------------------------------
        # activity parameters
        # lists with activity patterns

        self.add_col(am.IdlistsArrayConf('diaries', demand.activitytypes,
                                         groupnames=['activity'],
                                         name='Diary',
                                         info="Sequence of activity IDs to be accomplished by the person.",
                                         ))

        self.add_col(am.IdlistsArrayConf('activitypatterns', self.activities.get_value(),
                                         groupnames=['activity'],
                                         name='Activity IDs',
                                         info="Sequence of activity IDs to be accomplished by the person.",
                                         ))

        # --------------------------------------------------------------------
        # mobility parameters
        # --------------------------------------------------------------------

        # give a pedestrian vtype to each person
        vtypes = self.get_demand().vtypes
        self.add_col(am.IdsArrayConf('ids_vtype', vtypes,
                                     id_default=vtypes.select_by_mode(mode='pedestrian')[0],
                                     groupnames=['mobility'],
                                     name='Ped. type',
                                     info='The pedestrian type ID specifies the walking characteristics and visual representation of the person. In SUMO terminology, this is the vehicle type.',
                                     # xmltag = 'type',
                                     ))

        self.add_col(am.ArrayConf('traveltimebudgets', default=45*60,
                                  dtype=np.int32,
                                  groupnames=['mobility'],
                                  name='time budget',
                                  unit='s',
                                  info='Daily time budget used for traveling.',
                                  ))

        self.add_col(am.IdsArrayConf('ids_mode_preferred', scenario.net.modes,
                                     groupnames=['mobility'],
                                     name='ID preferred mode',
                                     info='ID of preferred transport mode of person.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_iauto', self.get_iautos(),
                                     groupnames=['mobility'],
                                     name='ID auto',
                                     info='ID of individual auto. Negative value means no bile available.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_ibike', self.get_ibikes(),
                                     groupnames=['mobility'],
                                     name='ID bike',
                                     info='ID of individual bicycle. Negative value means no bike available.',
                                     ))

        self.add_col(am.IdsArrayConf('ids_imoto', self.get_imotos(),
                                     groupnames=['mobility'],
                                     name='ID motorcycle',
                                     info='ID of individual motorcycle. Negative value means no motorcycle available.',
                                     ))

        self.add_col(am.ArrayConf('dists_walk_max', default=400.0,
                                  dtype=np.float32,
                                  groupnames=['mobility'],
                                  name='Max. walk dist',
                                  info='Maximum acceptable walking distance between origin and destination or for transfers between modes.',
                                  ))

        # --------------------------------------------------------------------
        # plans
        self.add_col(am.IdsArrayConf('ids_plan', self.get_plans(),
                                     groupnames=['plans'],
                                     name='ID Plan',
                                     info='Currently selected mobility plan ID of person. This is the plan which will be simulated.',
                                     ))

        self.add_col(am.IdlistsArrayConf('lists_ids_plan', self.get_plans(),
                                         groupnames=['plans'],
                                         name='Plan IDs',
                                         info='List with alternative, feasible mobility plan IDs for each person.',
                                         ))

    def _init_constants(self):
        modes = self.get_scenario().net.modes
        self.id_mode_bike = modes.get_id_mode('bicycle')
        self.id_mode_auto = modes.get_id_mode('passenger')
        self.id_mode_moto = modes.get_id_mode('motorcycle')
        self._edges = self.get_net().edges
        self.do_not_save_attrs(['id_mode_bike', 'id_mode_auto', 'id_mode_moto',
                                '_edges'])

    def get_demand(self):
        return self.parent

    def clear_population(self):
        # self.clear()
        self.clear_plans()
        self.clear_ivehicles()
        self.get_activities().clear_rows()
        # TODO: this should disappear
        self.get_landuse().parking.clear_booking()

        # for attrconfig in  self.get_attrsman().get_colconfigs():
        #    attrconfig.clear()
        self.clear_rows()

    def del_persons(self, ids_pers_delete):
        print('delete', len(ids_pers_delete), 'persons')
        ids_plans_all = set()
        ids_activity_del = set()
        lists_ids_plan = self.lists_ids_plan[ids_pers_delete]
        for id_pers, ids_plan in zip(ids_pers_delete, self.lists_ids_plan[ids_pers_delete]):
            ids_plans_all.update(ids_plan)
            for stageinfo in self.get_plans().stagelists[ids_plan]:
                for stages, id_stage in stageinfo:
                    # print ' id_pers ',id_pers,'del',stages,'id_stage',id_stage,stages.__class__,id_stage,stages.__class__== ActivityStages

                    # if stages.__class__ == ActivityStages:
                    #    print '    ids_activity',stages.ids_activity[id_stage]
                    #    ids_activity_del.add(stages.ids_activity[id_stage])

                    stages.del_row(id_stage)
                    # print '    check',id_stage in stages

        ids_plans_delete_all = list(ids_plans_all)
        # print 'len(plans)', len(plans),'delete ',len(ids_plans_delete_all)
        self.get_plans().del_rows(ids_plans_delete_all)
        # print ' ',len(self.get_activities()),'activities'
        # ids_activity_del = []
        for ids_activity in self.activitypatterns[ids_pers_delete]:
            ids_activity_del.update(ids_activity)

        ids_activity_del = list(ids_activity_del)
        # print '  del',len(ids_activity_del),'of',len(self.get_activities()),'activities'
        self.get_activities().del_rows(ids_activity_del)

        self.get_iautos().del_rows(self.ids_iauto[ids_pers_delete])
        self.get_ibikes().del_rows(self.ids_ibike[ids_pers_delete])
        self.get_imotos().del_rows(self.ids_imoto[ids_pers_delete])

        # print 'len(virtualpop)', len(virtualpop),'delete ',len(ids_pers_delete)
        self.del_rows(ids_pers_delete)

    def clear_plans(self):
        # print 'clear_plans',self.get_stagetables()
        self.ids_plan.reset()
        self.lists_ids_plan.reset()
        self.get_plans().clear_plans()

        # TODO: this should disappear
        self.get_landuse().parking.clear_booking()

    def clear_ivehicles(self):
        """
        Clear all individually owned vehicles.
        """
        print('clear_ivehicles')
        self.get_iautos().clear_vehicles()
        self.get_ibikes().clear_vehicles()
        self.get_imotos().clear_vehicles()

    def clear_households(self):
        print('clear households')
        self.get_households().clear_rows()

    def clear_activities(self):
        print('Virtualpop.clear_activities')
        self.get_activities().clear_rows()
        self.diaries.clear()
        self.activitypatterns.clear()

    def get_activities(self):
        return self.activities.get_value()

    def get_strategies(self):
        return self.strategies.get_value()

    def get_households(self):
        return self.households.get_value()

    def get_plans(self):
        return self.plans.get_value()

    def get_iautos(self):
        return self.iautos.get_value()

    def get_ibikes(self):
        return self.ibikes.get_value()

    def get_imotos(self):
        return self.imotos.get_value()

    def get_stagetables(self):
        return self.get_plans().get_stagetables()

    def get_landuse(self):
        return self.parent.get_scenario().landuse

    def get_scenario(self):
        return self.parent.get_scenario()

    def get_net(self):
        return self.parent.get_scenario().net

    def get_ptlines(self):
        return self.get_demand().ptlines

    def get_ptstops(self):
        return self.get_net().ptstops

    def get_id_sumo_from_id(self, id_sumo):
        return 'vp.%s' % id_sumo

    def get_id_from_id_sumo(self, id_veh_sumo):
        if len(id_veh_sumo.split('.')) == 2:
            prefix, id_pers = id_veh_sumo.split('.')
            if prefix == 'vp':
                return int(id_pers)
            else:
                return -1
        return -1

    def get_ids_from_ids_sumo(self, ids_sumo):
        ids = np.zeros(len(ids_sumo), dtype=int32)
        for id_sumo in ids_sumo:
            ids[i] = self.get_id_from_id_sumo(id_sumo)
        return ids

    def get_time_depart_first(self):
        # print 'Virtualpop.get_time_depart_first'
        if len(self.get_plans()) > 0:
            plans = self.get_plans()
            ids = self.select_ids(self.ids_plan.get_value() >= 0)
            # print '  ids',ids
            return float(np.min(plans.times_begin[self.ids_plan[ids]]))
        else:
            return 0.0

    def get_time_depart_last(self):
        if len(self.get_plans()) > 0:
            # todo: this can be improved by adding plan execution time
            plans = self.get_plans()
            ids = self.select_ids(self.ids_plan.get_value() >= 0)

            return float(np.max(plans.times_end[self.ids_plan[ids]]))
        else:
            return 0.0

    # def add_stagetable(self,ident,StageClass, **kwargs):
    #    print 'add_stagetable',ident,StageClass#,kwargs
    #    if not hasattr(self,ident):
    #        #print '  StageClass',StageClass(ident, self, **kwargs)
    #        #print '  ObjConf',cm.ObjConf(StageClass(ident, self, **kwargs), goupnames = ['stages'])
    #        self.add(cm.ObjConf(StageClass(ident, self, **kwargs), goupnames = ['stages']) )
    #    return getattr(self, ident).get_value()

    # def get_stagetable(self, ident):
    #    return getattr(self, ident).get_value()

    # def get_stagetables(self):
    #    """Return a list of with all stage objects"""
    #    stageobjs = []
    #    #print 'get_stagetables',self.get_group('stages')
    #    for stageobj in self.get_group('stages'):
    #        stageobjs.append(stageobj)
    #    return stageobjs

    def make_multiple(self, n, **kwargs):
        return self.add_rows(n=n, **kwargs)

    def disaggregate_odflow(self,   time_start, time_end, id_mode,
                            ids_fac,
                            probs_fac_orig, probs_fac_dest,
                            tripnumber_tot,
                            id_activitytype_orig,
                            id_activitytype_dest,
                            hour_begin_earliest_orig,
                            hour_begin_earliest_dest,
                            hour_begin_latest_orig,
                            hour_begin_latest_dest,
                            duration_min_orig,
                            duration_min_dest,
                            duration_max_orig,
                            duration_max_dest,
                            scale=1.0,
                            hour_offset=8.0,  # 08:00
                            hour_tripbudget=25.0/60,  # 25min
                            dist_walk_max=400,  # scalar!
                            **kwargs
                            ):
        """
        Disaggregation of demand dem  from taz id_zone_orig to id_zone_dest with id_mode
        during time interval  time_start,time_end, and creation of persons
        which are parameterized accordingly.
        The facility type at origin will be  landusetype_orig
        and at destination landusetype_dest.


        """
        tripnumber = int(scale*tripnumber_tot)
        print('disaggregate_odflow', time_start, time_end, id_mode, tripnumber)

        print('  id_activitytype_orig,id_activitytype_dest', id_activitytype_orig, id_activitytype_dest)

        # print '  probs_orig',sum(probs_fac_orig)#,'\n',probs_fac_orig
        # print '  probs_dest',sum(probs_fac_dest)#,'\n',probs_fac_dest

        # is there a chance to find facilities to locate persons in
        # origin and destination zone

        # activitytypes= self.get_scenario().demand.activitytypes
        # ctivitynames = self.activitytypes.names
        # get_id_act = activitynames.get_id_from_index

        if (np.sum(probs_fac_orig) > 0) & (np.sum(probs_fac_dest) > 0):
            # if id_mode == self._id_mode_bike:
            #    are_bikeowner =  np.ones(tripnumber, dtype=np.bool_)
            # else:
            #    # TODO: assign a default percentage of bike owners
            #    are_bikeowner =  np.zeros(tripnumber, dtype=np.bool_)
            # times_start = np.random.randint(time_start,time_end,tripnumber)
            ids_person = self.make_multiple(tripnumber,
                                            ids_mode_preferred=id_mode * np.ones(tripnumber, dtype=np.int32),
                                            dists_walk_max=dist_walk_max * np.ones(tripnumber, dtype=np.int32),
                                            # times_start = times_start,
                                            )

            unitvec_int = np.ones(tripnumber, dtype=np.int32)
            unitvec_float = np.ones(tripnumber, dtype=np.int32)

            # activity timing
            # hours_start = hour_offset + np.array(times_start,dtype = np.float32)/3600
            # tol_orig = hour_begin_latest_orig+duration_max_orig-(hour_begin_earliest_orig+duration_min_orig)

            # print '  hours_start[:3]',hours_start[:3]
            # print '  tol_orig',tol_orig

            # hours_end_est = hours_start + hour_tripbudget
            # tol_dest = hour_begin_latest_dest-hour_begin_earliest_dest

            # print '  hours_end_est[:3]',hours_end_est[:3]
            # print '  tol_dest',tol_dest

            # self.map_id_act_to_ids_facattrconf[id_activitytype_orig][ids_person] = ids_fac[random_choice(tripnumber, probs_fac_orig)]
            # self.map_id_act_to_ids_facattrconf[id_activitytype_dest][ids_person] = ids_fac[random_choice(tripnumber, probs_fac_dest)]
            activities = self.get_activities()

            # fix first departure hours for first activity imposed by
            # OD flow data
            hours_end_earliest_orig = (hour_offset+float(time_start)/3600)*unitvec_float
            hours_end_latest_orig = (hour_offset+float(time_end)/3600)*unitvec_float

            duration_mean_orig = 0.5 * duration_min_orig+duration_max_orig
            hours_begin_earliest_orig = hours_end_earliest_orig-duration_mean_orig
            hours_begin_latest_orig = hours_end_latest_orig-duration_mean_orig

            # this estimate could be *optionally* replaced by preliminary routing
            hours_begin_earliest_dest = hours_end_earliest_orig+hour_tripbudget
            hours_begin_latest_dest = hours_end_latest_orig+hour_tripbudget

            # hours_end_earliest_dest = hours_begin_earliest_dest+duration_min_dest
            # hours_end_latest_dest = hours_begin_latest_dest+duration_max_dest

            ids_activity_orig = activities.add_rows(
                n=tripnumber,
                ids_activitytype=id_activitytype_orig * unitvec_int,
                ids_facility=ids_fac[random_choice(tripnumber, probs_fac_orig)],
                hours_begin_earliest=hours_begin_earliest_orig,
                hours_begin_latest=hours_begin_latest_orig,
                durations_min=duration_mean_orig-1.0/60.0 * unitvec_float,  # min mast be less than max to prevent crash
                durations_max=duration_mean_orig * unitvec_float,
            )

            ids_activity_dest = activities.add_rows(
                n=tripnumber,
                ids_activitytype=id_activitytype_dest * unitvec_int,
                ids_facility=ids_fac[random_choice(tripnumber, probs_fac_dest)],
                hours_begin_earliest=hours_begin_earliest_dest,
                hours_begin_latest=hours_begin_latest_dest,
                durations_min=duration_min_dest*unitvec_float,
                durations_max=duration_max_dest*unitvec_float,
            )

            for id_person, id_activity_orig, id_activity_dest in zip(ids_person, ids_activity_orig, ids_activity_dest):
                self.activitypatterns[id_person] = [id_activity_orig, id_activity_dest]

            # activitypatterns = np.zeros((tripnumber,2), dtype = np.int32)
            # activitypatterns[:,0]= ids_activity_orig
            # activitypatterns[:,1]= ids_activity_dest
            # try convert in this way to lists
            # print '  activitypatterns',activitypatterns.tolist()
            # self.activitypatterns[ids_person] = activitypatterns.tolist()
            # for id_person, act_pattern in zip(ids_person,activitypatterns.tolist()):
            #    self.activitypatterns[id_person] =  act_pattern

            return ids_person
        else:
            print('WARNING in disaggregate_odflow: no probabilities', np.sum(probs_fac_orig), np.sum(probs_fac_dest))

            return []

    def create_pop_from_odflows(self, is_use_landusetypes=False, **kwargs):
        """
        Creates a population and defines home and activity facility
        according to OD matrix defined in odflows.
        The population is distributed within the zones according to 
        the area of the facility.
        if  landusetype_orig and landusetype_dest also landuse types
        of facilities of origin and destination are taken into account.
        """
        print('create_pop_from_odflows')

        demand = self.parent
        odflowtab = demand.odintervals.generate_odflows()
        landuse = self.get_landuse()
        activitytypes = demand.activitytypes
        log = kwargs.get('logger', self.get_logger())

        # self._make_map_id_act_to_ids_facattrconf()
        ids_flow = odflowtab.get_ids()
        n_flows = len(ids_flow)

        ids_activitytype_orig = odflowtab.ids_activitytype_orig[ids_flow]
        ids_activitytype_dest = odflowtab.ids_activitytype_dest[ids_flow]

        if is_use_landusetypes:

            # TODO: not tested and works only for one landusetype per activity
            # activitytypes = self.activitytypes.get_value()
            # id_landusetype_orig = activitytypes.ids_landusetypes[kwargs['id_activitytype_orig']][0]
            # id_landusetype_dest = activitytypes.ids_landusetypes[kwargs['id_activitytype_dest']][0]

            # TODO: get activitypes from activity
            # probs_fac, ids_fac = self.get_landuse().facilities.get_departure_probabilities_landuse()
            probs_activitytype_fac_area = {}
            ids_acttype = activitytypes.get_ids()
            for id_acttype, ids_landusetype in zip(ids_acttype, activitytypes.ids_landusetypes[ids_acttype]):
                # print 50*'-'
                # print '  id_acttype',id_acttype,'ids_landusetype',ids_landusetype
                probs_activitytype, ids_fac = landuse.facilities.get_departure_probabilities_landuse2(ids_landusetype)
                probs_activitytype_fac_area[id_acttype] = probs_activitytype
                # print '    probs_activitytype_fac_area',probs_activitytype_fac_area[id_acttype]
        else:
            probs_fac_area, ids_fac = landuse.facilities.get_departure_probabilities()

        i = 0.0
        for id_flow, \
            id_orig, \
            id_dest, \
            id_mode, \
            time_start, \
            time_end, \
            tripnumber, \
            id_activitytype_orig, \
            id_activitytype_dest, \
            hour_begin_earliest_orig, \
            hour_begin_earliest_dest, \
            hour_begin_latest_orig, \
            hour_begin_latest_dest, \
            duration_min_orig, \
            duration_min_dest, \
            duration_max_orig, \
            duration_max_dest\
            in zip(ids_flow,
                   odflowtab.ids_orig[ids_flow],
                   odflowtab.ids_dest[ids_flow],
                   odflowtab.ids_mode[ids_flow],
                   odflowtab.times_start[ids_flow],
                   odflowtab.times_end[ids_flow],
                   odflowtab.tripnumbers[ids_flow],
                   ids_activitytype_orig,
                   ids_activitytype_dest,
                   activitytypes.hours_begin_earliest[ids_activitytype_orig],
                   activitytypes.hours_begin_earliest[ids_activitytype_dest],
                   activitytypes.hours_begin_latest[ids_activitytype_orig],
                   activitytypes.hours_begin_latest[ids_activitytype_dest],
                   activitytypes.durations_min[ids_activitytype_orig],
                   activitytypes.durations_min[ids_activitytype_dest],
                   activitytypes.durations_max[ids_activitytype_orig],
                   activitytypes.durations_max[ids_activitytype_dest],
                   ):

            log.progress(i/n_flows*100)
            # print '    disagg between zones',id_orig,id_dest
            i += 1
            if is_use_landusetypes:
                # TODO: not tested and works only for one landusetype per activity
                # but in activity typrs several landuse types are defined

                # idea: add the probabilities for landuse types of origin and dest
                # probs_fac_orig = probs_fac[id_orig][id_landusetype_orig]
                # probs_fac_dest = probs_fac[id_dest][id_landusetype_dest]
                probs_fac_orig = probs_activitytype_fac_area[id_activitytype_orig][id_orig]
                probs_fac_dest = probs_activitytype_fac_area[id_activitytype_dest][id_dest]

            else:
                probs_fac_orig = probs_fac_area[id_orig]
                probs_fac_dest = probs_fac_area[id_dest]

            self.disaggregate_odflow(time_start,
                                     time_end,
                                     id_mode,
                                     ids_fac,
                                     probs_fac_orig,
                                     probs_fac_dest,
                                     tripnumber,
                                     id_activitytype_orig,
                                     id_activitytype_dest,
                                     hour_begin_earliest_orig,
                                     hour_begin_earliest_dest,
                                     hour_begin_latest_orig,
                                     hour_begin_latest_dest,
                                     duration_min_orig,
                                     duration_min_dest,
                                     duration_max_orig,
                                     duration_max_dest,
                                     **kwargs
                                     )

        # return odflowtab

    def scale_pop(self, scale, hours_delta):
        """Scale current population by scale factor greater than one.
        Currently this is not creating plans for the new persons.
        """
        activities = self.get_activities()
        ids_pers = self.get_ids()
        n_pers = len(self)

        n_pers_required = int(n_pers*scale) - n_pers

        ids_source = ids_pers[np.random.randint(n_pers, size=n_pers_required)]

        ids_person_new = self.make_multiple(n_pers_required,
                                            identifications=self.identifications[ids_source],
                                            ids_mode_preferred=self.ids_mode_preferred[ids_source],
                                            dists_walk_max=self.dists_walk_max[ids_source],
                                            )

        for id_person, activitypattern, hour_begin_delta, hour_duration_delta in zip(
                ids_person_new,
                self.activitypatterns[ids_source],
                np.random.uniform(low=0.0, high=hours_delta, size=n_pers_required),
                np.random.uniform(low=0.0, high=hours_delta, size=n_pers_required)):

            if activitypattern is not None:
                n_act = len(activitypattern)
                if n_act > 0:
                    hours_begin_earliest = activities.hours_begin_earliest[activitypattern] - hour_begin_delta
                    # we need to clip to a slightly positive value as SUMO is alergic to negative times
                    hours_begin_earliest.clip(0.1, np.inf)
                    hours_begin_latest = activities.hours_begin_latest[activitypattern] + hour_begin_delta
                    hours_begin_latest.clip(0.1, np.inf)
                    durations_min = activities.durations_min[activitypattern] - hour_duration_delta
                    durations_min.clip(0.1, np.inf)
                    durations_max = activities.durations_max[activitypattern] + hour_duration_delta
                    durations_max.clip(0.1, np.inf)
                    ids_activity = activities.add_rows(
                        n=n_act,
                        ids_activitytype=activities.ids_activitytype[activitypattern],
                        ids_facility=activities.ids_facility[activitypattern],
                        hours_begin_earliest=hours_begin_earliest,
                        hours_begin_latest=hours_begin_latest,
                        durations_min=durations_min,
                        durations_max=durations_max,
                    )
                    self.activitypatterns[id_person] = list(ids_activity)

    def add_plans(self, ids_person, id_strategy=-1):
        """Adds plans IDs with strategy id_strategy to all persons in list ids_person.
        Attention, this sets the current plan to this strategy.
        Returns a list of plan IDs ids_plan corrisponding to each person in ids_person.
        """
        print('add_plans n, id_strategy', len(ids_person), id_strategy)
        n_plans = len(ids_person)
        plans = self.get_plans()

        # self._is_plan_no_preferred
        # self._is_replace_plan
        # ids_plans = np.zeros(n_plans, dtype = np.int32)
        # inds_new = np.ones(n_plans, dtype = np.bool_)
        plans_remove = []
        #
        for i, id_person, ids_plan, id_mode_preferred in zip(range(n_plans), ids_person, self.lists_ids_plan[ids_person], self.ids_mode_preferred[ids_person]):
            if ids_plan is not None:
                if self._is_replace_plan:
                    ids_strategy = list(plans.ids_strategy[ids_plan])
                    if id_strategy in ids_strategy:
                        # if existing plans should  be replaced
                        # and a plan with this starategy exists then
                        # remove this existing plan
                        plans_remove.append((id_person, ids_plan[ids_strategy.index(id_strategy)]))

        # delete all previous plans with this strategy
        for id_person, id_plan in plans_remove:
            self.del_plan(id_person, id_plan)

        # print '  get_plans',self.get_plans()
        # print '  stagetables',self.get_plans().get_stagetables().get_ident_abs()
        # print '  stagetables',self.get_plans().get_stagetables().stagetables.get_value()
        ids_plan = plans.add_rows(n=n_plans,
                                  ids_person=ids_person,
                                  ids_strategy=id_strategy*np.ones(n_plans, dtype=np.int32),
                                  )

        # print '  post stagetables',self.get_plans().get_stagetables().get_ident_abs()
        # print '  post stagetables',self.get_plans().get_stagetables().stagetables.get_value()

        # set current plans
        self.ids_plan[ids_person] = 1*ids_plan

        # add to plan choices
        for id_person, id_plan in zip(ids_person, ids_plan):
            if self.lists_ids_plan[id_person] is None:
                self.lists_ids_plan[id_person] = [id_plan]
            else:
                self.lists_ids_plan[id_person].append(id_plan)
        return ids_plan

    def del_plan(self, id_person, id_plan):
        """Removes id_plan from person id_person, and completely deletes the plan including all data."""
        # remove current plan
        if self.ids_plan[id_person] == id_plan:
            self.ids_plan[id_person] = -1

        # remove from plan alternatives
        if id_plan in self.lists_ids_plan[id_person]:
            self.lists_ids_plan[id_person].remove(id_plan)

        # remove from plan database , deletes also all stages
        self.get_plans().del_plan(id_plan)

    def plan_with_strategy(self, id_strategy, evalcrit=0, ids_person=None, is_plan_no_preferred=False, is_replace_plan=False,  logger=None):
        strategy = self.get_strategies().strategies[id_strategy]
        plans = self.get_plans()
        # print '!!!!!!ids_person',ids_person
        if ids_person is None:
            ids_person = self.get_ids()

        else:
            ids_person = np.array(ids_person, dtype=np.int32)

        n_plans = len(ids_person)
        print('plan_with_strategy id_strategy', id_strategy, '(%d/%d) plans' %
              (n_plans, len(self)), 'evalcrit', evalcrit, 'is_replace_plan', is_replace_plan)

        evals = strategy.preevaluate(ids_person)
        # The values of the preevaluation vector are as follows:
        #        -1 : Strategy cannot be applied
        #        0  : Stategy can be applied, but the preferred mode is not used
        #        1  : Stategy can be applied, and preferred mode is part of the strategy
        #        2  : Strategy uses predomunantly preferred mode

        if is_plan_no_preferred:  # considering only people with vehicle but with another preferred mode
            inds_preeval = (evals == 0) | (evals == 1)

        # elif is_plan_preferred:# only plans for preferred mode
        #    inds_preeval = (evals == 0) | (evals == 1)

        else:
            inds_preeval = evals >= evalcrit

        # if  is_substitute:
        #    for i, id_strat_pers in zip(xrange(len(inds_preeval)), self.plans.ids_strategy[ids_person[inds_preeval]]):
        #        if id_strat_pers
        if not is_replace_plan:
            # means existing plans will not be touched
            for i, id_person, ids_plan, id_mode_preferred in zip(range(n_plans), ids_person, self.lists_ids_plan[ids_person], self.ids_mode_preferred[ids_person]):
                if ids_plan is not None:

                    ids_strategy = plans.ids_strategy[ids_plan]
                    if id_strategy in ids_strategy:
                        # if existing plans should not be replaced
                        # and a plan with this starategy exists then
                        # remove this person from replanning with this strategy
                        inds_preeval[i] = 0
                        # id_plan_exist = ids_plan[ids_strategy.index(id_strategy)]

        #        if self._is_replace_plan:

        # print '  type(ids_person)',type(ids_person),type(inds_preeval),'inds_preeval',inds_preeval

        ids_person_preeval = ids_person[inds_preeval]
        print('   eligible number of plans', len(ids_person_preeval))

        # TODO: is_replace_plan = is_replace_plan could be an argument to plan()
        # and then passed to the centralized add_plan() method of the VP.
        # inds_preeval = np.ones(len(ids_person_preeval), dtype = np.bool_)

        self._is_plan_no_preferred = is_plan_no_preferred
        self._is_replace_plan = is_replace_plan

        strategy.plan(ids_person_preeval, logger=logger)

    # def get_times(self, ind, ids_person = None, pdf = 'unit'):
    #    """
    #    Returns person IDs, activity IDs and initial times
    #    for persons with at least one acivity.
    #
    #    ids_person: array of preselected person IDs
    #
    #    pdf: gives the probability density function to be chosen to determin
    #    the departure times within the initial time intervals given by
    #    initial activity attributes.
    #    """
    #    ids_person, ids_activity = self.get_activity_from_pattern(0,ids_person)
    #    times_init = self.get_activities().get_times_init(ids_activity, pdf)
    #
    #    return ids_person, ids_activity, times_init

    def get_activities_from_pattern(self, ind, ids_person=None):
        """
        Returns person IDs and from/to activity IDs for persons who perform an activity
        at the given activity index ind.
        Returns arrays: ids_person, ids_activity_from, ids_activity_to

        ind: index of activity in activity pattern, starting with 0
        ids_person: array of preselected person IDs


        """
        if len(self.activitypatterns.get_linktab()) == 0:
            return [], [], []
        ids_person_activity = []
        ids_activity_from = []
        ids_activity_to = []
        if ids_person is None:
            ids_person = self.get_ids()

        for id_person, activitypattern in zip(ids_person, self.activitypatterns[ids_person]):
            # has person activity at index ind?
            if len(activitypattern) > ind+1:
                ids_person_activity.append(id_person)
                ids_activity_from.append(activitypattern[ind])
                ids_activity_to.append(activitypattern[ind+1])

        return ids_person_activity, ids_activity_from, ids_activity_to

        #        activities.hours_begin_earliest[ids_person_activity],
        #        activities.hours_begin_latest[ids_person_activity],
        #        activities.durations_min[ids_person_activity],
        #        activities.durations_max[ids_person_activity],

    def get_vtypes(self):

        ids_vtypes = set()
        # get individual vehicle types
        ids_vtypes.update(self.get_iautos().ids_vtype.get_value())
        ids_vtypes.update(self.get_imotos().ids_vtype.get_value())
        ids_vtypes.update(self.get_ibikes().ids_vtype.get_value())

        # add public transport
        ids_vtypes.update(self.get_ptlines().ids_vtype.get_value())

        # add pedestrian types
        ids_vtypes.update(self.ids_vtype.get_value())

        return ids_vtypes

    def select_plans_preferred_mode(self, fraction=0.1, timedev=-1.0, c_probit=-1.0, **kwargs):
        """
        Selects current plant to satisfy best the preferred mode.
        """

        strategies = self.get_strategies()
        ids_strat = strategies.get_ids()
        n_strat = len(ids_strat)
        ids_pers_all = self.get_ids()
        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        n_pers = len(ids_pers)
        preevals = -1*np.ones((np.max(ids_pers)+1, np.max(ids_strat)+1), dtype=np.int32)
        for ind, id_strategy, strategy in zip(list(range(n_strat)), ids_strat, strategies.strategies[ids_strat]):
            preevals[ids_pers, id_strategy] = strategy.preevaluate(ids_pers)

        preferred = 2
        plans = self.get_plans()
        self.ids_plan.reset()
        for id_pers, ids_plan in zip(ids_pers, self.lists_ids_plan[ids_pers]):
            if ids_plan is not None:
                if len(ids_plan) > 0:
                    # print '  id_pers,ids_plan',id_pers,ids_plan
                    # print '  ids_strat, preeval',plans.ids_strategy[ids_plan],preevals[id_pers,plans.ids_strategy[ids_plan]]
                    inds_sel = np.flatnonzero(preevals[id_pers, plans.ids_strategy[ids_plan]] == preferred)
                    # print '  inds_sel',inds_sel,inds_sel.dtype
                    if len(inds_sel) > 0:
                        # ids_plan_sel = np.array(ids_plan)[inds_sel]
                        # print '    ids_plan_sel',ids_plan_sel
                        # at least one plan contains preferred mode
                        self.ids_plan[id_pers] = np.array(ids_plan)[inds_sel][0]  # why [1]?
                    else:
                        times_est = self.get_plans().times_est
                        # print '  no plan contains preferred mode, take shortest estimated time instead'
                        # print '    id_pers,ids_plan',id_pers,ids_plan,'times_est[ids_plan]',times_est[ids_plan]

                        ids_plan_arr = np.array(ids_plan, dtype=np.int32)[times_est[ids_plan] >= 0.0]

                        if timedev > 0.1:
                            times_rand = np.random.normal(0.0, timedev, len(ids_plan_arr))
                        elif c_probit > 0:
                            times_rand = np.zeros(len(ids_plan_arr), dtype=np.float32)
                            for i, t in zip(range(len(ids_plan_arr)), times_est[ids_plan_arr]):
                                times_rand[i] = np.random.normal(0.0, c_probit * t, 1)

                        else:
                            times_rand = np.zeros(len(ids_plan_arr), dtype=np.float32)
                        self.ids_plan[id_pers] = ids_plan_arr[np.argmin(times_est[ids_plan_arr]+times_rand)]
                    #    assumption: a plan for the preferred mode always exists
                    #    # no preferred mode found try to satisfy best possible
                    #    #ids_plan[preevals[id_pers,plans.ids_strategy[ids_plan]] == preferred]
                    #    self.ids_plan[id_pers] = -1
        return True

    def select_plans_min_time_est(self, fraction=1.0, timedev=-1.0, c_probit=-1.0, **kwargs):
        """
        Select plan with minimum estimated travel time as current plant.
        """
        ids_pers_all = self.get_ids()
        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        times_est = self.get_plans().times_est
        # self.ids_plan.reset()
        for id_pers, ids_plan_all in zip(ids_pers, self.lists_ids_plan[ids_pers]):
            ids_plan = np.array(ids_plan_all, dtype=np.int32)[times_est[ids_plan_all] > 0.1]
            if len(ids_plan) > 0:
                # print '  id_pers,ids_plan',id_pers,ids_plan
                if timedev > 0.1:
                    times_rand = np.random.normal(0.0, timedev, len(ids_plan))
                elif c_probit > 0:
                    times_rand = np.zeros(len(ids_plan), dtype=np.float32)
                    for i, t in zip(range(len(ids_plan)), times_est[ids_plan]):
                        times_rand[i] = np.random.normal(0.0, c_probit * t, 1)

                else:
                    times_rand = np.zeros(len(ids_plan), dtype=np.float32)
                self.ids_plan[id_pers] = np.array(ids_plan)[np.argmin(times_est[ids_plan]+times_rand)]
        return True

    def select_plans_random(self, fraction=0.1,  **kwargs):
        """
        A fraction of the population changes a plan. 
        The new plans are chosen randomly. 
        """

        ids_pers_all = self.get_ids()
        print('select_plans_random', len(ids_pers_all), fraction)
        times_est = self.get_plans().times_est
        # self.ids_plan.reset()
        # ids_mode[random_choice(n,shares/np.sum(shares))]

        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        print('  ids_pers', ids_pers)
        for id_pers, ids_plan in zip(ids_pers, self.lists_ids_plan[ids_pers]):
            if len(ids_plan) > 0:
                # print '  id_pers,ids_plan',id_pers,ids_plan
                self.ids_plan[id_pers] = ids_plan[np.random.randint(len(ids_plan))]
        return True

    def select_plans_min_time_exec(self, fraction=0.1, timedev=-1, c_probit=-1, **kwargs):
        """
        Select plan with minimum executed travel time as current plant.
        """
        ids_pers_all = self.get_ids()
        # print 'select_plans_random',len(ids_pers_all),fraction
        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        times_exec = self.get_plans().times_exec
        # self.ids_plan.reset()
        for id_pers, ids_plan_all in zip(ids_pers, self.lists_ids_plan[ids_pers]):
            # print '    ids_plan_all',ids_plan_all,type(ids_plan_all)

            # select_plans_min_time_exec
            # For analyzing the total travelled distance of each vehicle, with or without humans
            # e.g. the total distance travelled by SAVs (shared autonomous vehicles) with and out passengers),
            # it is necessary to include UNIFINISHED TRIP DATA in the output trip data.
            # Unfinished trips/travel plans for the virtual population result in negative travel times.
            # A negative travel times means that this plan selected/started to be executed in the previous simulation,
            # but was not completed by the end of the simlation.
            # An executed travel time of 0 means that this plan has never been executed for this person in any simulation so far.

            # The plans with negative executed times are less preferable than plans with an executed time of 0,
            # since the negative-time-plan was definately not finished, while the 0-time-plan has not been tested yet.
            ids_plan_times_untested = np.array(ids_plan_all, dtype=np.int32)[times_exec[ids_plan_all] == 0]
            ids_plan_times_finished = np.array(ids_plan_all, dtype=np.int32)[times_exec[ids_plan_all] > 0]
            ids_plan_times_unfinished = np.array(ids_plan_all, dtype=np.int32)[times_exec[ids_plan_all] < 0]
            # time_exec == 0 means: the travel plan has not been executed yet.
            # Such un-tested plans with time_exec == 0 are be prioritized
            # by plan selector over executed trips with time > 0, since it their exec. time is unknown.
            if len(ids_plan_times_untested) > 0:
                ids_plan = ids_plan_times_untested
                self.ids_plan[id_pers] = ids_plan[np.random.randint(len(ids_plan))]
            elif len(ids_plan_times_finished) > 0:
                ids_plan = ids_plan_times_finished
                if timedev > 0.1:
                    times_rand = np.random.normal(0.0, timedev, len(ids_plan))
                elif c_probit > 0:
                    times_rand = np.zeros(len(ids_plan), dtype=np.float32)
                    for i, t in zip(range(len(ids_plan)), times_exec[ids_plan]):
                        times_rand[i] = np.random.normal(0.0, c_probit * t, 1)
                else:
                    times_rand = np.zeros(len(ids_plan), dtype=np.float32)
                self.ids_plan[id_pers] = np.array(ids_plan)[np.argmin(times_exec[ids_plan]+times_rand)]
            elif len(ids_plan_times_unfinished) > 0:
                # If this else case has been reached, all plans for this virtual person have been executed,
                # but no plan allowed the person to finish their trip (at least one travel stage unfinished).
                # Since the negative exec. time value of unfinished trips has no apparant connection to
                # how close the person was to finishing all stages,
                # the negative executed time number is considered meaningless.
                # Therefore, a random plan is selected.
                ids_plan = ids_plan_all
                if len(ids_plan) > 0:
                    self.ids_plan[id_pers] = ids_plan[np.random.randint(len(ids_plan))]
            else:
                pass  # no plans exist for this person
            # print '  id_pers,ids_plan',id_pers,ids_plan
        return True

    def select_plans_min_time_exec_est(self, fraction=0.1, timedev=-1, c_probit=-1, **kwargs):
        """
        Select plan with minimum executed or estimated (if executed doesn't exist) travel time as current plant.
        """
        n_analyzed_persons = 0
        ids_pers_all = self.get_ids()
        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        times_exec = self.get_plans().times_exec
        times_est = self.get_plans().times_est
        ids_plans = self.get_plans().get_ids()
        for id_pers, ids_plan_all in zip(ids_pers, self.lists_ids_plan[ids_pers]):
            ids_plan_est = np.array(ids_plan_all, dtype=np.int32)[times_est[ids_plan_all] > 0.1]
            ids_plan_exec = np.array(ids_plan_all, dtype=np.int32)[times_exec[ids_plan_all] > 0.1]
            if len(ids_plan_est) > 0:
                if len(ids_plan_est) == len(ids_plan_exec):
                    ids_plan_est = []
                else:
                    c = np.zeros(len(ids_plan_est))
                    for x in range(len(ids_plan_est)):
                        for y in range(len(ids_plan_exec)):
                            if ids_plan_est[x] == ids_plan_exec[y]:
                                c[x] = 1
                    d = np.delete(ids_plan_est, np.flatnonzero(c))
                    ids_plan_est = d

                n_analyzed_persons += 1

                if timedev > 10.0**-8:
                    if len(ids_plan_est) > 0:
                        times_rand_est = np.random.normal(0.0, timedev, len(ids_plan_est))
                    if len(ids_plan_exec) > 0:
                        times_rand_exec = np.random.normal(0.0, timedev, len(ids_plan_exec))

                elif c_probit > 0:
                    if len(ids_plan_est) > 0:
                        times_rand_est = np.zeros(len(ids_plan_est), dtype=np.float32)
                        for i, t in zip(range(len(ids_plan_est)), times_est[ids_plan_est]):
                            times_rand_est[i] = np.random.normal(0.0, c_probit * t, 1)
                    if len(ids_plan_exec) > 0:
                        times_rand_exec = np.zeros(len(ids_plan_exec), dtype=np.float32)
                        for i, t in zip(range(len(ids_plan_exec)), times_exec[ids_plan_exec]):
                            times_rand_exec[i] = np.random.normal(0.0, c_probit * t, 1)

                else:
                    if len(ids_plan_exec) > 0:
                        times_rand_exec = np.zeros(len(ids_plan_exec), dtype=np.float32)
                    if len(ids_plan_est) > 0:
                        times_rand_est = np.zeros(len(ids_plan_est), dtype=np.float32)

                if len(ids_plan_exec) > 0 and len(ids_plan_est) > 0:
                    if min(times_exec[ids_plan_exec]+times_rand_exec) < min(times_est[ids_plan_est]+times_rand_est):
                        self.ids_plan[id_pers] = np.array(ids_plan_exec)[np.argmin(
                            times_exec[ids_plan_exec]+times_rand_exec)]
                    else:
                        self.ids_plan[id_pers] = np.array(
                            ids_plan_est)[np.argmin(times_est[ids_plan_est]+times_rand_est)]
                elif len(ids_plan_exec) == 0:
                    self.ids_plan[id_pers] = np.array(ids_plan_est)[np.argmin(times_est[ids_plan_est]+times_rand_est)]

                else:

                    self.ids_plan[id_pers] = np.array(ids_plan_exec)[np.argmin(
                        times_exec[ids_plan_exec]+times_rand_exec)]

        print('were analyzed  %d persons' % (n_analyzed_persons))
        return True

    def select_plans_next(self, fraction=0.1, **kwargs):
        """
        Select next plan in the plan list as current plant.
        """
        # print 'select_plans_next'
        ids_pers_all = self.get_ids()
        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        for id_pers, id_plan_current, ids_plan in zip(ids_pers, self.ids_plan[ids_pers], self.lists_ids_plan[ids_pers]):
            n_plan = len(ids_plan)
            if n_plan > 0:
                # print '  id_pers,id_plan_current',id_pers,id_plan_current,ids_plan,id_plan_current != -1
                if id_plan_current != -1:
                    ind = ids_plan.index(id_plan_current)
                    # print '    ind',ind,ind +1 < n_plan
                    if ind + 1 < n_plan:
                        ind += 1
                    else:
                        ind = 0
                else:
                    ind = 0

                # print '    ind,n_plan',ind,n_plan,'ids_plan[ind]', ids_plan[ind]
                self.ids_plan[id_pers] = ids_plan[ind]
        # print '  finally: ids_plan=',self.ids_plan.get_value()
        return True

    def select_plans_utility_function(self, fraction=0.1, timedev=-1, c_probit=-1,  **kwargs):
        """
        Select plan with utility function, which is based on the minimum executed travel time selection method
        but considering also a constant (different for each plan strategy) and a multiplicative parameter (the
        same for every strategy).
        """
        strategies = self.get_strategies()
        value_of_time = strategies.value_of_time.get_value()

        plans = self.get_plans()
        ids_pers_all = self.get_ids()

        print('select_plans_utility_function', len(ids_pers_all))

        ids_pers = ids_pers_all[np.random.random(len(ids_pers_all)) > (1.0-fraction)]
        times_exec = self.get_plans().times_exec
        times_est = self.get_plans().times_est
        ids_plans_all = self.lists_ids_plan[ids_pers]

        for id_pers, ids_plan_all_list in zip(ids_pers, ids_plans_all):
            ids_plan_all = np.array(ids_plan_all_list, dtype=np.int32)
            # print '    ids_plan_all',ids_plan_all,type(ids_plan_all)
            n_plan = len(ids_plan_all)

            # if cycle necessary to apply a casual variation on plans executive times (in addition or in reduction)
            if n_plan > 0:
                traveltimes = np.zeros(n_plan, dtype=np.float32)
                traveltimes_noise = np.zeros(n_plan, dtype=np.float32)

                inds_exec = np.logical_and((times_exec[ids_plan_all] > 0.),
                                           (times_exec[ids_plan_all] > times_est[ids_plan_all]))
                inds_est = ~inds_exec

                traveltimes[inds_exec] = times_exec[ids_plan_all[inds_exec]]
                traveltimes[inds_est] = times_est[ids_plan_all[inds_est]]

                if timedev > 0:
                    # noise is normal distributed with deviation timedev
                    traveltimes_noise = np.random.normal(0.0, timedev, n_plan)

                elif c_probit > 0:
                    # noise is normal distributed with deviation  c_probit *traveltimes
                    traveltimes_noise = np.random.normal(0.0,  c_probit * traveltimes, n_plan)

                else:
                    # no travel time noise
                    traveltimes_noise = np.zeros(n_plan, dtype=np.float32)

                for id_plan, strategy, traveltime, timetime_noise in zip(ids_plan_all,
                                                                         strategies.strategies[plans.ids_strategy[ids_plan_all]],
                                                                         traveltimes,
                                                                         traveltimes_noise):

                    utility = strategy.get_utility_specific(id_plan) - value_of_time * (traveltime + timetime_noise)

                    plans.utilities[id_plan] = utility

                # set current plan of person to the one with the maximum utility
                self.ids_plan[id_pers] = ids_plan_all[np.argmax(plans.utilities[ids_plan_all])]

        return True

    def prepare_sim(self, process):
        return []  # [(steptime1,func1),(steptime2,func2),...]

    def get_trips(self):
        # returns trip object, method common to all demand objects
        return self.get_iautos()

    def get_writexmlinfo(self, is_route=False, is_plain=False, **kwargs):
        """
        Returns three array where the first array is the 
        begin time of the first vehicle and the second array is the
        write function to be called for the respectice vehicle and
        the third array contains the vehicle ids

        Method used to sort trips when exporting to route or trip xml file
        """

        plans = self.get_plans()

        ids_pers = self.select_ids(self.ids_plan.get_value() >= 0)
        n_pers = len(ids_pers)
        if n_pers == 0:
            return [], [], []
        ids_plans = self.ids_plan[ids_pers]
        print('Virtualpop.get_writexmlinfo is_plain', is_plain, 'is_route', n_pers, 'persons', len(ids_plans), 'plans.')

        # get vehicle trip info
        times_depart_bike, writefuncs_bike, ids_rides_bike = plans.get_stagetable(
            'bikerides').get_writexmlinfo(ids_plans, is_route, is_plain)
        times_depart_auto, writefuncs_auto, ids_rides_auto = plans.get_stagetable(
            'autorides').get_writexmlinfo(ids_plans, is_route, is_plain)
        times_depart_moto, writefuncs_moto, ids_rides_moto = plans.get_stagetable(
            'motorides').get_writexmlinfo(ids_plans, is_route, is_plain)

        if not is_plain:
            # do persons

            times_depart_pers = plans.times_begin[ids_plans]
            writefuncs_pers = np.zeros(n_pers, dtype=np.object_)
            writefuncs_pers[:] = self.write_person_xml

            # assemble vectors
            # print('  times_depart_pers.shape',times_depart_pers.shape)
            # print('  times_depart_bike.shape',times_depart_bike.shape)
            # print('  times_depart_auto.shape',times_depart_auto.shape)
            times_depart = np.concatenate((times_depart_pers,
                                           times_depart_auto,
                                           times_depart_bike,
                                           times_depart_moto,
                                           ))

            writefuncs = np.concatenate((writefuncs_pers,
                                         writefuncs_auto,
                                         writefuncs_bike,
                                         writefuncs_moto,
                                         ))

            ids = np.concatenate((ids_pers,
                                  ids_rides_auto,
                                  ids_rides_bike,
                                  ids_rides_moto,
                                  ))
        else:
            times_depart = np.concatenate((times_depart_auto,
                                           times_depart_bike,
                                           times_depart_moto,
                                           ))

            writefuncs = np.concatenate((writefuncs_auto,
                                         writefuncs_bike,
                                         writefuncs_moto,
                                         ))

            ids = np.concatenate((ids_rides_auto,
                                  ids_rides_bike,
                                  ids_rides_moto,
                                  ))
            # print('  times_depart_moto.shape',times_depart_moto.shape)
            # print('  times_depart_bike.shape',times_depart_bike.shape)
            # print('  times_depart_auto.shape',times_depart_auto.shape)
            # print ('  Number of plain routes of VP:',len(ids))

        return times_depart, writefuncs, ids

    def write_person_xml(self, fd, id_pers, time_begin, indent=2):

        stages = self.get_plans().get_stages(self.ids_plan[id_pers])

        fd.write(xm.start('person', indent=indent+2))
        fd.write(xm.num('id', self.get_id_sumo_from_id(id_pers)))
        # fd.write(xm.num('depart',self.times_start[id_pers]))
        fd.write(xm.num('depart', max(1, time_begin)))
        fd.write(xm.num('type', self.parent.vtypes.ids_sumo[self.ids_vtype[id_pers]]))

        activity_init, id_stage_init = stages[0]
        id_edge_init, pos_init = activity_init.get_edges_positions(id_stage_init)

        # self.ids_edge_depart.write_xml(fd,id_trip)
        # self.positions_depart.write_xml(fd,id_trip)
        fd.write(xm.num('from', self._edges.ids_sumo[id_edge_init]))
        fd.write(xm.num('departPos', pos_init))

        fd.write(xm.stop())

        # write stages of this person.
        # Attention!! first and last stage, which are activities,
        # will NOT be exportes , therefore [1:-1]
        for stage, id_stage in stages[1:-1]:
            stage.to_xml(id_stage, fd, indent+4)

        fd.write(xm.end('person', indent=indent+2))

    def config_results(self, results):

        results.add_resultobj(res.Personresults('virtualpersonresults', results,
                                                self,
                                                self.get_net().edges,
                                                name='Virtual person results',
                                                info='Table with simulation results for person of the virtual population. The results refer to all trips made by the person during the entire simulation period.',
                                                ), groupnames=['Trip results'])

        results.add_resultobj(res.Vehicleresults('iautotripresults', results,
                                                 self.get_iautos(),
                                                 self.get_net().edges,
                                                 name='Auto trip results',
                                                 info='Table with trip results made by individual autos. The results refer to all trips made by a specific vehicle during the entire simulation period.',
                                                 ), groupnames=['Trip results'])

        results.add_resultobj(res.Vehicleresults('ibiketripresults', results,
                                                 self.get_ibikes(),
                                                 self.get_net().edges,
                                                 name='Bike trip results',
                                                 info='Table with trip results made by individual bikes. The results refer to all trips made by a specific vehicle during the entire simulation period.',
                                                 ), groupnames=['Trip results'])

        results.add_resultobj(res.Vehicleresults('imototripresults', results,
                                                 self.get_imotos(),
                                                 self.get_net().edges,
                                                 name='Motorcycle trip results',
                                                 info='Table with trip results made by individual motorcycles. The results refer to all trips made by a specific vehicle during the entire simulation period.',
                                                 ), groupnames=['Trip results'])

    # def process_results(self, results, process = None):
    #    print 'process_results'
    #    ## copy total travel into plan execution time
    #    personresults = results.virtualpersonresults
    #    self.update_plans(personresults)

    def update_results(self, personresults):
        """
        Updates plans with results from previous 
        simulation run, and updates plan choice
        """

        ids_res = personresults.get_ids()
        print('update_results', len(ids_res))
        ids_person = personresults.ids_person[ids_res]
        ids_plan = self.ids_plan[ids_person]
        self.get_plans().times_exec[ids_plan] = personresults.times_travel_total[ids_res]
        # change mobility plan based on updated travel times
        pass

    def import_routealternatives_xml(self, filepath, is_fastest=True, ids_strategy=None, is_add=False):
        """Reads route alternatives of vehicles, and overwrites existing routes in stages.
        Dependent on the option the fastest routes or routes witg highest probability are imported.
        Used for example read results from Marouter process.
        """
        print('Virtualop.import_routes_xml from %s' % (filepath))
        # if is_clear_trips:
        #    self.clear_trips()

        counter = RouteCounter()
        parse(filepath, counter)
        reader = RouteAlternativesReaderVp(self, counter)
        if 1:  # try:
            parse(filepath, reader)
            reader.insert_routes(ids_strategy=ids_strategy)

        else:  # except KeyError:
            print("Error: Problems with reading routes!", file=sys.stderr)
            raise

    def import_routes_xml(self, filepath,  is_clear_trips=False,
                          ids_strategy=None,
                          is_generate_ids=True, is_add=False,
                          is_overwrite_only=True):
        """Reads route information of vehicles, and overwrites existing routes in stages.
        Used for example read results from Duaiterate process.
        """
        print('Virtualop.import_routes_xml from %s' % (filepath))
        if is_clear_trips:
            self.clear_trips()

        counter = RouteCounter()
        parse(filepath, counter)
        reader = RouteReaderVp(self, counter)
        if 1:  # try:
            parse(filepath, reader)
            print('  call make_routes', is_generate_ids, 'is_add', is_add)
            reader.insert_routes(ids_strategy=ids_strategy)

        else:  # except KeyError:
            print("Error: Problems with reading routes!", file=sys.stderr)
            raise

    def set_routes_sumo(self, ids_veh_sumo, ids_edges_sumo):
        plans = self.get_plans()
        strategies = self.get_strategies()
        id_strat_auto = strategies.names.get_id_from_index('auto')
        id_strat_bike = strategies.names.get_id_from_index('bike')
        id_strat_motorcycle = strategies.names.get_id_from_index('motorcycle')
        n_change_route = 0
        ids_strategy = [id_strat_auto, id_strat_bike, id_strat_motorcycle]

        get_ids_from_indices = self.get_scenario().net.edges.ids_sumo.get_ids_from_indices

        if id_strat_auto in ids_strategy:
            iautos = self.get_iautos()
            stages = iautos.get_stagetable()
            for id_sumo, ids_edge_sumo in zip(ids_veh_sumo, ids_edges_sumo):
                id_veh, id_stage = iautos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    ids_edge = get_ids_from_indices(ids_edge_sumo)
                    # print '    iauto: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        if id_strat_bike in ids_strategy:
            ibikes = self.get_ibikes()
            stages = ibikes.get_stagetable()

            # debug
            id_mode = self.get_net().modes.get_id_mode('bicycle')
            accesslevels = self.get_net().edges.get_accesslevels(id_mode)

            for id_sumo, ids_edge_sumo in zip(ids_veh_sumo, ids_edges_sumo):
                id_veh, id_stage = ibikes.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    ids_edge = get_ids_from_indices(ids_edge_sumo)
                    # print '    ibike: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if np.any(accesslevels[ids_edge] == -1):
                        print('WARNING: ibike', id_sumo, 'missing access')
                        print('  ids_edge', ids_edge)
                        print('  accesslevels', type(accesslevels[ids_edge]), accesslevels[ids_edge])
                        print('  ids_sumo', self.get_net().edges.ids_sumo[ids_edge])
                        # print '  inaccessible edges',self.get_net().edges.ids_sumo[ids_edge[accesslevels[ids_edge] == -1]]
                        ids_edge_orig = stages.ids_edges[id_stage]
                        print('  accesslevels_orig', accesslevels[ids_edge_orig])
                        print('  ids_sumo_orig', self.get_net().edges.ids_sumo[ids_edge_orig])

                    if stages.ids_edges[id_stage] != ids_edge:

                        n_change_route += 1
                        print('  route change of ibike', id_sumo, 'id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        if id_strat_motorcycle in ids_strategy:
            imotos = self.get_imotos()
            stages = imotos.get_stagetable()
            for id_sumo, ids_edge_sumo in zip(ids_veh_sumo, ids_edges_sumo):
                id_veh, id_stage = imotos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    ids_edge = get_ids_from_indices(ids_edge_sumo)
                    # print '    imotos: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        print('  vp.set_routes_sumo: Total number of changed routes:', n_change_route)


class PopulationScaler(Process):
    def __init__(self, virtualpop, logger=None, **kwargs):

        self._init_common('popscaler', name='Population Scaler',
                          parent=virtualpop,
                          logger=logger,
                          info='Scales up number of the population by cloning the existing population. Does not create plans-',
                          )

        attrsman = self.get_attrsman()

        self.scale = attrsman.add(cm.AttrConf('scale', 2.0,
                                              groupnames=['options'],
                                              name='Population scale',
                                              info="Scling of population, must be greater than one, no persons will be deleted.",
                                              ))

        self.hour_delta_max = attrsman.add(cm.AttrConf('hour_delta_max', 0.25,
                                                       groupnames=['options'],
                                                       name='Max. hour spread',
                                                       unit='h',
                                                       info="Maximum absolute random hours added to the begin time when doubling the activities of existing persons.",
                                                       ))

    def do(self):
        self.parent.scale_pop(self.scale, self.hour_delta_max)
        return True


class DuarouterVp(routing.RouterMixin):
    def __init__(self, ident='vpduarouter', virtualpop=None,
                 netfilepath=None,
                 routefilepaths=None,
                 outfilepath=None,
                 is_export_net=True,
                 is_export_trips=True,
                 logger=None,
                 **kwargs):
        print('DuarouterVp.__init__ ')

        self._init_common(ident, name='Virtual Population DUA router',
                          parent=virtualpop,
                          logger=logger,
                          info='DUA router for virtual population.',
                          )
        cml = 'duarouter'
        self.init_cml(cml)  # pass main shell command

        scenario = virtualpop.get_scenario()
        demand = virtualpop.parent
        net = scenario.net
        attrsman = self.get_attrsman()

        if routefilepaths is None:
            routefilepaths = demand.get_routefilepath()

        print('  default routefilepaths', routefilepaths)
        self.add_option('routefilepaths', routefilepaths,
                        groupnames=['files'],
                        cml='--route-files',
                        perm='rw',
                        name='Route file(s)',
                        wildcards='Route XML files (*.rou.xml)|*.rou.xml',
                        metatype='filepaths',
                        info='SUMO route files in XML format.',
                        )

        self.is_virtualpop_only = attrsman.add(cm.AttrConf('is_virtualpop_only', kwargs.get('is_virtualpop_only', False),
                                                           groupnames=['options'],
                                                           name='Consider only virtualpop demand',
                                                           info="""Consider only virtualpop demand.""",
                                                           ))

        # strategychoices = {}
        strategies = virtualpop.get_strategies()
        # ids_strat = strategies.get_ids()
        # for id_strat, name_strat in zip(ids_strat, strategies.names[ids_strat]):
        #    strategychoices[name_strat] = id_strat
        strategychoices = strategies.names.get_indexmap()
        self.ids_strategy = attrsman.add(cm.ListConf('ids_strategy', kwargs.get('ids_strategy', list(strategychoices.values())),
                                                     choices=strategychoices,
                                                     groupnames=['options'],
                                                     name='Rerouted strategies',
                                                     info="""Strategies to be rerouted. Note that the entire demand will be used for routing, but only the selected routes will be reimported in the virtual population.""",
                                                     ))

        self.is_export_net = is_export_net

        if netfilepath is None:
            netfilepath = net.get_filepath()

        self.add_option('netfilepath', netfilepath,
                        groupnames=['_private'],
                        cml='--net-file',
                        perm='r',
                        name='Net file',
                        wildcards='Net XML files (*.net.xml)|*.net.xml',
                        metatype='filepath',
                        info='SUMO Net file in XML format.',
                        )

        if outfilepath is None:
            outfilepath = scenario.get_rootfilepath()+'.dua.rou.xml'

        self.add_option('outfilepath', outfilepath,
                        groupnames=['_private'],
                        cml='--output-file',
                        perm='r',
                        name='Output routefile',
                        wildcards='Route XML files (*.rou.xml)|*.rou.xml',
                        metatype='filepath',
                        info='Output file of the routing process, which is a SUMO route file in XML format.',
                        )

        self.init_options_time(**kwargs)
        self.init_options_methods(**kwargs)
        self.init_options_processing_common(**kwargs)
        self.init_options_processing_dua(**kwargs)
        routing.init_random(self, **kwargs)

    def do(self):
        virtualpop = self.parent
        scenario = virtualpop.get_scenario()
        demand = virtualpop.parent
        net = scenario.net

        if self.is_export_net:
            net.export_netxml(self.netfilepath)

        if self.is_virtualpop_only:
            demandobjects = [virtualpop]
        else:
            demandobjects = None  # means entire demand is exported by default

        demand.export_routes_xml(filepath=self.routefilepaths,
                                 encoding='UTF-8',
                                 demandobjects=demandobjects,
                                 is_route=False,  # allow True if route export is implemened in virtual population self.is_skip_first_routing,# produce routes only if first dua routing is skipped
                                 vtypeattrs_excluded=['times_boarding', 'times_loading'],  # bug of duaiterate!!
                                 is_plain=True,  # this will prevent exporting stops and plans which causes routing errors with stops
                                 )

        self.update_params()
        cml = self.get_cml()

        self.run_cml(cml)
        if self.status == 'success':
            print('  DUA routing done.')
            if os.path.isfile(self.outfilepath):
                print('  outfile exists, start importing routes')
                virtualpop.import_routes_xml(self.outfilepath, ids_strategy=self.ids_strategy)

                return True
            else:
                return False
        return False


class RouteReaderVp(RouteReader):
    def insert_routes(self, ids_strategy=None):

        virtualpop = self._trips  # parent is called trips here need to be changed
        print('RouteReaderVp.insert_routes found %d routes.' % (len(self.ids_sumo)))
        plans = virtualpop.get_plans()
        strategies = virtualpop.get_strategies()
        id_strat_auto = strategies.names.get_id_from_index('auto')
        id_strat_bike = strategies.names.get_id_from_index('bike')
        id_strat_motorcycle = strategies.names.get_id_from_index('motorcycle')
        n_change_route = 0
        if (ids_strategy is None):
            ids_strategy = [id_strat_auto, id_strat_bike, id_strat_motorcycle]

        if id_strat_auto in ids_strategy:
            iautos = virtualpop.get_iautos()
            stages = iautos.get_stagetable()
            for id_sumo, ids_edge in zip(self.ids_sumo, self.routes):
                id_veh, id_stage = iautos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    iauto: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                    stages.ids_edges[id_stage] = ids_edge
                    # print '      after :',stages.ids_edges[id_stage]

        if id_strat_bike in ids_strategy:
            ibikes = virtualpop.get_ibikes()
            stages = ibikes.get_stagetable()
            for id_sumo, ids_edge in zip(self.ids_sumo, self.routes):
                id_veh, id_stage = ibikes.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    ibike: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                    stages.ids_edges[id_stage] = ids_edge
                    # print '      after :',stages.ids_edges[id_stage]

        if id_strat_motorcycle in ids_strategy:
            imotos = virtualpop.get_imotos()
            stages = imotos.get_stagetable()
            for id_sumo, ids_edge in zip(self.ids_sumo, self.routes):
                id_veh, id_stage = imotos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    ibike: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                    stages.ids_edges[id_stage] = ids_edge
                    # print '      after :',stages.ids_edges[id_stage]
        print('  RouteReaderVp: Total number of changed routes:', n_change_route)


class RouteAlternativesReaderVp(RouteAlternativesReader):
    def insert_routes(self, ids_strategy=None):

        virtualpop = self._trips  # parent is called trips here need to be changed
        print('RouteAlternativesReaderVp.insert_routes found %d routes.' % (len(self.ids_sumo)))
        plans = virtualpop.get_plans()
        strategies = virtualpop.get_strategies()
        id_strat_auto = strategies.names.get_id_from_index('auto')
        id_strat_bike = strategies.names.get_id_from_index('bike')
        id_strat_motorcycle = strategies.names.get_id_from_index('motorcycle')
        n_change_route = 0
        if (ids_strategy is None):
            ids_strategy = [id_strat_auto, id_strat_bike, id_strat_motorcycle]

        if id_strat_auto in ids_strategy:
            iautos = virtualpop.get_iautos()
            stages = iautos.get_stagetable()
            for id_sumo, routes, costs, probs in zip(self.ids_sumo, self.routes, self.costs, self.probabilities):
                ind = costs.index(min(costs))
                ids_edge = routes[ind]
                id_veh, id_stage = iautos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    iauto: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        if id_strat_bike in ids_strategy:
            ibikes = virtualpop.get_ibikes()
            stages = ibikes.get_stagetable()

            # debug
            id_mode = virtualpop.get_net().modes.get_id_mode('bicycle')
            accesslevels = virtualpop.get_net().edges.get_accesslevels(id_mode)

            for id_sumo, routes, costs, probs in zip(self.ids_sumo, self.routes, self.costs, self.probabilities):
                ind = costs.index(min(costs))
                ids_edge = routes[ind]
                id_veh, id_stage = ibikes.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    ibike: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if np.any(accesslevels[ids_edge] == -1):
                        print('WARNING: ibike', id_sumo, 'missing access')
                        print('  ids_edge', ids_edge)
                        print('  accesslevels', type(accesslevels[ids_edge]), accesslevels[ids_edge])
                        print('  ids_sumo', virtualpop.get_net().edges.ids_sumo[ids_edge])
                        # print '  inaccessible edges',virtualpop.get_net().edges.ids_sumo[ids_edge[accesslevels[ids_edge] == -1]]
                        ids_edge_orig = stages.ids_edges[id_stage]
                        print('  accesslevels_orig', accesslevels[ids_edge_orig])
                        print('  ids_sumo_orig', virtualpop.get_net().edges.ids_sumo[ids_edge_orig])

                    if stages.ids_edges[id_stage] != ids_edge:

                        n_change_route += 1
                        print('  route change of ibike', id_sumo, 'id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        if id_strat_motorcycle in ids_strategy:
            imotos = virtualpop.get_imotos()
            stages = imotos.get_stagetable()
            for id_sumo, routes, costs, probs in zip(self.ids_sumo, self.routes, self.costs, self.probabilities):
                ind = costs.index(min(costs))
                ids_edge = routes[ind]
                id_veh, id_stage = imotos.get_info_from_id_sumo(id_sumo)
                if (id_veh > -1) & (id_stage > 1):
                    # print '    imotos: id_sumo %s id_veh %d id_stage %d'%(id_sumo,id_veh,id_stage)
                    # print '      before:',stages.ids_edges[id_stage]
                    if stages.ids_edges[id_stage] != ids_edge:
                        n_change_route += 1
                        print('  route change of id_veh', id_veh, 'id_stage', id_stage)
                        stages.ids_edges[id_stage] = ids_edge
                        # print '      after :',stages.ids_edges[id_stage]

        print('  RouteReaderVp: Total number of changed routes:', n_change_route)


class PopGenerator(Process):
    def __init__(self, ident='virtualpopgenerator', virtualpop=None,  logger=None, **kwargs):
        print('PopFromOdfGenerator.__init__ ', ident, virtualpop)

        # TODO: let this be independent, link to it or child??
        #
        scenario = virtualpop.get_scenario()
        self._init_common(ident,
                          parent=virtualpop,
                          name='Population generator',
                          logger=logger,
                          info='Create virtual population from basic statistical data.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        # make for each possible pattern a field for prob
        activitytypes = self.parent.get_scenario().demand.activitytypes

        self.n_person = attrsman.add(cm.AttrConf('n_person', kwargs.get('n_person', 1000),
                                                 groupnames=['options'],
                                                 perm='rw',
                                                 name='Number of person',
                                                 info='Number of adult persons.',
                                                 ))

        self.ids_acttype_default = activitytypes.get_ids_from_formatted('home,work')
        # self.ids_acttype = attrsman.add(cm.AttrConf( 'ids_acttype',kwargs.get('id_acttype',activitytypes.get_id_from_formatted('home')),
        #                    groupnames = ['options'],
        #                    choices = activitytypes.names.get_indexmap(),
        #                    perm='rw',
        #                    name = 'Activity type',
        #                    info = 'Initial activity type.',
        #                    ))

        self.ttb_mean = attrsman.add(cm.AttrConf('ttb_mean', kwargs.get('ttb_mean', 55*60),
                                                 groupnames=['options'],
                                                 perm='rw',
                                                 name='Avg. of 24h travel time budget',
                                                 unit='s',
                                                 info="""Average travel time budget for one day. 
                            This time excludes time for activities.
                             """,
                                                 ))
        self.ttb_dev = attrsman.add(cm.AttrConf('ttb_dev', kwargs.get('ttb_dev', 10*60),
                                                groupnames=['options'],
                                                perm='rw',
                                                name='Std. of 24h travel time budget',
                                                unit='s',
                                                info="""Standard deviation of travel time budget for one day. 
                            """,
                                                ))

        mode_to_id = self.parent.get_scenario().net.modes.get_id_mode
        self.share_pedestrian = attrsman.add(cm.AttrConf('share_pedestrian', kwargs.get('share_pedestrian', 0.1),
                                                         groupnames=['options', 'modal split'],
                                                         perm='rw',
                                                         id_mode=mode_to_id('pedestrian'),
                                                         name='Pedestrian share',
                                                         info="""Share of pedestrians.""",
                                                         ))

        self.share_autouser = attrsman.add(cm.AttrConf('share_autouser', kwargs.get('share_autouser', 0.5),
                                                       groupnames=['options', 'modal split'],
                                                       perm='rw',
                                                       id_mode=mode_to_id('passenger'),
                                                       name='Auto user share',
                                                       info="""Share of auto users.""",
                                                       ))

        self.share_motorcycleuser = attrsman.add(cm.AttrConf('share_motorcycleuser', kwargs.get('share_motorcycleuser', 0.1),
                                                             groupnames=['options', 'modal split'],
                                                             perm='rw',
                                                             id_mode=mode_to_id('motorcycle'),
                                                             name='Motorcycle user share',
                                                             info="""Share of Motorcycle users.""",
                                                             ))

        self.share_bikeuser = attrsman.add(cm.AttrConf('share_bikeuser', kwargs.get('share_bikeuser', 0.1),
                                                       groupnames=['options', 'modal split'],
                                                       perm='rw',
                                                       id_mode=mode_to_id('bicycle'),
                                                       name='Bike user share',
                                                       info="""Share of bike users.""",
                                                       ))

        self.share_ptuser = attrsman.add(cm.AttrConf('share_ptuser', kwargs.get('share_ptuser', 0.2),
                                                     groupnames=['options', 'modal split'],
                                                     id_mode=mode_to_id('bus'),
                                                     perm='rw',
                                                     name='PT share',
                                                     info="""Share of public transport user.""",
                                                     ))
        self.is_delete_existing_pop = attrsman.add(cm.AttrConf('is_delete_existing_pop', kwargs.get('is_delete_existing_pop', True),
                                                               groupnames=['options'],
                                                               name='Delete existing Virtual Population',
                                                               info='If it is True: Delete existing virtual population. If it is False: keep existing virtual population',
                                                               ))

        # self.modeshares = attrsman.add( cm.ObjConf(ModeShares('modeshares',self,scenario.net.modes),groupnames = ['options']) )

    def do(self):
        print('PopGenerator.do')
        # links

        virtualpop = self.parent
        if self.is_delete_existing_pop == True:
            virtualpop.clear_population()
        logger = self.get_logger()
        # logger.w('Update Landuse...')
        scenario = virtualpop.get_scenario()
        activitytypes = scenario.demand.activitytypes
        facilities = scenario.landuse.facilities
        edges = scenario.net.edges

        ids_fac = facilities.get_ids()
        map_id_edge_to_ids_fac = {}
        for id_fac, id_edge in zip(ids_fac, facilities.ids_roadedge_closest[ids_fac]):
            if id_edge in map_id_edge_to_ids_fac:
                map_id_edge_to_ids_fac[id_edge].append(id_fac)
            else:
                map_id_edge_to_ids_fac[id_edge] = [id_fac,]

        n_pers = self.n_person
        unitvec_int = np.ones(n_pers, dtype=np.int32)

        ids_person = virtualpop.make_multiple(n_pers)
        virtualpop.traveltimebudgets[ids_person] = self.get_ttb(ids_person)

        virtualpop.ids_mode_preferred[ids_person] = self.get_modes_random(n_pers)

        # here we could preselect correct landuse based on
        # percentage of workers, students, employees
        prob_fac_to = facilities.capacities[ids_fac].astype(np.float32)
        prob_fac_to /= np.sum(prob_fac_to)
        # print '  np.sum(prob_fac_to)',np.sum(prob_fac_to)
        ids_fac_to = ids_fac[random_choice(n_pers, prob_fac_to)]

        # determine id_fac_from by backward routing from id_fac_to
        ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]

        # pre calculate backward star and mode dependent link travel times
        bstar = edges.get_bstar()
        edgetimes = {}
        ids_mode = self.get_ids_mode()
        # idea: do also consider gradient of house prices
        for id_mode, speed_max in zip(ids_mode, scenario.net.modes.speeds_max[ids_mode]):
            edgetimes[id_mode] = edges.get_times(id_mode=0,  # no mode given here as we do mode independent routing # ADAPTION PYthon 3: mode 0 can pass everywhere
                                                 speed_max=speed_max,  # mode specific speed
                                                 is_check_lanes=False
                                                 )
        # determine home facilities by backwards tracking from work facility
        ids_fac_from = np.ones(n_pers, dtype=np.int32)

        # list with persons with origin outside the network
        # this happens if the time budget is too large and
        # the origin would be out of the network
        ids_person_outside = []
        i = 0
        for id_person, id_edge_to, id_mode, ttb\
                in zip(ids_person,
                       ids_edge_to,
                       virtualpop.ids_mode_preferred[ids_person],
                       virtualpop.traveltimebudgets[ids_person],
                       ):

            # print 10*'-'
            # print '  Backsearch',id_person,'id_edge_to',id_edge_to,edges.ids_sumo[id_edge_to],'ttb[s]',0.5*ttb
            ids_edge_from, costs, btree = routing.edgedijkstra_backwards(id_edge_to,
                                                                         0.5*ttb,  # to be specified better
                                                                         weights=edgetimes[id_mode],
                                                                         bstar=bstar,
                                                                         )
            if len(ids_edge_from) == 0:
                # routing failed to deliver edges of origins
                # origins would be outside
                ids_person_outside.append(id_person)
                ids_fac_from[i] = -1

            else:
                # look at all edges of origin and pick most likely facility
                ids_fac_lim = []
                for id_edge_from in ids_edge_from:

                    id_from_check = id_edge_from
                    print('    check from', id_from_check, 'back to', id_edge_to,
                          'time =%ds' % costs[id_from_check], 'ttb=%ds' % (0.5*ttb))
                    # while id_from_check != id_edge_to:
                    #    id_from_check = btree[id_from_check]
                    #    #print '  id_edge = ',id_from_check
                    # print '  success = ',id_from_check==id_edge_to
                    if id_edge_from in map_id_edge_to_ids_fac:
                        ids_fac_lim += map_id_edge_to_ids_fac[id_edge_from]

                if len(ids_fac_lim) == 0:
                    # no facilities at all destinations found
                    # go edges backawards and search there
                    # this will reduce travel time
                    for id_edge_from in ids_edge_from:
                        # verify if id_edge_from has facilities.
                        while id_edge_from not in map_id_edge_to_ids_fac:
                            # print '  no facility, go backward'
                            id_edge_from = btree[id_edge_from]

                ids_fac_lim = np.array(ids_fac_lim, dtype=np.int32)

                prob_fac_from = facilities.capacities[ids_fac_lim].astype(np.float32)
                prob_fac_from /= np.sum(prob_fac_from)
                # print '  np.sum(prob_fac_to)',np.sum(prob_fac_to)
                ids_fac_from[i] = ids_fac[random_choice(1, prob_fac_to)]

            i += 1

        # idea: adjust wake-up time with employment type
        activities = virtualpop.get_activities()
        ids_activity_from = activities.add_activities(
            self.ids_acttype_default[0] * unitvec_int,
            ids_fac_from,
            # use default
            # hours_begin_earliest = None,
            # hours_begin_latest = None,
            # durations_min = None,
            # durations_max = None,
        )

        ids_activity_to = activities.add_activities(
            self.ids_acttype_default[1] * unitvec_int,
            ids_fac_to,
            # use default
            # hours_begin_earliest = None,
            # hours_begin_latest = None,
            # durations_min = None,
            # durations_max = None,
        )

        for id_person, id_activity_from, ids_activity_to in zip(ids_person, ids_activity_from, ids_activity_to):
            virtualpop.activitypatterns[id_person] = [id_activity_from, ids_activity_to, ]

        # now remove persons with origins outside the net
        virtualpop.del_rows(ids_person_outside)
        return True

    def get_ids_mode(self):
        modesplitconfigs = self.get_attrsman().get_group('modal split')
        ids_mode = np.zeros(len(modesplitconfigs), dtype=np.int32)
        i = 0
        for modeconfig in modesplitconfigs:
            ids_mode[i] = modeconfig.id_mode
            i += 1
        return ids_mode

    def get_modes_random(self, n):
        """
        Return a vector with mode IDs of length n. 
        """
        # print 'get_modes_random',n
        modesplitconfigs = self.get_attrsman().get_group('modal split')
        ids_mode = np.zeros(len(modesplitconfigs), dtype=np.int32)
        shares = np.zeros(len(modesplitconfigs), dtype=np.float32)
        i = 0
        for modeconfig in modesplitconfigs:
            ids_mode[i] = modeconfig.id_mode
            shares[i] = modeconfig.get_value()
            i += 1
        # print '  ids_mode',ids_mode
        # print '  shares',shares
        return ids_mode[random_choice(n, shares/np.sum(shares))]

    def get_ttb(self, ids_pers):
        n_pers = len(ids_pers)

        # Truncated Normal dist with scipy
        # load libraries
        # import scipy.stats as stats
        # lower, upper, mu, and sigma are four parameters
        # lower, upper = 0.5, 1
        # mu, sigma = 0.6, 0.1
        # instantiate an object X using the above four parameters,
        # X = stats.truncnorm((lower - mu) / sigma, (upper - mu) / sigma, loc=mu, scale=sigma)
        # generate 1000 sample data
        # samples = X.rvs(1000)

        return np.random.normal(self.ttb_mean, self.ttb_dev, n_pers).clip(0, 2*3600)


class PopFromOdfGenerator(Process):
    def __init__(self, ident, virtualpop,  logger=None, **kwargs):
        print('PopFromOdfGenerator.__init__')

        # TODO: let this be independent, link to it or child??

        self._init_common(ident,
                          parent=virtualpop,
                          name='Pop from OD-flow generator',
                          logger=logger,
                          info='Create virtual population from origin-to-destination zone flows by disaggregation.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        # make for each possible pattern a field for prob
        activitytypes = self.parent.get_scenario().demand.activitytypes

        self.hour_offset = attrsman.add(cm.AttrConf('hour_offset', kwargs.get('hour_offset', 8.0),
                                                    groupnames=['options'],
                                                    perm='rw',
                                                    name='Offset hours',
                                                    unit='h',
                                                    info='Hour when simulation starts. This is the hour (of the day) when simulation time shows zero seconds.',
                                                    ))

        self.dist_walk_max = attrsman.add(cm.AttrConf('dist_walk_max', kwargs.get('dist_walk_max', 400),
                                                      groupnames=['options'],
                                                      perm='rw',
                                                      name='Max. walking dist',
                                                      unit='m',
                                                      info="""Maximum distance that a person accepts to walk, 
                                    either to reach the destination or to access any sort of vehicle (tranfers at bus stops, walking to carparking, etc).
                             """,
                                                      ))

        self.hour_tripbudget = attrsman.add(cm.AttrConf('hour_tripbudget', kwargs.get('hour_tripbudget', 0.5),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Triptime budget',
                                                        unit='h',
                                                        info="""Time budget for this trip. This time is used
                             to initially estimate the time in hours between 
                             the activity at origin and the activity
                             at destination.
                             """,
                                                        ))

        self.scale = attrsman.add(cm.AttrConf('scale', kwargs.get('scale', 1.0),
                                              groupnames=['options'],
                                              perm='rw',
                                              name='Scale',
                                              info='Global scale factor. Scales the number of all OD trips.',
                                              ))

        self.is_use_landusetypes = attrsman.add(cm.AttrConf('is_use_landusetypes', kwargs.get('is_use_landusetypes', False),
                                                            groupnames=['options'],
                                                            perm='rw',
                                                            name='use landuse types',
                                                            info="""If True, use the landuse type of 
                            facilities when assigning the origin and destination facility. 
                            The landuse type is selected according to the activity type.
                            Use this option only if landuse types have been correctly defined for 
                            all facilities.
                            """,
                                                            ))

        self.is_update_landuse = attrsman.add(cm.AttrConf('is_update_landuse', kwargs.get('is_update_landuse', True),
                                                          groupnames=['options'],
                                                          perm='rw',
                                                          name='update Landuse',
                                                          info="""If True, update land use database (zones, facilities, parking) before generating the population. Updating means identifying edges and facilities within zones.
                            """,
                                                          ))

    def do(self):
        print('PopFromOdfGenerator.do')
        # links

        virtualpop = self.parent
        logger = self.get_logger()
        if self.is_update_landuse:
            logger.w('Update Landuse...')
            scenario = virtualpop.get_scenario()
            scenario.landuse.zones.refresh_zoneedges()
            scenario.landuse.facilities.identify_taz()
            scenario.landuse.facilities.identify_closest_edge()
            scenario.landuse.facilities.update()

        logger.w('Create population...')
        virtualpop.create_pop_from_odflows(logger=logger, **self.get_kwoptions())
        # activitytypes = virtualpop.activitytypes
        return True


class Planner(Process):
    def __init__(self, ident='planner', virtualpop=None,  ids_strategy=None, logger=None, **kwargs):
        print('Planner.__init__')
        # TODO: let this be independent, link to it or child??

        self._init_common(ident,
                          parent=virtualpop,
                          name='Planner',
                          logger=logger,
                          info='Generates mobility plan for population for a specific mobility strategy. Plans are only generated for persons for whome the strategy is applicable.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        # make for each possible pattern a field for prob
        strategies = virtualpop.get_strategies()

        strategychoices = strategies.names.get_indexmap()

        if (ids_strategy is None):
            # no info given, select all
            ids_strategy_default = list(strategychoices.values())

        elif ids_strategy is not None:
            ids_strategy_default = ids_strategy

        # print '  ids_strategy_default',ids_strategy_default
        self.ids_strategy = attrsman.add(cm.ListConf('ids_strategy', ids_strategy_default,
                                                     groupnames=['options'],
                                                     choices=strategychoices,
                                                     perm='rw',
                                                     name='Strategies',
                                                     info='Strategies to be used to create mobility plans.',
                                                     ))

        evalcrits = {'apply to all persons if feasible': 0,
                     'apply only if preferred mode is used': 1,
                     'apply only if exclusively preferred mode is used': 2,
                     }
        self.evalcrit = attrsman.add(cm.AttrConf('evalcrit', kwargs.get('evalcrit', evalcrits['apply to all persons if feasible']),
                                                 groupnames=['options'],
                                                 choices=evalcrits,
                                                 perm='rw',
                                                 name='Application criteria',
                                                 info=""" Value that determines for which persons the plans will be generated.
                                        Apply to all persons if feasible:0
                                        Apply only if preferred mode is used:1
                                        Apply only if exclusively preferred mode is used:2
                            """,
                                                 ))

        self.is_clear_plans = attrsman.add(cm.AttrConf('is_clear_plans', kwargs.get('is_clear_plans', False),
                                                       groupnames=['options'],
                                                       name='Clear all plans',
                                                       info=""" If it is True:Clear all previously generated plans.""",
                                                       ))

        self.is_replace_plan = attrsman.add(cm.AttrConf('is_replace_plan', kwargs.get('is_replace_plan', True),
                                                        groupnames=['options'],
                                                        name='Replace plan',
                                                        info=""" If it is True: Existing plans are replaced if plan for strategy exists.
                                                If it is False: Existing plan are not replaced, plan only plans for strategies that do not exist.
                                    """,
                                                        ))

        self.is_plan_no_preferred = attrsman.add(cm.AttrConf('is_plan_no_preferred', kwargs.get('is_plan_no_preferred', False),
                                                             groupnames=['options'],
                                                             name='Plan for no preferred',
                                                             info=""" If it is True: Generate new plans only for strategies that are different from the preferred strategies.
                                                If it is False: Generate new plans for all strategies
                                    """,
                                                             ))

        self.ids_person = attrsman.add(cm.ListConf('ids_person', kwargs.get('ids_person', []),
                                                   groupnames=['options', '_private'],
                                                   perm='rw',
                                                   name='IDs person',
                                                   info='List with person IDs to which the stategies shall be applied. If left empty stategy will be applied to the entire virtual population.',
                                                   ))

    def do(self):
        print('Planner.do', len(self.ids_strategy))
        # links

        virtualpop = self.parent
        logger = self.get_logger()
        if self.is_clear_plans:
            virtualpop.clear_plans()

        if len(self.ids_person) == 0:
            ids_person = None
        else:
            ids_person = self.ids_person
            logger.w('Check applicability for %d persons' % (len(ids_person)))
        # strategies = virtualpop.strategies.get_value()

        # if self.id_strategy != -1:
        #    virtualpop.plan_with_strategy(self.id_strategy, evalcrit = self.evalcrit, is_npm = self.is_new_planner_method,  logger = logger)
        #
        # else: #plan with all strategies

        i = 1
        strategies = virtualpop.get_strategies()
        print('  strategies:', self.ids_strategy, strategies.names[self.ids_strategy])
        print('             instances', strategies.strategies[self.ids_strategy])

        n_strat = len(self.ids_strategy)
        for id_strategy, strategy in zip(self.ids_strategy, strategies.strategies[self.ids_strategy]):
            print('  current strategy', strategy, id(strategy))
            logger.w('Plan with '+strategy.get_name()+' (%d/%d)' % (i, n_strat))
            virtualpop.plan_with_strategy(id_strategy, evalcrit=self.evalcrit, ids_person=ids_person,
                                          is_replace_plan=self.is_replace_plan, is_plan_no_preferred=self.is_plan_no_preferred,  logger=logger)
            i += 1

        return True


class ActivityCompleter(Process):
    def __init__(self, ident='activitycompletor', virtualpop=None,
                 logger=None, **kwargs):

        print('ActivityCompleter.__init__')

        self._init_common(ident,
                          parent=virtualpop,
                          name='Activity Completer',
                          logger=logger,
                          info='Complete daily activity of each person by making everyone to return to home activity.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        self.is_secondary_activity = attrsman.add(cm.AttrConf('is_secondary_activity', kwargs.get('is_secondary_activity', True),
                                                              groupnames=['options'],
                                                              perm='rw',
                                                              name='Introduce secondary activity',
                                                              info="""Introduce a secondary activity while returning home from primary activity. Secondary activities depend on person attributes.""",
                                                              ))

    def do(self):
        print('ActivityCompleter.do')
        # links

        virtualpop = self.parent
        activities = virtualpop.activities.get_value()
        activitytypes = virtualpop.parent.activitytypes
        logger = self.get_logger()
        # logger.w('Check applicability')
        for id_pers in virtualpop.get_ids():
            ids_activity = virtualpop.activitypatterns[id_pers]
            id_activitytype_last = activities.ids_activitytype[ids_activity[-1]]
            symbol_activity = activitytypes.symbols[id_activitytype_last]
            if symbol_activity != 'h':  # last activity not home activity
                # append first = home activity
                ids_activity.append(ids_activity[0])

        return True


class PlanSelector(Process):
    def __init__(self, ident='planselector', virtualpop=None,
                 ids_strategy=None,  # not yet implemeted
                 methodname='plan with shortest estim. time',
                 logger=None, **kwargs):

        print('PlanSelector.__init__')

        # TODO: let this be independent, link to it or child??

        self._init_common(ident,
                          parent=virtualpop,
                          name='Plan Selector',
                          logger=logger,
                          info='Selects the plan for each person which will be executed during the next simulation run according to a defined selection method.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        # make for each possible pattern a field for prob
        strategies = virtualpop.get_strategies()
        # strategychoices.update(strategies.names.get_indexmap())

        strategychoices = strategies.names.get_indexmap()

        # if  (ids_strategy is None):
        #    # no info given, select all
        #    ids_strategy_default  = strategychoices.values()
        #
        # elif ids_strategy is not None:
        #    ids_strategy_default = ids_strategy

        # print '  ids_strategy_default',ids_strategy_default
        # self.ids_strategy = attrsman.add(cm.ListConf( 'ids_strategy',ids_strategy_default,
        #                    groupnames = ['options'],
        #                    choices = strategychoices,
        #                    perm='rw',
        #                    name = 'Strategies',
        #                    info = 'Set of strategies from which one is selected per person for the next simulation run.',
        #                    ))

        methods = {'plan with shortest estim. time': virtualpop.select_plans_min_time_est,
                   'plan with shortest exec. time': virtualpop.select_plans_min_time_exec,
                   'plan with preferred mode': virtualpop.select_plans_preferred_mode,
                   'next plan in list': virtualpop.select_plans_next,
                   'random plan': virtualpop.select_plans_random,
                   'plan with shortest exec. time or est. time': virtualpop.select_plans_min_time_exec_est,
                   'plan with utility function': virtualpop.select_plans_utility_function
                   }

        self.method = attrsman.add(cm.AttrConf('method', methods[methodname],
                                               groupnames=['options'],
                                               choices=methods,
                                               perm='rw',
                                               name='Selection method',
                                               info='Selection method used to select current plans.',
                                               ))

        self.fraction = attrsman.add(cm.AttrConf('fraction', kwargs.get('fraction', 1.0),
                                                 groupnames=['options'],
                                                 perm='rw',
                                                 name='Change fraction',
                                                 info="""Fraction of persons that are randomly chosen to change plans according to the defined method.
                            A value of 1.0 mens that the plans of oll persons will be changed.""",
                                                 ))

        self.timedev = attrsman.add(cm.AttrConf('timedev', kwargs.get('timedev', 0.0),
                                                groupnames=['options'],
                                                perm='rw',
                                                name='Time deviation',
                                                info="""Time deviation of random time component of estimated or effective time. If zero, no random time is added.
                                      Alternatively the probit constant can be given to quantify the amount of noise.
                                    """,
                                                ))

        self.c_probit = attrsman.add(cm.AttrConf('c_probit', kwargs.get('c_probit', 0.0),
                                                 groupnames=['options'],
                                                 perm='rw',
                                                 name='Probit const',
                                                 info="""Probit constant used to determine the deviation of the normal distributed random time component. 
                                    The deviation is the product of this constant and the travel time. If zero, no random time is added.
                                    This is an alternative to explicitely giving the time deviation.""",
                                                 ))

    def do(self):
        print('Planner.do')
        # links

        # virtualpop = self.parent
        logger = self.get_logger()
        # logger.w('Check applicability')
        return self.method(logger=logger,
                           # ids_strategy = self.ids_strategy,
                           **self.get_kwoptions()
                           )


class VehicleProvider(Process):
    def __init__(self, ident='vehicleprovider', virtualpop=None,  logger=None, **kwargs):
        print('VehicleProvider.__init__')

        # TODO: let this be independent, link to it or child??

        self._init_common(ident,
                          parent=virtualpop,
                          name='Vehicle Provider',
                          logger=logger,
                          info='Provides individual vehicles to persons according to preferred mode and giveb statistical data.',
                          )

        attrsman = self.set_attrsman(cm.Attrsman(self))

        # make for each possible pattern a field for prob

        self.share_autoowner = attrsman.add(cm.AttrConf('share_autoowner', kwargs.get('share_autoowner', 0.8),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Car auto share',
                                                        info="""Share of auto owners. This specifies the share of auto owners and a car will be created for each car owner. 
                                    Attention if prefeered mode has been already defined: persons who have bicicle as preferred mode get automatically a bike assigned.
                                    """,
                                                        ))

        self.share_motorcycleowner = attrsman.add(cm.AttrConf('share_motorcycleowner', kwargs.get('share_motorcycleowner', 0.3),
                                                              groupnames=['options'],
                                                              perm='rw',
                                                              name='Motorcycle owner share',
                                                              info="""Share of Motorcycle owners. This specifies the share of Motorcycle owners and a bike will be created for each Motorcycle owner. 
                                    Attention if prefeered mode has been already defined: persons who have Motorcycle as preferred mode get automatically a Motorcycle assigned.
                                    """,
                                                              ))

        self.share_bikeowner = attrsman.add(cm.AttrConf('share_bikeowner', kwargs.get('share_bikeowner', 0.5),
                                                        groupnames=['options'],
                                                        perm='rw',
                                                        name='Bike owner share',
                                                        info="""Share of bike owners. This specifies the share of bike owners and a bike will be created for each bike owner. 
                                    Attention if prefeered mode has been already defined: persons who have bicicle as preferred mode get automatically a bike assigned.
                                    """,
                                                        ))

    def do(self):
        print('VehicleProvider.do')
        # links

        virtualpop = self.parent
        logger = self.get_logger()
        logger.w('Provide vehicles...')

        ids_person = virtualpop.get_ids()
        n_person = len(ids_person)
        modes = virtualpop.get_scenario().net.modes
        id_mode_bike = modes.get_id_mode('bicycle')
        id_mode_auto = modes.get_id_mode('passenger')
        id_mode_moto = modes.get_id_mode('motorcycle')

        iautos = virtualpop.get_iautos()
        ibikes = virtualpop.get_ibikes()
        imotos = virtualpop.get_imotos()

        logger.w('generate individual vehicles for prefered modes')
        ids_prefer_auto = virtualpop.select_ids(
            (virtualpop.ids_mode_preferred.get_value() == id_mode_auto) & (virtualpop.ids_iauto.get_value() == -1))
        ids_iauto = iautos.assign_to_persons(ids_prefer_auto)

        n_current = iautos.get_share(is_abs=True)
        # n_none = int(self.share_autoowner*n_person)-(n_person-n_current)
        n_need = int(self.share_autoowner*n_person)-n_current
        if n_need > 0:
            ids_pers_miss = np.flatnonzero(virtualpop.ids_iauto.get_value() == -1)+1
            # print '  n_person,n_current,n_target,n_need,len(ids_pers_miss)',n_person,n_current,int(self.share_autoowner*n_person),n_need,len(ids_pers_miss)
            ids_pers_assign = np.random.choice(ids_pers_miss, n_need, replace=False)
            ids_iauto = iautos.assign_to_persons(ids_pers_assign)

        print('  created %d autos, target share=%.2f, share = %.2f' %
              (iautos.get_share(is_abs=True), iautos.get_share(), self.share_autoowner))

        ids_prefer_bike = virtualpop.select_ids(
            (virtualpop.ids_mode_preferred.get_value() == id_mode_bike) & (virtualpop.ids_ibike.get_value() == -1))
        ids_ibikes = ibikes.assign_to_persons(ids_prefer_bike)

        n_current = ibikes.get_share(is_abs=True)
        n_need = int(self.share_bikeowner*n_person)-n_current
        if n_need > 0:
            ids_pers_miss = np.flatnonzero(virtualpop.ids_ibike.get_value() == -1)+1
            # print '  n_person,n_current,n_target,n_need,len(ids_pers_miss)',n_person,n_current,int(self.share_autoowner*n_person),n_need,len(ids_pers_miss)
            ids_pers_assign = np.random.choice(ids_pers_miss, n_need, replace=False)
            ids_ibike = ibikes.assign_to_persons(ids_pers_assign)

        print('  created %d bikes, target share=%.2f, share = %.2f' %
              (ibikes.get_share(is_abs=True), ibikes.get_share(), self.share_bikeowner))

        ids_prefer_moto = virtualpop.select_ids(
            (virtualpop.ids_mode_preferred.get_value() == id_mode_moto) & (virtualpop.ids_imoto.get_value() == -1))
        ids_imoto = imotos.assign_to_persons(ids_prefer_moto)

        n_current = imotos.get_share(is_abs=True)
        n_need = int(self.share_motorcycleowner*n_person)-n_current
        if n_need > 0:
            ids_pers_miss = np.flatnonzero(virtualpop.ids_imoto.get_value() == -1)+1
            ids_pers_assign = np.random.choice(ids_pers_miss, n_need, replace=False)
            ids_imoto = imotos.assign_to_persons(ids_pers_assign)

        print('  created %d moto, target share=%.2f, share = %.2f' %
              (imotos.get_share(is_abs=True), imotos.get_share(), self.share_motorcycleowner))
        return True

        # TODO: generate and assign  additional vehicles
        # to satisfy prescribes ownership


class Virtualhouseholds(am.ArrayObjman):
    def __init__(self, ident, virtualpop, **kwargs):
        self._init_objman(ident=ident,
                          parent=virtualpop,
                          name='Virtual households',
                          info='Contains information of each virtual household.',
                          version=0.1,
                          **kwargs)
        self._init_attributes()

    def _init_attributes(self):

        self.set_version(0.1)

        virtualpop = self.parent
        demand = virtualpop.parent
        print('demand', demand)
        scenario = demand.get_scenario()

        # ===================================================================
        # Add household attributes

        # --------------------------------------------------------------------

        self.add_col(am.IdsArrayConf('buildings', scenario.landuse.facilities,
                                     groupnames=['socioeconomic'],
                                     name='Building',
                                     info='Building representing the house in which the household is located.',
                                     ))

        self.add_col(am.IdlistsArrayConf('virtual_persons', virtualpop,
                                         groupnames=['socioeconomic'],
                                         name='Virtual persons',
                                         info='Virtual persons related to the household.',
                                         ))

        self.add_col(am.ArrayConf('incomes', 100000,
                                  dtype=np.int32,
                                  groupnames=['socioeconomic'],
                                  unit='$',
                                  name='Annual income',
                                  info='Annual income of the household.',
                                  ))

        # --------------------------------------------------------------------
        # household parameters
        self.add_col(am.ArrayConf('sizes', default=1,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Size',
                                  info='Number of persons in household.',
                                  ))

        self.add_col(am.ArrayConf('numbers_minor', default=0,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Number minors',
                                  info='Number of minors in household (uder 14 years old). In the context of traffic simulations minors are persons who need to be accompaigned by adulds when travelling.',
                                  ))

        self.add_col(am.ArrayConf('numbers_major', default=0,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Number majors',
                                  info='Number of majors in household (over 65 years old). In the context of traffic simulations majors are generally retired and have different travel needs.',
                                  ))

        self.add_col(am.ArrayConf('numbers_car', default=0,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Number cars',
                                  info='Number of cars in household.',
                                  ))

        self.add_col(am.ArrayConf('numbers_moto', default=0,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Number motos',
                                  info='Number of motos in household.',
                                  ))

        self.add_col(am.ArrayConf('numbers_bike', default=0,
                                  dtype=np.int32,
                                  groupnames=['household'],
                                  name='Number bikes',
                                  info='Number of bikes in household.',
                                  ))

    def clear_households(self):
        print('clear households')
        self.clear_rows()

        # TODO: generate and assign  additional vehicles
        # to satisfy prescribes ownership
