# -*- coding: utf-8 -*-
# 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    mapmatching.py
# @author  Joerg Schweizer
# @author  Fabian Schuhmann
# @author  Ngoc An Nguyen
# @author  Cristian Poliziani
# @date    2012

"""
This plugin provides methods to match GPS traces to a SUMO road network and to generate routes.

The general structure is as follows:
GPSPoints
routes

Traces
    
"""
import os, sys, re
from xml.sax import saxutils, parse, handler
from collections import OrderedDict
import time, string
import datetime
import numpy as np
from xml.sax import saxutils, parse, handler
from copy import copy, deepcopy
if  __name__ == '__main__':
    try:
        APPDIR = os.path.dirname(os.path.abspath(__file__))
    except:
        APPDIR = os.path.dirname(os.path.abspath(sys.argv[0]))
    hybridPYDIR = os.path.join(APPDIR,'..','..')
    sys.path.append(hybridPYDIR)

##import matplotlib.pyplot as plt
##from matplotlib.path import Path
##import matplotlib.patches as patche
from coremodules.modules_common import *
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 get_inversemap
#from agilepy.lib_base.geometry import get_length_polypoints,get_dist_point_to_segs, get_diff_angle_clockwise
from agilepy.lib_base.geometry import get_dist_point_to_segs, get_length_polypoints, anglediff, is_point_in_polygon
from agilepy.lib_base.processes import Process#,CmlMixin,ff,call
from coremodules.network.network import SumoIdsConf
from coremodules.network import routing 
from coremodules.demand.demandbase import DemandobjMixin
from coremodules.demand.virtualpop import *
from coremodules.demand.demand import Trips, Routes
from coremodules.misc import shapeformat
from coremodules.demand.origin_to_destination import OdIntervals
from agilepy.lib_base.geometry import *
try:
    try:
        import pyproj
    except:
        from mpl_toolkits.basemap import pyproj
    try:
        from shapely.geometry import Polygon, MultiPolygon, MultiLineString, Point, LineString, MultiPoint
        from shapely.ops import cascaded_union
    except:
        print('Import error: No shapely module available.')
except:
    print('Import error: in order to run the traces plugin please install the following modules:')
    print('   mpl_toolkits.basemap and shapely')
    print('Please install these modules if you want to use it.')
    print(__doc__)
    raise

AGES_STRAVA = {'unknown':0,
                'Less than 18':1,
                '18-24':2,
                '25-34':3,
                '35-44':4,
                '45-54':5,
                '55-64':6,
                '65+':7,
                }
                
GENDERS_STRAVA = {'0':'unknown','1':'Female','2':'Male'}
ETHNICITIES_STRAVA = {'unknown':0,
                'White':1,
                'African American':2,
                'Asian':3,
                'Native American':4,
                'Pacific Islander':5,
                'Multi-racial':6,
                'Hispanic / Mexican / Latino':7,
                'Other':8,
}
INCOMES_STRAVA = {'unknown':0,
                'Less than $20,000':1,
                '$20,000 to $39,999':2,
                '$40,000 to $59,999':3,
                '60,000 to $74,999':4,
                '$75,000 to $99,999':5,
                '$100,000 or greater':6,}
CYCLING_FREQUENCIES_STRAVA = {'unknown':0,
                'Less than once a month':1,
                'Several times per month':2,
                'Several times per week':3,
                'Daily':4,}
RIDER_TYPES_STRAVA = {'unknown':0,
                'Strong & fearless':1,
                'Enthused & confident':2,
                'Comfortable, but cautious':3,
                'Interested, but concerned':4,}
RIDER_HISTORIES_STRAVA = {'unknown':0,
                'Since childhood':1,
                'Several years':2,
                'One year or less':3,
                'Just trying it out / just started':4,
}
TRIPPUROPSES = {'unknown':-1,
                'HomeToWork':1,
                'WorkToHome':2,
                'HomeToSchool':3,
                'SchoolToHome':4,
                'SchoolToHome':5,
                'Leisure':6,
                'Other':7,
                'Commute':8,
                'Errand':9,
                'Social':10,
                'Work-Related':11,
                'Exercise':12,
                'School':13,
                'other':14
                }

              
MONTHS = {'January':1,
                'February':2,
                'March':3,
                'April':4,
                'May':5,
                'June':6,
                'July':7,
                'August':8,
                'September':9,
                'October':10, 
                'November':11,
                'December':12,
                }

        
DAYS = {'1':1,
                '2':2,
                '3':3,
                '4':4,
                '5':5,
                '6':6,
                '7':7,
                '8':8,
                '9':9,
                '10':9, 
                '11':11,
                '12':12,
                '13':13,
                '14':14,
                '15':15,
                '16':16,
                '17':17,
                '18':18,
                '19':19,
                '20':20,
                '21':21,
                '22':22,
                '23':23,
                '24':24,
                '25':25,
                '26':26,
                '27':27,
                '28':28,
                '29':29,
                '30':30,
                '31':31,
                }
                
OCCUPATIONS = {'unknown':-1,
                'worker':1,
                'student':2,
                'employee':3,
                'public employee':4,
                'selfemployed':5,
                'pensioneer':6,
                'other':7
                }
 
##            seconds_departure[i] = dp.tm_hour*3600+dp.tm_min*60+dp.tm_sec
##            ar = localtime(points.timestamps[starting_point])
##            seconds_arrive[i] = ar.tm_hour*3600+ar.tm_min*60+ar.tm_sec
GENDERS = {'male':0,'female':1,'unknown':-1}
               
DEVICES = {'unknown':-1,
                'cy-android':1,
                'cy-windows':2,
                'cy-ios':3,
                'cy-web-gpx':4,
                'cy-web-manual':5,
                'Other':7
                }
WEEKDAYCHOICE = {  'Monday':[0],
                    'Thuesday':[1],
                    'Wednesday':[2],
                    'Thursday':[3],
                    'Friday':[4],
                    'Saturday':[5],
                    'Sunday':[6],
                    'all':[0,1,2, 3,4,5,6]}
                  
                    
WEEKDAYCHOICES = {  'Monday-Friday':[0,1,2,3,4],
                    'Monday-Saturday':[0,1,2,3,4,5],
                    'Saturday, Sunday':[5,6],
                    'all':[0,1,2, 3,4,5,6]
##            seconds_departure[i] = dp.tm_hour*3600+dp.tm_min*60+dp.tm_sec
##            ar = localtime(points.timestamps[starting_point])
##            seconds_arrive[i] = ar.tm_hour*3600+ar.tm_min*60+ar.tm_sec3,4,5,6],
                    }
  
COLOR_SHORTEST_ROUTE = np.array([108,183, 0,  0.6*255], np.float32)/255
COLOR_MATCHED_ROUTE =  np.array([255,210, 2,  0.6*255], np.float32)/255 
COLOR_FASTEST_ROUTE =  np.array([198,0  ,255, 0.6*255], np.float32)/255 
# Structure GPS traces


# 2016

#UserID                        TripID                        TimeStamp    Start DT                    Distance     ECC     AvgSpeed     TrackType     Sex     Year     Profession     Frequent User     ZIP     Source      TypeOfBike     TipeOfTrip     Max Spd
#57249bcd88c537874f9fa1ae    57515edc88c537576ca3e16f    1464945480    2016-06-03T09:18:00.000Z    4.75    4.75        10.16        urban bicycle    F    1999    Studente        yes                    cy-web-gpx    MyBike        HomeToSchool    25.45
#5550800888c53765217661aa    574ec13788c537476aa3e122    1464797040    2016-06-01T16:04:00.000Z    2.93    2.93        5.87        urban bicycle    F    1972    Worker            yes            40136    cy-web-gpx    --    --                        0
#5535586e88c53786637b23c6    574f3c3688c537c877a3e139    1464796080    2016-06-01T15:48:00.000Z    8.95    8.95        7.84        urban bicycle    M    1973    Lavoratore        yes            40133    cy-web-gpx    MyBike        HomeToWork        34.08
#5550800888c53765217661aa    574ec12b88c537812fa3e118    1464781800    2016-06-01T11:50:00.000Z    3.75    3.75        14.99        urban bicycle    F    1972    Worker            yes            40136    cy-web-gpx    --    --        0
#5550800888c53765217661aa    574ec11988c5370944a3e107    1464778980    2016-06-01T11:03:00.000Z    2.77    2.77        11.09        urban bicycle    F    1972    Worker            yes            40136    cy-web-gpx    --    --        0
#55e5edfc88c5374b4974fdbc    574e98c988c5378163a3e11f    1464765060    2016-06-01T07:11:00.000Z    11.09    11.14        15.63        urban bicycle        1982                    SI                    cy-web-gpx    MyBike        HomeToWork        27.9


# csv 2016
# TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
# 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
#5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid
# timestamp: 1464160924 after 1970


# 2015 csv 

# workouts
#UserID     TripID     TimeStamp     Start DT      Distance     AvgSpeed     TrackType     Sex     Year     Profession     Frequent User     ZIP
#54eb068de71f393530a9a74d    54eb0737e71f394c2fa9a74d    1424692504    Mon, 23 Feb 2015 11:55:04 GMT    0    0    urban bicycle    M    1987    Developer    no    
#54eb9374e71f39f02fa9a750    5505cb04e71f39542e25e2d4    1426442994    Sun, 15 Mar 2015 18:09:54 GMT    0    0.7    urban bicycle    M    1974    Worker    yes    40128


#TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
#54eb0737e71f394c2fa9a74d,1424692509,"Mon, 23 Feb 2015 11:55:09 GMT",44.499096,11.361185,49.419395,0,0.000815,start



#https://docs.python.org/2/library/time.html
#>>> t = time.mktime((2017,03,07,13,46,0,0,0,0))
#>>> lt = time.localtime(t)
#>>> time.strftime("%a, %d %b %Y %H:%M:%S +0000", lt)
#'Tue, 07 Mar 2017 13:46:00 +0000'
#>>> time.strftime("%a, %d %b %Y %H:%M:%S", lt)
#'Tue, 07 Mar 2017 13:46:00'

def load_results(filepath, parent = None, logger = None):
    # typically parent is the scenario
    results = cm.load_obj(filepath, parent=parent)
    if logger  is not None:
        results.set_logger(logger)
    return results

def calc_seconds(   t_data, 
                    sep_date_clock = ' ', sep_date = '-', sep_clock = ':', 
                    is_float = False, sep_subsec = ''):
    """
    Returns time in seconds after 1/1/1970.
    Time format for time data string used:
        2012-05-02 12:57:08
        2023-01-01T16:28:44+00:00 needs sep_date_clock = 'T' and sep_subsec = '+'
    """ 
    
    #print ('calc_seconds',t_data)
    if len(t_data.split(sep_date_clock))!=2:
        return -1
    (date, clock_raw) = t_data.split(sep_date_clock) 
    
    if sep_subsec != '':
        clock, clock_subsec = clock_raw.split(sep_subsec)
        
    else:
        clock = clock_raw
        
    if (len(clock.split( sep_clock))==3)&(len(date.split(sep_date))==3):
        (year_str,month_str,day_str) = date.split(sep_date)
        #print ('  year_str,month_str,day_str',year_str,month_str,day_str)
        (hours_str,minutes_str,seconds_str) = clock.split(sep_clock)
        
        #print ('  hours_str,minutes_str,seconds_str',hours_str,minutes_str,seconds_str)
        t=time.mktime(( int(year_str),int(month_str),int(day_str),
                        int(hours_str),int(minutes_str),int(float(seconds_str)),-1,-1,-1))

        
        if is_float:
            return t
        else:
            return int(t)
    else:
        return -1
    

  
def find_longest_common_sequence_index(a,b):
    """
    Longest common sequence
    returns list indexes ia, ib and length n such that
        The longest sequence in list a is a[ia: ia + n]
        The longest sequence in list b is b[ib: ib + n]
    
    """
    # Email   : hipersayan DOT x AT gmail DOT com
    # Web-Site: http://hipersayanx.blogspot.com/
    ja = -1
    jb = -1
    n = 0
 
    if a == [] or b == []:
        return ja, jb, n
 
    l = len(a) + len(b) - 1
    ia = len(a) - 1
    ib = 0
    s = 1
 
    for k in range(l):
        nCur = 0
 
        for r in range(s):
            if a[ia + r] == b[ib + r]:
                nCur += 1
 
                if nCur > n:
                    ja = ia + r - nCur + 1
                    jb = ib + r - nCur + 1
                    n = nCur
            else:
                nCur = 0
 
        if k < min(len(a), len(b)) - 1:
            ia -= 1
            s += 1
        elif k > l - min(len(a), len(b)) - 1:
            ib += 1
            s -= 1
        elif ia > 0:
            ia -= 1
        else:
            ib += 1
 
    return ja, jb, n
    

def is_sublist(a, b):
    for i in range(len(a) - len(b) + 1):
        if a[i:i + len(b)] == b:
            return True
    return False

def find_sublist(mylist, pattern):
    matches = []
    for i in range(len(mylist)):
        if mylist[i] == pattern[0] and mylist[i:i+len(pattern)] == pattern:
            matches.append(pattern)
    return matches
 
def get_routelinestring(route, edges):
        #print 'get_routelinestring'
        # TODO: we could make the np.sum of shapes if shapes where lists
        shape = []
        for id_edge in route:
            #print '    ',edges.shapes[id_edge ],type(edges.shapes[id_edge ])
            shape += list(edges.shapes[id_edge ])
        #print '  ',shape
        
        return LineString(shape)
    
  

def get_boundary(coords):
    #print 'get_boundary',len(coords),coords.shape
    #print '  coords',coords
    if len(coords)==0:
        return [ 0,0,0,0]
    else:
        x_min,y_min = coords[:,:2].min(0)
        x_max,y_max = coords[:,:2].max(0)
        
        return [x_min,y_min,x_max,y_max] 
       
class OdCreator(Process):
    def __init__(self, ident, mapmatching, logger = None, **kwargs):
        print('VpCreator.__init__')
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Od routes creator from GPS trips', 
                            logger = logger,
                            info ='Creation of the Od demand, trips and routes from the GPS trips.',
                            )      
        attrsman = self.set_attrsman(cm.Attrsman(self))
     
     
        self.is_clear_flows = attrsman.add(cm.AttrConf('is_clear_flows', kwargs.get('is_clear_flows',True),
                                        groupnames = ['options'], 
                                        name = 'Clear flows', 
                                        info = 'Clear all the OD flow tables.',
                                        ))  
                                        
        self.is_add_flows = attrsman.add(cm.AttrConf('is_add_flows', kwargs.get('is_add_flows',True),
                                        groupnames = ['options'], 
                                        name = 'Add flows', 
                                        info = 'Add the guessed flows to the flow table.',
                                        )) 
      
        self.t_start = attrsman.add(cm.AttrConf('t_start', kwargs.get('t_start',8),
                                        groupnames = ['options'], 
                                        name = 'Starting time of the interval', 
                                        info = 'Begin time of interval in hour (also fractional hour).',
                                        unit = 'h',
                                        )) 
                                        
        self.t_end = attrsman.add(cm.AttrConf('t_end', kwargs.get('t_end',10),
                                        groupnames = ['options'], 
                                        name = 'Ending time of the interval', 
                                        info = 'End time of interval in hour (also fractional hour).',
                                        unit = 'h',
                                        ))  
                                        
        self.scale = attrsman.add(cm.AttrConf('scale', kwargs.get('scale',1.0),
                                        groupnames = ['options'], 
                                        name = 'Scale of the od flow', 
                                        info = 'scale parameter to apply to each od flow.',
                                        )) 
                                        
        scenario = self.parent.get_scenario()
        activitytypes = scenario.demand.activitytypes
        self.ids_activitytype_orig = attrsman.add(cm.AttrConf('ids_activitytype_orig', kwargs.get('activitytypes',activitytypes.names.get_indexmap()['home']),
                                         groupnames=['options'],
                                         perm='rw',
                                         choices = activitytypes.names.get_indexmap(),
                                         name='Activity type at orig.',
                                         symbol='Act. orig.',
                                         info='Type of activity performed at the origin, before the trip.',
                                         #xmltag = 'actType',
                                         #xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                         ))
                                         
        self.ids_activitytype_dest = attrsman.add(cm.AttrConf('ids_activitytype_dest', kwargs.get('activitytypes',activitytypes.names.get_indexmap()['work']),
                                         groupnames=['options'],
                                         perm='rw',
                                         choices = activitytypes.names.get_indexmap(),
                                         name='Activity type at dest.',
                                         symbol='Act. dest.',
                                         info='Type of activity performed at the destination, after the trip.',
                                         #xmltag = 'actType',
                                         #xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                         ))
                                         
        vtypes = scenario.demand.vtypes   
        self.od_modes = attrsman.add(cm.AttrConf('od_modes', kwargs.get('od_modes',vtypes.get_modechoices()['bicycle']),
                                     groupnames=['options'],
                                     choices = vtypes.get_modechoices(),
                                     name='vehicle type',
                                     symbol='Mode type.',
                                     info='transport mode of the flow.',
                                     ))   
    def do(self):
        print('OdCreator.__do__')
        #Preparation
        logger = self.get_logger()
        scenario = self.get_scenario()
        net = scenario.net
        trips = self.parent.trips
        points = self.parent.points
        ids_trip = trips.get_ids()
        ids_trip = ids_trip[(trips.are_selected[ids_trip] == True) &(trips.ids_points[ids_trip] != int)]
        n_pers = len(ids_trip)
        demand = scenario.demand
        mapmatching = self.parent.parent
        zones = scenario.landuse.zones
##        ids_sumo_zone = zones.ids_sumo.get_value()
        ids_zones = zones.get_ids()
        ids_points = trips.ids_points[ids_trip]
        from_zone =np.zeros(np.max(ids_trip)+1, dtype = np.object_) 
        to_zone =np.zeros(np.max(ids_trip)+1, dtype = np.object_) 
        ids_zone = zones.get_ids()     
        od_matrix = np.zeros((len(ids_zone),len(ids_zone)))
        analyzedtrips = 0 
        for  id_trip, ids_point in zip(ids_trip,ids_points): 
            id_final_point = ids_point[-1]
            id_initial_point = ids_point[0]  
                           
            for id_zone in ids_zone:
                if   is_point_in_polygon(points.coords[id_initial_point], zones.shapes[id_zone]):
                   from_zone[id_trip] = id_zone
                   analyzedtrips += 0.5
                   break
            for id_zone in ids_zone: 
                if   is_point_in_polygon(points.coords[id_final_point], zones.shapes[id_zone]):
                   to_zone[id_trip] = id_zone
                   analyzedtrips += 0.5 
                   break      
            print(analyzedtrips)
        i = 0
        j= 0
        for id_zonei in ids_zone:
            for id_zonej in ids_zone:
                for id_trip in ids_trip:
                    if from_zone[id_trip] == id_zonei and to_zone[id_trip] == id_zonej:
                        od_matrix[i,j]+=1
                j += 1
            i += 1 
            j = 0

        n_trip = np.sum(od_matrix) 
        print(' number of trips', n_trip)
        generated = np.zeros(len(od_matrix[0,:]))
        attracted  = np.zeros(len(od_matrix[:,0]))
        
        for i in range(len(ids_zone)):
            generated[i] = np.sum(od_matrix[i,:])
            attracted[i] = np.sum(od_matrix[:,i])
            
        if self.is_clear_flows:
            OdIntervals.clear_od_trips(scenario.demand.odintervals)
                
        if self.is_add_flows:
            i = 0
            j = 0
            for zone_orig in ids_zone:
                for zone_dest in  ids_zone:
                    OdIntervals.add_od_flow( scenario.demand.odintervals, t_start = self.t_start*3600, 
                                        t_end = self.t_end*3600, id_mode = self.od_modes,
                                        id_activitytype_orig = self.ids_activitytype_orig, 
                                        id_activitytype_dest = self.ids_activitytype_dest,
                                        scale = self.scale,
                                        name_orig = zones.ids_sumo[zone_orig], 
                                        name_dest =  zones.ids_sumo[zone_dest], 
                                        tripnumber = od_matrix[i,j])
                    j += 1
                i += 1
                j = 0
            
            
           
        re_allocates = generated - attracted
        print('generated',generated, 'attracted',attracted, 're_allocates', re_allocates)
        print('od_matrix',od_matrix)
        np.savetxt('od_matrix.csv', od_matrix)

        return od_matrix, generated, attracted, re_allocates                     
                       
    def get_scenario(self):
        return self.parent.get_scenario()   
                       
       
                                            
                       
class OdRouteCreator(Process):
    def __init__(self, ident, mapmatching, logger = None, **kwargs):
        print('OdRouteCreator.__init__')
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Od routes creator from GPS trips', 
                            logger = logger,
#                           info ='Creation of the Od demand, trips and routes from the GPS trips.',
                            )      
        attrsman = self.set_attrsman(cm.Attrsman(self))
        

  
    def do(self):
        print('OdRouteCreator.__do__')
        #Preparation
        logger = self.get_logger()
        scenario = self.get_scenario()
        demand = scenario.demand
        
        trips = self.parent.trips
        routes = trips.get_routes()
        
        trips_demand = demand.trips
        
        ids_trip = trips.get_ids()
        ids_trip = ids_trip[trips.are_selected[ids_trip] & np.logical_not(np.equal(trips.ids_points[ids_trip],None))]
        n_trips = len(ids_trip)
        print('  len(ids_trip)',n_trips)

        trips_demand.clear_trips()
        trips_demand.clear_routes()
        second_departures = np.zeros(n_trips, dtype = np.int32)
        ids_edge_depart  = np.zeros(n_trips, dtype = np.int32)
        ids_edge_arrival  = np.zeros(n_trips, dtype = np.int32)
        for ind, id_trip in zip (np.arange(n_trips),ids_trip):
            dp = time.localtime(trips.timestamps[id_trip])
            second_departures[ind] = dp.tm_hour*3600+dp.tm_min*60+dp.tm_sec
            ids_edge_depart[ind] = routes.ids_edges[trips.ids_route_matched[id_trip]][0]
            ids_edge_arrival[ind] = routes.ids_edges[trips.ids_route_matched[id_trip]][-1]
        
        
        ids_routes_demand, ids_trip_demand = trips_demand.make_routes(\
                                trips.ids_vtype[ids_trip], 
                                times_depart = second_departures,
                                ids_edge_depart = ids_edge_depart,
                                ids_edge_arrival = ids_edge_arrival,
                                is_generate_ids = True,
                                is_add = False,
                                routes = deepcopy(routes.ids_edges[trips.ids_route_matched[ids_trip]]))
        
        
        
        
        self.ids_trip_gps = ids_trip
        self.ids_trip_demand = ids_trip_demand 
        #print '  max(ids_trip_gps)',np.max(ids_trip)
        #print '  max(ids_trip_demand)',np.max(ids_trip_demand)
        return True 
        #ricalcola meglio i timestamps, ricordati che ho cambiato il calcolo dei timestamp in mapmatching,
        #controlla che non ci fosse altro da modificare nei filtri, fai il plot delle parenze
    
    
    def get_scenario(self):
        return self.parent.get_scenario()
                                                             
class VpCreator(Process):
    def __init__(self, ident='vpcreator', mapmatching=None, logger = None, **kwargs):
        print('VpCreator.__init__')
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'VP creator from GPS trips', 
                            logger = logger,
                            info ='Creation of the virtual population from the GPS trips.',
                            )      
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.scale = attrsman.add(cm.AttrConf( 'scale',kwargs.get('scale',1.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Pop scale', 
                            info = 'Scale gives the number of persons created for each GPS person or trip, dependent on the Creation method.',
                            ))
        self.mean_radius = attrsman.add(cm.AttrConf( 'mean_radius',kwargs.get('mean_radius',100.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = 'm',
                            name = 'Mean Radius', 
                            info = 'Mean distance of the home activity from the beginning of the traces',
                            ))
        self.dev_radius = attrsman.add(cm.AttrConf( 'dev_radius',kwargs.get('dev_radius',50.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = 'm',
                            name = 'Standard deviation Radius', 
                            info = 'Standard deviation of the mean distance of the home activity from the beginning of the traces',
                            ))
        mapmatching = self.parent.parent
        scenario = mapmatching.get_scenario()
        activitytypes = scenario.demand.activitytypes     
        activitychoices =  activitytypes.names.get_indexmap()
        self.ids_activitytype_orig = attrsman.add(cm.AttrConf('ids_activitytype_orig', activitychoices[kwargs.get('activitytype_orig','home')],
                                         groupnames=['options'],
                                         perm='rw',
                                         choices = activitychoices,
                                         name='Activity type at orig.',
                                         symbol='Act. orig.',
                                         info='Type of activity performed at the origin, before the trip.',
                                         #xmltag = 'actType',
                                         #xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                         ))
        self.ids_activitytype_dest = attrsman.add(cm.AttrConf('ids_activitytype_dest', activitychoices[kwargs.get('activitytype_dest','work')],
                                         groupnames=['options'],
                                         perm='rw',
                                         choices = activitychoices,
                                         name='Activity type at dest.',
                                         symbol='Act. dest.',
                                         info='Type of activity performed at the destination, after the trip.',
                                         #xmltag = 'actType',
                                         #xmlmap = get_inversemap( activitytypes.names.get_indexmap()),
                                         ))
        self.is_first_method = attrsman.add(cm.AttrConf( 'is_first_method',kwargs.get('is_first_method',True),
                                                         choices = {'1 virtual person per recorded trip': True, '1 virtual person for each person in person DB': False},
                                                         groupnames = ['options'],
                                                         name = 'Creation method',
                                                         info = 'If True use first method (1 person for each recorded trip). Otherwise use second method (1 person for each person who recorded at least one trip)',
                                                         ))
        strategies = scenario.demand.virtualpop.get_strategies()
        strategychoices = {'all':-1}

        
        strategychoices.update(strategies.names.get_indexmap())
        self.id_strategy = attrsman.add(cm.AttrConf( 'id_strategy',strategychoices[kwargs.get('strategy','bike')],
                                                     groupnames = ['options'],
                                                     choices = strategychoices,
                                                     perm='rw',
                                                     name = 'Strategy',
                                                     info = 'Strategy to be used to create mobility plane. In case of all strategies, the planner generates all applicable plans.',
                                                     ))
        self.is_select_time = attrsman.add(cm.AttrConf( 'is_select_time',kwargs.get('is_select_time',False),
                                                         groupnames = ['options'],
                                                         name = 'VpCreator Selection Time',
                                                         info = 'If it is True: you can select the time interval in which plans will start. If it is False: it takes the mapmatching timestamps',
                                                         ))
        self.from_second = attrsman.add(cm.AttrConf( 'from_second',kwargs.get('from_second',25200),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = 's',
                            name = 'Time interval start', 
                            info = 'Time interval start for plans generation from GPS traces',
                            ))
        self.to_second = attrsman.add(cm.AttrConf( 'to_second',kwargs.get('to_second',28800),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = 's',
                            name = 'Time interval end', 
                            info = 'Time interval end for plans generation from GPS traces',
                            ))
                            
        self.is_delete_existing_pop = attrsman.add(cm.AttrConf( 'is_delete_existing_pop',kwargs.get('is_delete_existing_pop',False),
                                    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.is_update_bike_vtypes = attrsman.add(cm.AttrConf( 'is_update_bike_vtypes',kwargs.get('is_update_bike_vtypes',False),
                                    groupnames = ['options'],
                                    name = 'update ibike vtypes',
                                    info = 'Update the ibike vtypes based on age and gender of cyclists',
                                    ))
                                    
        self.year_database = attrsman.add(cm.AttrConf( 'year_database',kwargs.get('year_database',2017),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = '',
                            name = 'Database year', 
                            info = 'year of the database',
                            ))
                            
        self.age_treshold = attrsman.add(cm.AttrConf( 'age_treshold',kwargs.get('age_treshold',40),
                            groupnames = ['options'], 
                            perm='rw', 
                            unit = '',
                            name = 'Treshold age', 
                            info = 'Treshold between young and old cyclists: this number is included with youngs',
                            ))
                            
        vtypechoices = scenario.demand.get_vtypes().ids_sumo.get_indexmap()

        self.male_young = attrsman.add(cm.AttrConf( 'male_young',vtypechoices['bicycle'],
                                                     groupnames = ['options'],
                                                     choices = vtypechoices,
                                                     perm='rw',
                                                     name = 'Vtype young males',
                                                     info = 'ID Vtype for young males.',
                                                     ))
        self.male_old = attrsman.add(cm.AttrConf( 'male_old',vtypechoices['bicycle'],
                                                     groupnames = ['options'],
                                                     choices = vtypechoices,
                                                     perm='rw',
                                                     name = 'Vtype old males',
                                                     info = 'ID Vtype for old males.',
                                                     ))                                                     
        self.female_young = attrsman.add(cm.AttrConf( 'female_young',vtypechoices['bicycle'],
                                                     groupnames = ['options'],
                                                     choices = vtypechoices,
                                                     perm='rw',
                                                     name = 'Vtype young females',
                                                     info = 'ID Vtype for young females.',
                                                     ))
        self.female_old = attrsman.add(cm.AttrConf( 'female_old',vtypechoices['bicycle'],
                                                     groupnames = ['options'],
                                                     choices = vtypechoices,
                                                     perm='rw',
                                                     name = 'Vtype old females',
                                                     info = 'ID Vtype for old females.',
                                                     ))
        
                                                           
    def do(self):
        print('VpCreator.__do__')
        #Preparation
        logger = self.get_logger()
        scenario = self.get_scenario()
        demand = scenario.demand
        net = scenario.net
        virtualpop =  scenario.demand.virtualpop
        strategies = virtualpop.get_strategies()
        modes = net.modes
        
        
        if self.is_delete_existing_pop == True:
            virtualpop.clear_population()
            virtualpop.activities.clear()
        trips = self.parent.trips
        points = self.parent.points
        facilities = scenario.landuse.facilities
        activities = virtualpop.get_activities()
        ids_facilities = facilities.get_ids()
        centroids = facilities.centroids
        ids_trip_all = trips.get_ids()
        #Only selected and with almost 2 GPS-points trips are cosidered
        ids_trip = ids_trip_all[(trips.are_selected[ids_trip_all] == True) & (trips.ids_points[ids_trip_all] != int)]
        
        ids_mode = demand.vtypes.ids_mode[trips.ids_vtype[ids_trip]]

        #strategies
##        ids_trip_bike = ids_trip[(ids_mode == 2)]
##        ids_trip_bus = ids_trip[(ids_mode == 5)]
##        ids_trip_ped = ids_trip[(ids_mode == 1)]
        ids_trip_bike = np.array([], dtype = np.int32)
        ids_trip_bus = np.array([], dtype = np.int32)
        ids_trip_ped = np.array([], dtype = np.int32)

        persons = scenario.demand.mapmatching.persons
        persons.analyze()
        print('id_strategy =',self.id_strategy)
        print('is_first_method =',self.is_first_method)
        #select strategy
        if self.is_first_method == True:
            if self.id_strategy == -1:
                ids_trip_bike = ids_trip[(ids_mode == modes.get_id_from_formatted('bicycle'))]
                ids_trip_bus = ids_trip[(ids_mode == modes.get_id_from_formatted('bus'))]
                ids_trip_ped = ids_trip[(ids_mode == modes.get_id_from_formatted('pedestrian'))]
                n_pers = len(ids_trip_bike)+len(ids_trip_bus)+len(ids_trip_ped)
                ids_trip = ids_trip_bike
                ids_trip = np.append(ids_trip,ids_trip_bus)
                ids_trip = np.append(ids_trip,ids_trip_ped)
                print('ids_trip =',ids_trip)
                # ids_trip = ids_trip_bike + ids_trip_ped + ids_trip_bus
            if self.id_strategy == strategies.get_id_from_formatted('walk'):
                ids_trip_ped = ids_trip[(ids_mode == modes.get_id_from_formatted('pedestrian'))]
                n_pers = len(ids_trip_ped)
                ids_trip = ids_trip_ped
            if self.id_strategy == strategies.get_id_from_formatted('transit'):
                ids_trip_bus = ids_trip[(ids_mode == modes.get_id_from_formatted('bus'))]
                n_pers = len(ids_trip_bus)
                ids_trip = ids_trip_bus
            if self.id_strategy == strategies.get_id_from_formatted('bike'):
                ids_trip_bike = ids_trip[(ids_mode == modes.get_id_from_formatted('bicycle'))]
                n_pers = len(ids_trip_bike)
                ids_trip = ids_trip_bike
            
                
##            ids_points = trips.ids_points[ids_trip]

        if self.is_first_method == False:
            if self.id_strategy == -1:
                ids_trip_bike = ids_trip[(ids_mode == modes.get_id_from_formatted('bicycle'))]
                ids_trip_bus = ids_trip[(ids_mode == modes.get_id_from_formatted('bus'))]
                ids_trip_ped = ids_trip[(ids_mode == modes.get_id_from_formatted('pedestrian'))]
                n_trips_for_scale = len(ids_trip_bike)+len(ids_trip_bus)+len(ids_trip_ped)
                
                ids_pers_bike = trips.ids_person[ids_trip_bike]
                ids_pers_bike = np.unique(ids_pers_bike)
                ids_pers = ids_pers_bike
                ids_pers_bus = trips.ids_person[ids_trip_bus]
                ids_pers_bus = np.unique(ids_pers_bus)
                ids_pers = np.append(ids_pers,ids_pers_bus)
                ids_pers_ped = trips.ids_person[ids_trip_ped]
                ids_pers_ped = np.unique(ids_pers_ped)
                ids_pers = np.append(ids_pers,ids_pers_ped)
                ids_pers = np.unique(ids_pers)     
                
                #ids_pers_all = persons.get_ids()
                #ids_pers = ids_pers_all[(persons.numbers_tot_trip_gps[ids_pers_all]>0)]
                n_pers = len(ids_pers)
            if self.id_strategy == strategies.get_id_from_formatted('walk'):
                ids_trip_ped = ids_trip[(ids_mode == modes.get_id_from_formatted('pedestrian'))]
                n_trips_for_scale = len(ids_trip_ped)
                ids_pers_ped = trips.ids_person[ids_trip_ped]
                ids_pers = np.unique(ids_pers_ped)
                n_pers = len(ids_pers)
            if self.id_strategy == strategies.get_id_from_formatted('transit'):
                ids_trip_bus = ids_trip[(ids_mode == modes.get_id_from_formatted('bus'))]
                n_trips_for_scale = len(ids_trip_bus)
                ids_pers_bus = trips.ids_person[ids_trip_bus]
                ids_pers = np.unique(ids_pers_bus)
                n_pers = len(ids_pers)
            if self.id_strategy == strategies.get_id_from_formatted('bike'):
                ids_trip_bike = ids_trip[(ids_mode == modes.get_id_from_formatted('bicycle'))]
                n_trips_for_scale = len(ids_trip_bike)
                ids_pers_bike = trips.ids_person[ids_trip_bike]
                ids_pers = np.unique(ids_pers_bike)
                n_pers = len(ids_pers)
                
                
        
        for step in range(int(self.scale + 1.0)):
            count = step
            difference = float(self.scale - count)

            # if the scale parameter isn't an integer number than do this
            if count == max(range(int(self.scale + 1.0))) and difference != 0.0:
                                
                if self.is_first_method == True:
                    n_pers = int(n_pers*(float(self.scale - count))) #scale parameter based on people number
                    print('new n_pers = ',n_pers)
                    
                    print('old ids_trip = ',ids_trip)
                    
                    if self.id_strategy == -1:
                        ids_trip = np.array([],dtype = np.int32)
                        if len(ids_trip_bike) > 0:
                            ids_trip_bike = np.random.choice(ids_trip_bike, n_pers, replace = False)
                            ids_trip_bike.sort()
                            ids_trip = np.append(ids_trip,ids_trip_bike)
                        if len(ids_trip_bus) > 0:
                            ids_trip_bus = np.random.choice(ids_trip_bus, n_pers, replace = False)
                            ids_trip_bus.sort()
                            ids_trip = np.append(ids_trip,ids_trip_bus)
                        if len(ids_trip_ped) > 0:
                            ids_trip_ped = np.random.choice(ids_trip_ped, n_pers, replace = False)
                            ids_trip_ped.sort()
                            ids_trip = np.append(ids_trip,ids_trip_ped)                        
                            # ids_trip = ids_trip_bike + ids_trip_ped + ids_trip_bus
                    if self.id_strategy == strategies.get_id_from_formatted('walk'):
                        ids_trip_ped = np.random.choice(ids_trip_ped, n_pers, replace = False)
                        ids_trip_ped.sort()
                        ids_trip = ids_trip_ped
                    if self.id_strategy == strategies.get_id_from_formatted('transit'):
                        ids_trip_bus = np.random.choice(ids_trip_bus, n_pers, replace = False)
                        ids_trip_bus.sort()
                        ids_trip = ids_trip_bus
                    if self.id_strategy == strategies.get_id_from_formatted('bike'):
                        ids_trip_bike = np.random.choice(ids_trip_bike, n_pers, replace = False)
                        ids_trip_bike.sort()
                        ids_trip = ids_trip_bike
                    
                    #ids_trip = np.random.choice(ids_trip, n_pers, replace = False)
                    #ids_trip.sort()
                    
                    print('new ids_trip = ',ids_trip)
                    
                    
                elif self.is_first_method == False:
                    n_trips_tab = int(n_trips_for_scale*(float(self.scale - count))) # scale parameter based on trips number
                    print('new n_pers = ',n_pers)
                    
                    print('old ids_pers = ',ids_pers)
                    
                    if self.id_strategy == -1:
                        
                        ids_trip = np.array([],dtype = np.int32)
                        ids_pers = np.array([],dtype = np.int32)
                        if len(ids_trip_bike) > 0:
                            ids_trip_bike = np.random.choice(ids_trip_bike, n_trips_tab, replace = False)
                            ids_trip_bike.sort()
                            ids_trip = np.append(ids_trip,ids_trip_bike)
                            ids_pers_bike = trips.ids_person[ids_trip_bike]
                            ids_pers_bike = np.unique(ids_pers_bike)
                            ids_pers = np.append(ids_pers,ids_pers_bike)
                        if len(ids_trip_bus) > 0:
                            ids_trip_bus = np.random.choice(ids_trip_bus, n_trips_tab, replace = False)
                            ids_trip_bus.sort()
                            ids_trip = np.append(ids_trip,ids_trip_bus)
                            ids_pers_bus = trips.ids_person[ids_trip_bus]
                            ids_pers_bus = np.unique(ids_pers_bus)
                            ids_pers = np.append(ids_pers,ids_pers_bus)
                        if len(ids_trip_ped) > 0:
                            ids_trip_ped = np.random.choice(ids_trip_ped, n_trips_tab, replace = False)
                            ids_trip_ped.sort()
                            ids_trip = np.append(ids_trip,ids_trip_ped)
                            ids_pers_ped = trips.ids_person[ids_trip_ped]
                            ids_pers_ped = np.unique(ids_pers_ped)
                            ids_pers = np.append(ids_pers,ids_pers_ped)
                        ids_pers = np.unique(ids_pers)                        
                    if self.id_strategy == strategies.get_id_from_formatted('walk'):
                        ids_trip_ped = np.random.choice(ids_trip_ped, n_trips_tab, replace = False)
                        ids_trip_ped.sort()
                        ids_pers_ped = trips.ids_person[ids_trip_ped]
                        ids_pers = np.unique(ids_pers_ped)
                    if self.id_strategy == strategies.get_id_from_formatted('transit'):
                        ids_trip_bus = np.random.choice(ids_trip_bus, n_trips_tab, replace = False)
                        ids_trip_bus.sort()
                        ids_pers_bus = trips.ids_person[ids_trip_bus]
                        ids_pers = np.unique(ids_pers_bus)
                    if self.id_strategy == strategies.get_id_from_formatted('bike'):
                        ids_trip_bike = np.random.choice(ids_trip_bike, n_trips_tab, replace = False)
                        ids_trip_bike.sort()
                        ids_pers_bike = trips.ids_person[ids_trip_bike]
                        ids_pers = np.unique(ids_pers_bike)
                    
                    n_pers = len(ids_pers)
                    
                    #ids_pers = np.random.choice(ids_pers, n_pers, replace = False)
                    #ids_pers.sort()
                    
                    print('new ids_pers = ',ids_pers)
            elif count == max(range(int(self.scale + 1.0))) and difference == 0.0:
                break
                    
            if self.is_first_method == True:
                #create virtual person
                print('create virtal person')
                ids_person = virtualpop.make_multiple(n_pers)
                
      ##        localtime = time.localtime
                       
##                unitvec_int_act = np.ones(n_pers, dtype = np.int32)  
##                ids_fac_from = np.zeros(n_pers, dtype = np.int32)
##                ids_fac_to = np.zeros(n_pers, dtype = np.int32 )
##                
##                starting_points = np.zeros((len(ids_trip)), dtype = np.int32)
##                ending_points = np.zeros((len(ids_trip)), dtype = np.int32)
##                seconds_departure = np.zeros(n_pers, dtype = np.float32)
##                seconds_arrive = np.zeros(n_pers, dtype = np.float32)
##                duration_for_scale = np.zeros(n_pers, dtype = np.float32)

                for id_trip, id_person, i  in zip(ids_trip, ids_person, list(range(len(ids_trip)))):
                    #Population attributes
                    virtualpop.ids_mode_preferred[id_person] = scenario.demand.vtypes.ids_mode[trips.ids_vtype[id_trip]]  
                    virtualpop.years_birth[id_person] = persons.years_birth[trips.ids_person[id_trip]]
                    virtualpop.ids_gender[id_person] = persons.ids_gender[trips.ids_person[id_trip]]
                    virtualpop.ids_occupation[id_person] = persons.ids_occupation[trips.ids_person[id_trip]]
                    virtualpop.identifications[id_person] = trips.ids_sumo[id_trip]
                    
##                    starting_point = ids_point[0]
##                    ending_point = ids_point[-1]
##                    starting_points[i] = ids_point[0]
##                    ending_points[i] = ids_point[-1]
##                    activitytypes = scenario.demand.activitytypes
##                    dp = time.localtime(points.timestamps[starting_point])
##                    seconds_departure[i] = dp.tm_hour*3600+dp.tm_min*60+dp.tm_sec
##                    ar = time.localtime(points.timestamps[ending_point])
##                    seconds_arrive[i] = ar.tm_hour*3600+ar.tm_min*60+ar.tm_sec
##
##                    if count == 0:
##                        min_seconds_departure = np.min(seconds_departure)
##                        max_seconds_arrive = np.max(seconds_arrive)
##                    else:
##                        duration_for_scale[i] = seconds_arrive[i] - seconds_departure[i]
##                        seconds_departure[i] = np.random.randint(min_seconds_departure,(max_seconds_arrive-duration_for_scale[i]))
##                        seconds_arrive[i] = seconds_departure[i] + duration_for_scale[i]

            elif self.is_first_method == False:
                #create virtual person
                print('create virtal person')
                ids_vppers = virtualpop.make_multiple(n_pers)
                print('ids_vppers',ids_vppers)
                pers_vppers = np.zeros(np.max(ids_pers)+1, dtype = np.int32)
        ##        pers_vppers = []
                for pers, vppers in zip(ids_pers, ids_vppers):
                    pers_vppers[pers] = vppers
                
                ids_trips = persons.ids_trips[ids_pers]
                
                for id_pers, id_vppers, ids_trip  in zip(ids_pers, ids_vppers, ids_trips):
                    #Population attributes
                    print('Add population attributes')
                    ids_mode = scenario.demand.vtypes.ids_mode[trips.ids_vtype[ids_trip]]
                    id_preferred_mode = np.argmax(np.bincount(ids_mode))
                    virtualpop.ids_mode_preferred[id_vppers] = id_preferred_mode 
                    virtualpop.years_birth[id_vppers] = persons.years_birth[id_pers]
                    virtualpop.ids_gender[id_vppers] = persons.ids_gender[id_pers]
                    virtualpop.ids_occupation[id_vppers] = persons.ids_occupation[id_pers]
                    virtualpop.identifications[id_vppers] = persons.ids_sumo[id_pers] 
                
            #Calculate trips departure and arrive times
            print('Calculate trips departure and arrive times')

            if self.id_strategy == strategies.get_id_from_formatted('bike') or self.id_strategy == -1:            
                seconds_departure_bike = np.zeros(len(ids_trip_bike), dtype = np.float32)
                seconds_arrive_bike = np.zeros(len(ids_trip_bike), dtype = np.float32)
                duration_for_scale_bike = np.zeros(len(ids_trip_bike), dtype = np.float32)
                i=0
               
                for id_trip in ids_trip_bike:
                    starting_point = trips.ids_points[id_trip][0]
                    ending_point = trips.ids_points[id_trip][-1]
                    
                    # selection of plans departure time
                    if self.is_select_time == False:
                        dp = time.localtime(points.timestamps[starting_point])
                        seconds_departure_bike[i] = dp.tm_hour*3600.+dp.tm_min*60.+dp.tm_sec
                    elif self.is_select_time == True:
                        seconds_departure_bike[i] = np.random.randint(self.from_second,self.to_second)
                    
        ##            ar = time.localtime(points.timestamps[ending_point])
                    seconds_arrive_bike[i] = seconds_departure_bike[i] + trips.durations_gps[id_trip]
                    if count == 0:
                        min_seconds_departure_bike = np.min(seconds_departure_bike)
                        max_seconds_departure_bike = np.max(seconds_departure_bike)
                    else:
                        duration_for_scale_bike[i] = seconds_arrive_bike[i] - seconds_departure_bike[i]
                        seconds_departure_bike[i] = np.random.randint(min_seconds_departure_bike,max_seconds_departure_bike)
                        seconds_arrive_bike[i] = seconds_departure_bike[i] + duration_for_scale_bike[i]

                    i+=1
                print('  min_seconds_departure_bike: ',min_seconds_departure_bike)
                print('  max_seconds_arrive_bike: ',max_seconds_departure_bike)
                print()
                print('  ids_trip_bike: ',ids_trip_bike)
                print()
                print('  seconds_departure_bike: ', seconds_departure_bike)
                print()
                

            if self.id_strategy == strategies.get_id_from_formatted('transit') or self.id_strategy == -1:   
                seconds_departure_bus = np.zeros(len(ids_trip_bus), dtype = np.float32)
                seconds_arrive_bus = np.zeros(len(ids_trip_bus), dtype = np.float32)
                duration_for_scale_bus = np.zeros(len(ids_trip_bus), dtype = np.float32)
                i=0

                for id_trip in ids_trip_bus:
                    starting_point = trips.ids_points[id_trip][0]
                    ending_point = trips.ids_points[id_trip][-1]
                    
                    # selection of plans departure time
                    if self.is_select_time == False:
                        dp = time.localtime(points.timestamps[starting_point])
                        seconds_departure_bus[i] = dp.tm_hour*3600.+dp.tm_min*60.+dp.tm_sec
                    elif self.is_select_time == True:
                        seconds_departure_bus[i] = np.random.randint(self.from_second,self.to_second)
                        
                    
        ##            ar = time.localtime(points.timestamps[ending_point])
                    seconds_arrive_bus[i] = seconds_departure_bus[i] + trips.durations_gps[id_trip]

                    if count == 0:
                        min_seconds_departure_bus = np.min(seconds_departure_bus)
                        max_seconds_arrive_bus = np.max(seconds_arrive_bus)
                    else:
                        duration_for_scale_bus[i] = seconds_arrive_bus[i] - seconds_departure_bus[i]
                        seconds_departure_bus[i] = np.random.randint(min_seconds_departure_bus,(max_seconds_arrive_bus-duration_for_scale_bus[i]))
                        seconds_arrive_bus[i] = seconds_departure_bus[i] + duration_for_scale_bus[i]                  

                    i+=1

                print(seconds_departure_bus, seconds_arrive_bus)
            
            if self.id_strategy == strategies.get_id_from_formatted('walk') or self.id_strategy == -1:   
                seconds_departure_ped = np.zeros(len(ids_trip_ped), dtype = np.float32)
                seconds_arrive_ped = np.zeros(len(ids_trip_ped), dtype = np.float32)
                duration_for_scale_ped = np.zeros(len(ids_trip_ped), dtype = np.float32)
                i=0

                for id_trip in ids_trip_ped:
                    starting_point = trips.ids_points[id_trip][0]
                    ending_point = trips.ids_points[id_trip][-1]
                    
                    # selection of plans departure time
                    if self.is_select_time == False:
                        dp = time.localtime(points.timestamps[starting_point])
                        seconds_departure_ped[i] = dp.tm_hour*3600.+dp.tm_min*60.+dp.tm_sec
                    elif self.is_select_time == True:
                        seconds_departure_ped[i] = np.random.randint(self.from_second,self.to_second)
                    
        ##            ar = time.localtime(points.timestamps[ending_point])
                    seconds_arrive_ped[i] = seconds_departure_ped[i] + trips.durations_gps[id_trip]

                    if count == 0:
                        min_seconds_departure_ped = np.min(seconds_departure_ped)
                        max_seconds_arrive_ped = np.max(seconds_arrive_ped)
                    else:
                        duration_for_scale_ped[i] = seconds_arrive_ped[i] - seconds_departure_ped[i]
                        seconds_departure_ped[i] = np.random.randint(min_seconds_departure_ped,(max_seconds_arrive_ped-duration_for_scale_ped[i]))
                        seconds_arrive_ped[i] = seconds_departure_ped[i] + duration_for_scale_ped[i]
                        
                    i+=1


                print(seconds_departure_ped, seconds_arrive_ped)


            # call Vehicle provider to give vehicles to persons
            # the vehicle according to their preferred mode
            VehicleProvider(\
                        virtualpop = virtualpop,
                        share_autoowner = 0.0,
                        share_motorcycleowner = 0.0,
                        share_bikeowner = 0.0,
                        logger = logger,
                        ).do()
            #Update bike vtypes

            if self.is_update_bike_vtypes:
                for id_ibike in virtualpop.get_ibikes().get_ids():
                    id_person = virtualpop.get_ibikes().ids_person[id_ibike]
                    age_person = self.year_database - virtualpop.years_birth[id_person]
                    gender_person = virtualpop.ids_gender[id_person]
                    if gender_person == 0 and age_person <= self.age_treshold:
                        virtualpop.get_ibikes().ids_vtype[id_ibike] = self.male_young
                    if gender_person == 0 and age_person > self.age_treshold:
                        virtualpop.get_ibikes().ids_vtype[id_ibike] = self.male_old
                    if gender_person == 1 and age_person <= self.age_treshold:
                        virtualpop.get_ibikes().ids_vtype[id_ibike] = self.female_young
                    if gender_person == 1 and age_person > self.age_treshold:
                        virtualpop.get_ibikes().ids_vtype[id_ibike] = self.female_old
                    
    
            #Identify backward and forward edges
            bstar = net.edges.get_bstar()
            fstar = net.edges.get_fstar()
            
            #Preapare parameters for stages
            print('Preapare parameters for stages')
            
            #bus
            print('Preapare parameters for BUS plans')
            if self.id_strategy == strategies.get_id_from_formatted('transit') or self.id_strategy == -1:
                times_from_bus = seconds_departure_bus
                durations_approx_bus = seconds_arrive_bus - seconds_departure_bus 
            ##            ids_vehicle_bike = virtualpop.ids_ibike[ids_person]
                ids_edge_from_busstop = np.zeros(len(ids_trip_bus), dtype = np.int32)
                ids_edge_to_busstop = np.zeros(len(ids_trip_bus), dtype = np.int32)
                positions_edge_from_busstop = np.zeros(len(ids_trip_bus), dtype = np.int32)
                positions_edge_to_busstop = np.zeros(len(ids_trip_bus), dtype = np.int32)
                ids_route = trips.ids_route_matched[ids_trip_bus]
                ids_fac_from_bus = np.zeros(len(ids_trip_bus), dtype = np.int32)
                ids_fac_to_bus = np.zeros(len(ids_trip_bus), dtype = np.int32)
                routes_bus = trips.get_routes().ids_edges[ids_route]
                i=0

                for id_trip  in ids_trip_bus:
                    ##                ids_edge_from_bus[i] = np.array(trips.get_routes().ids_edges[trips.ids_route_matched[id_trip]])[0]
                    ##                ids_edge_to_bus[i] = np.array(trips.get_routes().ids_edges[trips.ids_route_matched[id_trip]])[-1]
                    id_ptlink_from = trips.get_routes().ids_ptlinks[trips.ids_route_matched[id_trip]][0]
                    id_ptlink_to = trips.get_routes().ids_ptlinks[trips.ids_route_matched[id_trip]][-1]
            
                    id_fromstop = scenario.demand.ptlines.get_ptlinks().ids_fromstop[id_ptlink_from]
                    id_tostop = scenario.demand.ptlines.get_ptlinks().ids_tostop[id_ptlink_to]
                    ids_edge_from_busstop[i] = net.lanes.ids_edge[net.ptstops.ids_lane[id_fromstop]] 
                    ids_edge_to_busstop[i] = net.lanes.ids_edge[net.ptstops.ids_lane[id_tostop]] 
                    positions_edge_from_busstop[i]= (scenario.net.ptstops.positions_from[id_fromstop]+scenario.net.ptstops.positions_to[id_fromstop])/2.
                    positions_edge_to_busstop[i]=  (scenario.net.ptstops.positions_from[id_tostop]+scenario.net.ptstops.positions_to[id_tostop])/2.
                    #Find facilities
                    #print 'Find facilities for BUS trips'
                    coords_fac_centroids = centroids[ids_facilities]
                    coords_node_origin = points.coords[trips.ids_points[id_trip][0]]
                    coords_node_destination = points.coords[trips.ids_points[id_trip][-1]]
                    diff_origin = coords_fac_centroids-coords_node_origin
                    diff_dest = coords_fac_centroids-coords_node_destination
                    distances_from = np.abs((np.sqrt(diff_origin[:,0]**2 + diff_origin[:,1]**2.) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distances_to = np.abs((np.sqrt(diff_dest[:,0]**2 + diff_dest[:,1]**2.) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distance_fac_list_from = np.column_stack((distances_from,ids_facilities))
                    distance_fac_list_to = np.column_stack((distances_to,ids_facilities))
                    distance_fac_list_from = distance_fac_list_from[distance_fac_list_from[:,0].argsort()]
                    distance_fac_list_to = distance_fac_list_to[distance_fac_list_to[:,0].argsort()]
                    ids_fac_from_bus[i] = int(distance_fac_list_from[0,1])
                    ids_fac_to_bus[i] = int(distance_fac_list_to[0,1])
                    i+=1
        
                    
        ##        ids_route = trips.ids_route_matched[ids_trip]
                #Identify closest edge for facilities
                print('Identify closest edge for facilities from')
                facilities.identify_closest_edge(ids = ids_fac_from_bus, priority_max = 7, has_sidewalk = True)
                print('Identify closest edge for facilities to')
                facilities.identify_closest_edge(ids = ids_fac_to_bus, priority_max = 7, has_sidewalk = True)
            
            #bike
            print('Preapare parameters for BIKE plans')
            
            if self.id_strategy == strategies.get_id_from_formatted('bike') or self.id_strategy == -1:
                times_from_bike = seconds_departure_bike
                durations_approx_bike = seconds_arrive_bike - seconds_departure_bike 

                if self.is_first_method == True:
                    ids_vehicle_bike = virtualpop.ids_ibike[ids_person]
                if self.is_first_method == False:
                    ids_vehicle_bike = virtualpop.ids_ibike[pers_vppers[trips.ids_person[ids_trip_bike]]]
                ids_edge_from_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_edge_to_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                positions_edge_from_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                positions_edge_to_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_edge_from_bike1 = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_edge_to_bike1 = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_fac_from_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_fac_to_bike = np.zeros(len(ids_trip_bike), dtype = np.int32)
                ids_only_bike_trip = []
                route_from = [0]*len(ids_trip_bike)
                route_to = [0]*len(ids_trip_bike)
                ids_route = trips.ids_route_matched[ids_trip_bike]
                routes_bike = trips.get_routes().ids_edges[ids_route]
                i=0
                bstar = net.edges.get_bstar(id_mode = modes.get_id_from_formatted('bicycle'))
                fstar = net.edges.get_fstar(id_mode = modes.get_id_from_formatted('bicycle'))
                for id_trip  in ids_trip_bike:
                    ids_edge_from_bike1[i] = np.array(trips.get_routes().ids_edges[trips.ids_route_matched[id_trip]])[0]
                    ids_edge_to_bike1[i] = np.array(trips.get_routes().ids_edges[trips.ids_route_matched[id_trip]])[-1]
                    
                    ids_edge_from_bike[i], positions_edge_from_bike[i], route_from[i] = virtualpop.get_strategies().strategies[strategies.get_id_from_formatted('bike')].get_edge_bikeaccess(ids_edge_from_bike1[i], is_get_route = True, is_search_backward = True, is_star = True, bstar = bstar)
                    ids_edge_to_bike[i],  positions_edge_to_bike[i], route_to[i] = virtualpop.get_strategies().strategies[strategies.get_id_from_formatted('bike')].get_edge_bikeaccess(ids_edge_to_bike1[i],is_get_route = True,  is_search_backward = False, is_star = True, fstar = fstar)
                    routes_bike[i] = route_from[i] + routes_bike.tolist()[i] + route_to[i]

                    if ids_edge_from_bike[i] == -1 or ids_edge_to_bike[i] == -1:
                        if self.is_first_method == True:
                            ids_only_bike_trip.append(id_person)
                        if self.is_first_method == False:
                            ids_only_bike_trip.append(id_trip)
                        ids_edge_from_bike[i] = ids_edge_from_bike1[i]
                        ids_edge_to_bike[i] = ids_edge_to_bike1[i]
                    #Find facilities
                    #print 'Find facilities for BIKE trips'
                    coords_fac_centroids = centroids[ids_facilities]
                    coords_node_origin = net.nodes.coords[net.edges.ids_fromnode[routes_bike[i][0]]]
                    coords_node_destination = net.nodes.coords[net.edges.ids_fromnode[routes_bike[i][-1]]]
                    diff_origin = coords_fac_centroids-coords_node_origin
                    diff_dest = coords_fac_centroids-coords_node_destination
                    distances_from = np.abs((np.sqrt(diff_origin[:,0]**2 + diff_origin[:,1]**2) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distances_to = np.abs((np.sqrt(diff_dest[:,0]**2 + diff_dest[:,1]**2) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distance_fac_list_from = np.column_stack((distances_from,ids_facilities))
                    distance_fac_list_to = np.column_stack((distances_to,ids_facilities))
                    distance_fac_list_from = distance_fac_list_from[distance_fac_list_from[:,0].argsort()]
                    distance_fac_list_to = distance_fac_list_to[distance_fac_list_to[:,0].argsort()]
                    ids_fac_from_bike[i] = int(distance_fac_list_from[0,1])
                    ids_fac_to_bike[i] = int(distance_fac_list_to[0,1])
                    i+=1

                print('ids_only_bike_trip', ids_only_bike_trip)
                ##        ids_route = trips.ids_route_matched[ids_trip]
                #Identify closest edge for facilities
                print('Identify closest edge for facilities from')
                facilities.identify_closest_edge(ids = ids_fac_from_bike, priority_max = 7, has_sidewalk = True)
                print('Identify closest edge for facilities to')
                facilities.identify_closest_edge(ids = ids_fac_to_bike, priority_max = 7, has_sidewalk = True)
    
            #pedestrian
            print('Preapare parameters for PEDESTRIAN plans')
            
            if self.id_strategy == strategies.get_id_from_formatted('walk') or self.id_strategy == -1:
                times_from_ped = seconds_departure_ped
                durations_approx_ped = seconds_arrive_ped - seconds_departure_ped 
            ##            ids_vehicle_bike = virtualpop.ids_ibike[ids_person]
                ids_edge_from_ped = np.zeros(len(ids_trip_ped), dtype = np.int32)
                ids_edge_to_ped = np.zeros(len(ids_trip_ped), dtype = np.int32)
                ids_fac_from_ped = np.zeros(len(ids_trip_ped), dtype = np.int32)
                ids_fac_to_ped = np.zeros(len(ids_trip_ped), dtype = np.int32)
                i=0
                for id_trip  in ids_trip_ped:
                    #Find facilities
                    #print 'Find facilities for PEDESTRIAN trips'
                    coords_fac_centroids = centroids[ids_facilities]
                    coords_node_origin = points.coords[trips.ids_points[id_trip][0]]
                    coords_node_destination = points.coords[trips.ids_points[id_trip][-1]]
                    diff_origin = coords_fac_centroids-coords_node_origin
                    diff_dest = coords_fac_centroids-coords_node_destination
                    distances_from = np.abs((np.sqrt(diff_origin[:,0]**2 + diff_origin[:,1]**2) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distances_to = np.abs((np.sqrt(diff_dest[:,0]**2 + diff_dest[:,1]**2) - np.random.normal(self.mean_radius, self.dev_radius)))
                    distance_fac_list_from = np.column_stack((distances_from,ids_facilities))
                    distance_fac_list_to = np.column_stack((distances_to,ids_facilities))
                    distance_fac_list_from = distance_fac_list_from[distance_fac_list_from[:,0].argsort()]
                    distance_fac_list_to = distance_fac_list_to[distance_fac_list_to[:,0].argsort()]
                    ids_fac_from_ped[i] = int(distance_fac_list_from[0,1])
                    ids_fac_to_ped[i] = int(distance_fac_list_to[0,1])
    
                    i+=1
                    
###########################################      
            available_strategies = []
            if len(ids_trip_ped)>0:
                available_strategies.append(strategies.get_id_from_formatted('walk'))
            if len(ids_trip_bike)>0:
                available_strategies.append(strategies.get_id_from_formatted('bike'))
            if len(ids_trip_bus)>0:
                available_strategies.append(strategies.get_id_from_formatted('transit'))  
                
            for strategy in available_strategies:
                print('init strategy', strategy)
    
                
                if strategy == strategies.get_id_from_formatted('bike'):
                    ids_fac_from = ids_fac_from_bike
                    ids_fac_to = ids_fac_to_bike
                    seconds_departure = seconds_departure_bike
                    seconds_arrive = seconds_arrive_bike
                    ids_trip = ids_trip_bike
                if strategy == strategies.get_id_from_formatted('transit'):
                    ids_fac_from = ids_fac_from_bus
                    ids_fac_to = ids_fac_to_bus
                    seconds_departure = seconds_departure_bus
                    seconds_arrive = seconds_arrive_bus
                    ids_trip = ids_trip_bus
                if strategy == strategies.get_id_from_formatted('walk'):
                    ids_fac_from = ids_fac_from_ped
                    ids_fac_to = ids_fac_to_ped
                    seconds_departure = seconds_departure_ped
                    seconds_arrive = seconds_arrive_ped
                    ids_trip = ids_trip_ped

                if self.is_first_method == True:
                    n_trips = n_pers    
                if self.is_first_method == False:
                    ids_person = pers_vppers[trips.ids_person[ids_trip]]
                    n_trips = len(ids_person)
                                    
                
                #walk1
                print('Prepare parameters for Walk1')
    
                ids_edge_from_walk1 =  facilities.ids_roadedge_closest[ids_fac_from]
                positions_edge_from_walk1 = facilities.positions_roadedge_closest[ids_fac_from]
                times_from_walk1 = seconds_departure
                if strategy == strategies.get_id_from_formatted('bike'):
                    positions_edge_to_walk1 = positions_edge_from_bike
                    ids_edge_to_walk1 = ids_edge_from_bike
                if strategy == strategies.get_id_from_formatted('transit'):
                    positions_edge_to_walk1 = positions_edge_from_busstop
                    ids_edge_to_walk1 = ids_edge_from_busstop
                
                
                    
                #for pedestrian
                if strategy == strategies.get_id_from_formatted('walk'):
                    positions_edge_to_walk1 = facilities.positions_roadedge_closest[ids_fac_to]
                    ids_edge_to_walk1 = facilities.ids_roadedge_closest[ids_fac_to]
                if strategy == strategies.get_id_from_formatted('transit') or strategy == strategies.get_id_from_formatted('bike'):
                    #walk2
                    print('Prepare parameters for Walk2')
        
                    times_from_walk2 = seconds_arrive +1.-1.
            
                    ids_edge_to_walk2 = facilities.ids_roadedge_closest[ids_fac_to]
                    positions_edge_to_walk2 = facilities.positions_roadedge_closest[ids_fac_to]
                    if strategy == strategies.get_id_from_formatted('bike'):
                        ids_edge_from_walk2 = ids_edge_to_bike
                        positions_edge_from_walk2 = positions_edge_to_bike
                    if strategy == strategies.get_id_from_formatted('transit'):
                        ids_edge_from_walk2 = ids_edge_to_busstop
                        positions_edge_from_walk2 = positions_edge_to_busstop
                
                print(seconds_arrive)  
                #act1 
                print('Prepare parameters for Act1')
                
                unitvec_int_act = np.ones(n_trips)
                ids_edge_from_act1 = facilities.ids_roadedge_closest[ids_fac_from]
                positions_edge_from_act1 = facilities.positions_roadedge_closest[ids_fac_from]
                names_acttype_from = self.ids_activitytype_orig * unitvec_int_act
                durations_act_from = np.ones(n_trips)*3600.0
                times_from_act1 = seconds_departure-3600.0
             
                #act2
                print('Prepare parameters for Act2')
                
                ids_edge_to_act2 = facilities.ids_roadedge_closest[ids_fac_to]
                positions_edge_to = facilities.positions_roadedge_closest[ids_fac_to]                  
                times_from_act2 = seconds_arrive +1.0-1.0
                names_acttype_to  = self.ids_activitytype_dest*unitvec_int_act
                durations_act_to = np.ones(n_trips)*3600.0               
                        
            #Add activities 
            print('Add activities ')
    ##        ids_fac_from = ids_facilities[ids_fac_from]
    ##        ids_fac_to = ids_facilities[ids_fac_to]
            
            ids_activity_from = activities.add_activities(\
                                    self.ids_activitytype_orig*unitvec_int_act,
                                    ids_fac_from,
                                    hours_begin_earliest = (seconds_departure-3600.0)/3600.0,
                                    hours_begin_latest =   (seconds_departure-3600.0+10.0)/3600.0,
                                    durations_min = np.ones(n_trips),
                                    durations_max = np.ones(n_trips)+0.1,
                                    )
    
            ids_activity_to = activities.add_activities(\
                                    self.ids_activitytype_dest*unitvec_int_act,
                                    ids_fac_to,
                                    hours_begin_earliest = seconds_arrive/3600.0,
                                    hours_begin_latest = (seconds_arrive+10.0)/3600.0,
                                    durations_min = np.ones(n_trips),
                                    durations_max = np.ones(n_trips)+0.1,
                                    )

            if self.is_first_method == True:
                for id_person,id_activity_from,id_activity_to in zip(ids_person, ids_activity_from, ids_activity_to):
                    virtualpop.activitypatterns[id_person] = [id_activity_from,id_activity_to, ]

                #Create plans
                print('Create plans')
                ids_plan = virtualpop.add_plans(ids_person, strategy)

            # QUESTO METODO POTREBBE ESSERE VALIDO ANCHE PER VPCREATOR1???
            elif self.is_first_method == False:
                for id_person,id_activity_from,id_activity_to in zip(ids_person,ids_activity_from,ids_activity_to):
                    if virtualpop.activitypatterns[id_person] is not None:
                              patterns = virtualpop.activitypatterns[id_person]
                              print('patterns', patterns)
                              patterns.append(id_activity_from)
                              print('patterns', patterns)
                              patterns.append(id_activity_to)
                              print('patterns', patterns)
                              virtualpop.activitypatterns[id_person] = patterns
                              print('patterns', patterns)
                    else:
                              virtualpop.activitypatterns[id_person] = [id_activity_from, id_activity_to]

                #Create plans
                print('Create plans')
                if strategy == strategies.get_id_from_formatted('transit') or strategy ==strategies.get_id_from_formatted('bike'):
                    ids_plan = virtualpop.add_plans(ids_person, strategy)
                elif strategy == strategies.get_id_from_formatted('walk'):
                    ids_plan = virtualpop.add_plans(ids_person, 5)

            strat_for_stages = strategy


            
            #Add activity stages
            print('Add activity stages')
        
            for id_plan,\
                id_act_from,\
                name_acttype_from,\
                duration_act_from,\
                id_edge_from,\
                pos_edge_from,\
                time_from in\
                 zip(ids_plan,\
                     ids_activity_from,\
                     names_acttype_from,\
                     durations_act_from,\
                     ids_edge_from_act1,\
                     positions_edge_from_act1,\
                     times_from_act1):

                virtualpop.get_plans().get_stagetable('activities').append_stage(\
                                                        id_plan, time_from,
                                                        ids_activity = id_act_from,
                                                        names_activitytype =  name_acttype_from,
                                                        durations = duration_act_from,
                                                        ids_lane =  scenario.net.edges.ids_lanes[id_edge_from][0],
                                                        positions = pos_edge_from,
                                                        )       
               
            #Add walk stages
            print('Add walk stages')
            ids_walk1 = np.zeros(len(ids_trip), dtype = np.int32)
            times_end_walk1 = np.zeros(len(ids_trip), dtype = np.int32)

            if self.is_first_method == True:
                for id_plan,\
                time_from,\
                id_edge_from,\
                position_edge_from,\
                id_edge_to,\
                position_edge_to,\
                id_pers,\
                i in\
                     zip(ids_plan,\
                         times_from_walk1,\
                         ids_edge_from_walk1,\
                         positions_edge_from_walk1,\
                         ids_edge_to_walk1,\
                         positions_edge_to_walk1,\
                         ids_person,\
                         list(range(len(ids_trip)))): 
                    try:
                        ids_only_bike_trip.index(id_pers) 
                    except:
                        print('plan first walk')
                        ids_walk1[i] , times_end_walk1[i] = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                    id_plan, time_from, 
                                                    id_edge_from = id_edge_from, 
                                                    position_edge_from = position_edge_from, 
                                                    id_edge_to = id_edge_to, 
                                                    position_edge_to = position_edge_to,
                                                    )
                                                    
                durations_walk1 = np.zeros(len(ids_trip), dtype = np.int32)
                for time_end,  second_departure, i in zip(times_end_walk1, seconds_departure, list(range(len(ids_trip)))) :
                    if time_end !=0:
                        durations_walk1[i] = virtualpop.get_plans().get_stagetable('walks').durations[ids_walk1[i]]
                #bike
                times_from_bike = seconds_departure + durations_walk1
                #bus
                times_from_bus = seconds_departure + durations_walk1

            elif self.is_first_method == False:
                for id_plan,\
                time_from,\
                id_edge_from,\
                position_edge_from,\
                id_edge_to,\
                position_edge_to,\
                id_pers,\
                id_trip,\
                i in\
                     zip(ids_plan,\
                         times_from_walk1,\
                         ids_edge_from_walk1,\
                         positions_edge_from_walk1,\
                         ids_edge_to_walk1,\
                         positions_edge_to_walk1,\
                         ids_person,\
                         ids_trip,\
                         list(range(len(ids_trip)))): 
                    try:
                        ids_only_bike_trip.index(id_trip) 
                    except:
                        print('plan first walk')
                        ids_walk1[i] , times_end_walk1[i] = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                    id_plan, time_from, 
                                                    id_edge_from = id_edge_from, 
                                                    position_edge_from = position_edge_from, 
                                                    id_edge_to = id_edge_to, 
                                                    position_edge_to = position_edge_to,
                                                    )
                                                    
                durations_walk1 = np.zeros(len(ids_trip), dtype = np.float32)
                for time_end,  second_departure, i in zip(times_end_walk1, seconds_departure, list(range(len(ids_trip)))) :
                    if time_end !=0:
                        durations_walk1[i] = virtualpop.get_plans().get_stagetable('walks').durations[ids_walk1[i]]
                print(seconds_departure, durations_walk1)
                
                if strategy ==strategies.get_id_from_formatted('bike'):
                    #bike
                    times_from_bike = seconds_departure + durations_walk1
                if strategy ==strategies.get_id_from_formatted('transit'):
                    #bus
                    times_from_bus = seconds_departure + durations_walk1

            if strat_for_stages == strategies.get_id_from_formatted('bike'):
                #Add bikeride stages
                print('Add bikeride stages')
                ##        virtualpop.get_plans().get_stagetable('bikeride').prepare_planning()
                ##        duration_approx1, route1 = routing.get_mincostroute_edge2edge(
                ##                                                    ids_edge_from_bike,
                ##                                                    ids_edge_from_bike1,
                ##                                                    )
                ##        duration_approx2, route2 = routing.get_mincostroute_edge2edge(
                ##                                                    ids_edge_to_bike1,
                ##                                                    ids_edge_to_bike,
                ##                                                    )
                for id_plan,\
                    time_from,\
                    id_veh,\
                    id_edge_from,\
                    position_edge_from,\
                    id_edge_to,\
                    position_edge_to,\
                    route,\
                    duration_approx in\
                     zip(ids_plan,\
                         times_from_bike,\
                         ids_vehicle_bike,\
                         ids_edge_from_bike,\
                         positions_edge_from_bike,\
                         ids_edge_to_bike,\
                         positions_edge_to_bike,\
                         routes_bike,\
                         durations_approx_bike ): 
                    virtualpop.get_plans().get_stagetable('bikerides').append_stage(\
                                id_plan, 
                                time_start=time_from, 
                                id_veh = id_veh, 
                                time_init = time_from-10,
                                id_edge_from = id_edge_from, 
                                id_edge_to = id_edge_to, 
                                position_edge_from = position_edge_from, 
                                position_edge_to = position_edge_to,
                                is_route = False, 
                                route = route, 
                                duration_approx = duration_approx)

            if strat_for_stages == strategies.get_id_from_formatted('transit'):

                #Add bus stages
                print('Add bus stages')
                ids_route = trips.ids_route_matched[ids_trip]
                ids_ptlinks = trips.get_routes().ids_ptlinks[ids_route]
                for id_route, id_plan, time_from_bus, i in zip(ids_route, ids_plan,times_from_bus, list(range(len(ids_trip)))):
                    ids_ptlinks = trips.get_routes().ids_ptlinks[id_route]
                    types_ptlinks = scenario.demand.ptlines.get_ptlinks().types[ids_ptlinks]
                    board = 0
                    for type, id_ptlink, j in zip(types_ptlinks, ids_ptlinks, list(range(len(ids_ptlinks)))):
                        #board
                        if type == 3:
                            if board ==1:
                                id_fromstop = scenario.demand.ptlines.get_ptlinks().ids_fromstop[id_ptlink]
                                id_tostop = id_fromstop
                                id_edge_from = scenario.net.lanes.ids_edge[scenario.net.ptstops.ids_lane[id_fromstop]]
                                position_edge_from = (scenario.net.ptstops.positions_from[id_fromstop]+scenario.net.ptstops.positions_to[id_fromstop])/2
                                id_edge_to = id_edge_from
                                position_edge_to = position_edge_from
                                time_from = time_from_bus
                                ids_walk , end_time = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                                        id_plan, time_from, 
                                                                        id_edge_from = id_edge_from, 
                                                                        position_edge_from = position_edge_from, 
                                                                        id_edge_to = id_edge_to, 
                                                                        position_edge_to = position_edge_to,
                                                                        )
                                time_from_bus  += (end_time-time_from)
                                times_from_bus[i] = time_from_bus
                            id_line = scenario.demand.ptlines.get_ptlinks().ids_line[id_ptlink]
                            duration = scenario.demand.ptlines.get_ptlinks().durations[id_ptlink]
                            id_fromstop = scenario.demand.ptlines.get_ptlinks().ids_fromstop[id_ptlink]
                            numb_links = j
                            print(i, scenario.demand.ptlines.get_ptlinks().types[ids_ptlinks[numb_links+1]])
                            is_ok = True
                            if numb_links+2<len(ids_ptlinks):
                                while (scenario.demand.ptlines.get_ptlinks().types[ids_ptlinks[numb_links+1]] == 2)and(is_ok == True):
                                    numb_links+=1
                                    #da togliere...se ci fossero le ultime 2 fasi del pt stage: alight and stage
                                    if numb_links+2<len(ids_ptlinks):
                                        is_ok = True
                                    else:                                        
                                        is_ok = False 
                            numb_links+=1
    ##                        id_ptlink_board = id_ptlink
    ##                        is_ok = True
    ##                        print id_line, id_ptlink, scenario.demand.ptlines.get_ptlinks().types[id_ptlink_board+1], scenario.demand.ptlines.get_ptlinks().ids_line[id_ptlink_board+1]
    ##                        while (scenario.demand.ptlines.get_ptlinks().types[id_ptlink_board+1] == 2) and (scenario.demand.ptlines.get_ptlinks().ids_line[id_ptlink_board+1] == id_line) and (is_ok == True):
    ##                            if id_ptlink_board+1 in ids_ptlinks:
    ##                                id_ptlink_board +=1
    ##                                print id_ptlink_board
    ##                            else:
    ##                                is_ok = no
                            id_tostop = scenario.demand.ptlines.get_ptlinks().ids_tostop[ids_ptlinks[numb_links]]
    
                            id_edge_from = scenario.net.lanes.ids_edge[scenario.net.ptstops.ids_lane[id_fromstop]]
                            id_edge_to = scenario.net.lanes.ids_edge[scenario.net.ptstops.ids_lane[id_tostop]]
                            time_from = time_from_bus
                            id_stage_transit, end_time = virtualpop.get_plans().get_stagetable('transits').append_stage(
                                id_plan, time_from,
                                id_line = id_line,
                                duration = duration,
                                id_fromedge = id_edge_from,
                                id_toedge = id_edge_to, 
                                ) 
                            print('2',times_from_bus, end_time ,  time_from)    
                            time_from_bus  += (end_time-time_from)
                            times_from_bus[i] = time_from_bus
                            board = 1
                        #walk   
                        elif type == 6:
                            id_fromstop = scenario.demand.ptlines.get_ptlinks().ids_fromstop[id_ptlink]
                            id_tostop = scenario.demand.ptlines.get_ptlinks().ids_tostop[id_ptlink]
                            id_edge_from = scenario.net.lanes.ids_edge[scenario.net.ptstops.ids_lane[id_fromstop]]
                            position_edge_from = (scenario.net.ptstops.positions_from[id_fromstop]+scenario.net.ptstops.positions_to[id_fromstop])/2
                            id_edge_to = scenario.net.lanes.ids_edge[scenario.net.ptstops.ids_lane[id_tostop]]
                            position_edge_to =(scenario.net.ptstops.positions_from[id_tostop]+scenario.net.ptstops.positions_to[id_tostop])/2
                            time_from = time_from_bus
                            ids_walk , end_time = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                                    id_plan, time_from, 
                                                                    id_edge_from = id_edge_from, 
                                                                    position_edge_from = position_edge_from, 
                                                                    id_edge_to = id_edge_to, 
                                                                    position_edge_to = position_edge_to,
                                                                    )
                            print('3',times_from_bus, end_time ,  time_from)                                     
                            time_from_bus  += (end_time-time_from)
                            times_from_bus[i] = time_from_bus
                            board = 0
    
                    
            #Add walk stages
            print('Add walk stages')
            
            if self.is_first_method == True:
                ids_walk2 = np.zeros(len(ids_trip), dtype = np.int32)
                times_end_walk2 = np.zeros(len(ids_trip), dtype = np.int32)
                if self.id_strategy ==strategies.get_id_from_formatted('walk'):
                    times_from_walk2 += durations_walk1
                if self.id_strategy ==strategies.get_id_from_formatted('transit'):
                    times_from_walk2 = times_from_bus
                    
                for id_plan,\
                    time_from,\
                    id_edge_from,\
                    position_edge_from,\
                    id_edge_to,\
                    position_edge_to,\
                    i,\
                    id_pers in\
                     zip(ids_plan,\
                         times_from_walk2,\
                         ids_edge_from_walk2,\
                         positions_edge_from_walk2,\
                         ids_edge_to_walk2,\
                         positions_edge_to_walk2,\
                         list(range(len(ids_trip))),\
                         ids_person ): 
                    try:
                        ids_only_bike_trip.index(id_pers) 
                    except:
                        ids_walk2[i] , times_end_walk2[i] = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                    id_plan, time_from, 
                                                    id_edge_from = id_edge_from, 
                                                    position_edge_from = position_edge_from, 
                                                    id_edge_to = id_edge_to, 
                                                    position_edge_to = position_edge_to,
                                                    )
            elif self.is_first_method == False:
                if strategy == strategies.get_id_from_formatted('transit') or strategy == strategies.get_id_from_formatted('bike'):
                    ids_walk2 = np.zeros(len(ids_trip), dtype = np.int32)
                    times_end_walk2 = np.zeros(len(ids_trip), dtype = np.int32)
                    if strategy ==strategies.get_id_from_formatted('bike'):
                        times_from_walk2 += durations_walk1 
                    if strategy ==strategies.get_id_from_formatted('transit'):
                        times_from_walk2 = times_from_bus
                        
                    for id_plan,\
                        time_from,\
                        id_edge_from,\
                        position_edge_from,\
                        id_edge_to,\
                        position_edge_to,\
                        id_trip,\
                        i,\
                        id_pers in\
                         zip(ids_plan,\
                             times_from_walk2,\
                             ids_edge_from_walk2,\
                             positions_edge_from_walk2,\
                             ids_edge_to_walk2,\
                             positions_edge_to_walk2,\
                             ids_trip,\
                             list(range(len(ids_trip))),\
                             ids_person ): 
                        try:
                            ids_only_bike_trip.index(id_trip) 
                        except:
                            ids_walk2[i] , times_end_walk2[i] = virtualpop.get_plans().get_stagetable('walks').append_stage(\
                                                        id_plan, time_from, 
                                                        id_edge_from = id_edge_from, 
                                                        position_edge_from = position_edge_from, 
                                                        id_edge_to = id_edge_to, 
                                                        position_edge_to = position_edge_to,
                                                        )

            durations_walk2 = np.zeros(len(ids_trip), dtype = np.float32)
            for time_end,  second_arrive, i, duration_walk_1 in zip(times_end_walk2, seconds_arrive, list(range(len(ids_trip))),durations_walk1):
                if strat_for_stages ==strategies.get_id_from_formatted('bike'):
                    if time_end !=0:
                        durations_walk2[i] = virtualpop.get_plans().get_stagetable('walks').durations[ids_walk2[i]]
                    times_from_act2+= (durations_walk2+durations_walk1)
                if strat_for_stages ==strategies.get_id_from_formatted('transit'):
                    durations_walk2 = times_end_walk2-times_from_bus
                    times_from_act2 = times_from_bus + durations_walk2+ durations_walk1
            print('durations_walk1',durations_walk1,'durations_walk2',durations_walk2, 'seconds_arrive', seconds_arrive, 'seconds_departure', seconds_departure)
            #Add activity stages
            print('Add activity stages')
    
            for id_plan,\
                id_act_to,\
                name_acttype_to,\
                duration_act_to,\
                id_edge_to,\
                pos_edge_to,\
                time_from,\
                i in\
                 zip(ids_plan,\
                     ids_activity_to,\
                     names_acttype_to,\
                     durations_act_to,\
                     ids_edge_to_act2,\
                     positions_edge_to,\
                     times_from_act2,\
                     list(range(len(ids_trip)))): 
                virtualpop.get_plans().get_stagetable('activities').append_stage(\
                                                        id_plan, time_from,
                                                        ids_activity = id_act_to,
                                                        names_activitytype =  name_acttype_to,
                                                        durations = duration_act_to,
                                                        ids_lane =  scenario.net.edges.ids_lanes[id_edge_to][0],
                                                        positions = pos_edge_to,
                                                        )
            #Add plan times
            if self.is_first_method == True:
                plans = virtualpop.get_plans()
                plans.times_begin[ids_plan] = seconds_departure
                plans.times_end[ids_plan] = seconds_arrive + durations_walk2 + durations_walk1
                plans.times_est[ids_plan] = seconds_arrive + durations_walk2 + durations_walk1 - seconds_departure
            elif self.is_first_method == False:
                if strategy ==strategies.get_id_from_formatted('transit') or strategy ==  strategies.get_id_from_formatted('bike'):
                    plans = virtualpop.get_plans()
                    plans.times_begin[ids_plan] = seconds_departure 
                    plans.times_end[ids_plan] = seconds_arrive + durations_walk2 + durations_walk1
                    plans.times_est[ids_plan] = seconds_arrive + durations_walk2 + durations_walk1 - seconds_departure
                elif strategy ==strategies.get_id_from_formatted('walk'):
                    plans = virtualpop.get_plans()
                    plans.times_begin[ids_plan] = seconds_departure 
                    plans.times_end[ids_plan] = seconds_arrive +  durations_walk1
                    plans.times_est[ids_plan] = seconds_arrive + durations_walk1 - seconds_departure 
        return True 
   
    def get_scenario(self):
        return self.parent.get_scenario()
                                                                                        
class BirgilMatcher(Process):
    def __init__(self, ident = 'birgilmatcher', parent = None,  logger = None, **kwargs):
        print('BirgilMatcher.__init__')
        
        # TODO: let this be independent, link to it or child??
       
        
        self._init_common(  ident, 
                            parent = parent,
                            name = 'Birgillito Map matching', 
                            logger = logger,
                            info ='Birgillito Map matching.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.width_buffer_max = attrsman.add(cm.AttrConf( 'width_buffer_max',kwargs.get('width_buffer_max',140.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. buffer width', 
                            unit = 'm',
                            info = 'GPS points are only valid if they are within the given maximum buffer width distant from a network edge.',
                            ))
        
        
        self.width_buffer_terminal_max = attrsman.add(cm.AttrConf( 'width_buffer_terminal_max',kwargs.get('width_buffer_terminal_max',140.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. terminal. buffer width', 
                            unit = 'm',
                            info = """During the initialization phase when potential start and final edges are determined, 
                                  consider only edges  with a distance to  GPS point less of less than maximum terminal buffer width.
                                """,
                            ))
        
        #self.dist_backmove = attrsman.add(cm.AttrConf( 'dist_backmove',kwargs.get('dist_backmove',999999.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Back move dist', 
        #                    unit = 'm',
        #                    info = """Distance to detect whether a points is moving backward on an edge.
        #                        This may be dye to a false projection and the point will be considered outside the edge.
        #                        """,
        #                    ))
                            
        self.n_points_min = attrsman.add(cm.AttrConf( 'n_points_min',kwargs.get('n_points_min',5),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. point number', 
                            info = 'Minimum number of valid GPS points. Only if this minimum number is reached, a map-matching attempt is performed.',
                            ))                    
        
        
        #self.dist_min = attrsman.add(cm.AttrConf( 'dist_min',kwargs.get('dist_min',3.0),
        #                    groupnames = ['options'], 
        #                    name = 'Min. dist', 
        #                    unit = 'm',
        #                    info = 'Minimum distance used in the calculation of the edge cost function.',
        #                    ))
        
        
        self.weight_cumlength = attrsman.add(cm.AttrConf( 'weight_cumlength',kwargs.get('weight_cumlength',0.2),
                            groupnames = ['options'], 
                            name = 'Cumlength weight', 
                            info = 'Cost function weight of cumulative length.',
                            ))
        
        self.weight_angle = attrsman.add(cm.AttrConf( 'weight_angle',kwargs.get('weight_angle',10.0),
                            groupnames = ['options'], 
                            name = 'Angle weight', 
                            info = 'Cost function weight of angular coincidence.',
                            ))
                                                
        self.weight_access = attrsman.add(cm.AttrConf( 'weight_access',kwargs.get('weight_access',10.0),
                            groupnames = ['options'], 
                            name = 'Access weight', 
                            info = 'Cost function weight of access level for matched mode.',
                            ))
                            
        
        
        self.dist_modespecific = attrsman.add(cm.AttrConf( 'dist_modespecific',kwargs.get('dist_modespecific',5.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode dist', 
                            unit = 'm',
                            info = 'This mode specific distance is multiplied with the access-level and subtracted from the edge distance in case the edge is exclusive for the mode of the matched trip.',
                            ))
         
         
        
        self.dist_min_modespecific = attrsman.add(cm.AttrConf( 'dist_min_modespecific',kwargs.get('dist_min_modespecific',15.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode dist min', 
                            unit = 'm',
                            info = 'Minimum edge distance for which mode specific modifications take place. This is to prevent that a sum of very short edges with exclusive access causes the route to receive too much cost reduction.',
                            ))
                                                 
        self.n_routes_follow = attrsman.add(cm.AttrConf( 'n_routes_follow',kwargs.get('n_routes_follow',20),
                            groupnames = ['options'], 
                            name = 'Followed routes', 
                            info = 'Number of routes which are followed in parallel.',
                            ))
        
        self.is_ignor_connections = attrsman.add(cm.AttrConf( 'is_ignor_connections',kwargs.get('is_ignor_connections',True),
                            groupnames = ['options'], 
                            name = 'Ignore connections', 
                            info = 'Ignore connections during the route reconstruction. This option should be disabeled if routes are the bases for successive simulations.',
                            ))
        
        self.n_targetedge_route = attrsman.add(cm.AttrConf( 'n_targetedge_route',kwargs.get('n_targetedge_route',5),
                            groupnames = ['options'], 
                            name = 'Numb. of target edges', 
                            info = """Maximum number of routes to target edges. 
                                    In absents of GPS points near edges, shortest path routing takes place. 
                                    This number limits the number of routes to bridge the GPS points.
                                    """,
                            ))                    
        
        
        #self.dist_route_max = attrsman.add(cm.AttrConf( 'dist_route_max',kwargs.get('dist_route_max',500.0),
        #                    groupnames = ['options'], 
        #                    name = 'Max. routing distance', 
        #                    unit = 'm',
        #                    info = 'This is the maximum length the algorithm is routing in the absents of GPS points.',
        #                    ))
                            
        #print '  color_route',kwargs.get('color_route',COLOR_MATCHED_ROUTE),COLOR_MATCHED_ROUTE              
        self.color_route = attrsman.add(cm.AttrConf(  'color_route', kwargs.get('color_route',COLOR_MATCHED_ROUTE.copy()),
                                        groupnames = ['options'],
                                        perm='wr', 
                                        metatype = 'color',
                                        name = 'Route color', 
                                        info = 'Color of matched routes.',
                                        ))
    
    def do(self):
        logger = self.get_logger()
        timeconst = 0.95
        
        
        trips = self.parent.trips
        #routes = trips.get_routes()
        edges = self.get_edges()
        ids_edge = edges.get_ids()
        id_mode_ped = self.get_net().modes.get_id_mode('pedestrian')
        vtypes = self.parent.get_scenario().demand.vtypes
        
        ids_trip = trips.get_ids_selected()
        
        ids_mode = vtypes.ids_mode[trips.ids_vtype[ids_trip]]
        
        
        
        n_trips = len(ids_trip)
        logger.w('Start mapmatching of %d GPS traces with Birgillito method'%n_trips) 
        
        distancesmap = self.parent.get_distancesmap(is_check_lanes = True)
        accesslevelsmap = self.parent.get_accesslevelsmap()
        fstarmap = self.parent.get_fstarmap(is_return_arrays = True, is_ignor_connections = self.is_ignor_connections)
        
        n_trip_matched = 0
        time_match_trace_av = 3.0 # initial guess of match time
        
        # ,vtypes.speed_max[ids_vtype]  
        for id_trip, ids_point, id_mode in zip(ids_trip, trips.ids_points[ids_trip], ids_mode):
            tick_before = time.time()
            logger.w('Analyzing Trip %d...'%id_trip) 
            if ids_point is not None:
                if (id_mode in distancesmap) & (id_mode != id_mode_ped) & (len(ids_point)>=2):
                    
                    
                    are_valid = distancesmap[id_mode] > self.dist_min_modespecific
                    
                    #print '  weights[26216]',distancesmap[id_mode][26216],accesslevelsmap[id_mode][26216]
                    #print '  weights[26217]',distancesmap[id_mode][26217],accesslevelsmap[id_mode][26217]
    
                    # call conventional monodirectional matcher
                    #########DELETED PRINT
                    #print '  call matcher for trip',id_trip,'mode',id_mode
                    
                    #print '  len(are_valid)',len(are_valid),len(ids_edge)
                    route, length_route, length_route_mixed,length_route_exclusive, length_route_contrary, duration_gps, lengthindex,err_dist, t_match, ids_point_edgeend ,is_connected = \
                                self.match_trip_birgil(id_trip, ids_point, 
                                                        fstarmap[id_mode], 
                                                        distancesmap[id_mode] - self.dist_modespecific * are_valid * accesslevelsmap[id_mode], 
                                                        accesslevelsmap[id_mode])
                            
                                        
                    trips.set_matched_route(    id_trip, route, 
                                                length_route,  
                                                length_route_mixed,
                                                length_route_exclusive,
                                                length_route_contrary,
                                                duration_gps, 
                                                lengthindex,
                                                err_dist, 
                                                t_match,
                                                is_connected = is_connected,
                                                ids_point_edgeend = ids_point_edgeend,
                                                color = self.color_route,
                                                )
                else:
                    logger.w("  Failed id_trip %d because pedestrian mode or id_mode %d does not occured in network"%(id_trip,id_mode)) 
                    print('  distancesmap.keys()',list(distancesmap.keys()))
                    
            else:
                logger.w("  Failed id_trip %d because no points"%(id_trip)) 
                print('  ids_point',ids_point)
                
            n_trip_matched += 1
            
            time_match_trace =  time.time()-tick_before
            
            time_match_trace_av = timeconst*time_match_trace_av + (1.0-timeconst)*time_match_trace
            time_end_est = (n_trips-n_trip_matched)*time_match_trace_av
            
            progress = 100.0*n_trip_matched/float(n_trips)
            logger.w("Matched %d/%d traces, %.2f%% comleted. Avg match time = %.2fs, Terminated in %.1fh"%(n_trip_matched,n_trips,progress,time_match_trace_av,float(time_end_est)/3600.0),key ='message')
            logger.w(progress, key ='progress')
        return True
    
    def get_edges(self):
        return self.parent.get_scenario().net.edges
    
    def get_net(self):
        return self.parent.get_scenario().net
    
    def match_trip_birgil(self,id_trip,  ids_point, fstar, weights, accesslevels):
        # TODO: record time intervals
        # calculate initial and final position on edge to get more precise
        # triplength
        
        # TODO: check get_closest_edge everywhere if projecting properly
        print(79*'=')
        print('match_trip_birgil',id_trip,'n_points=',len(ids_point))
        #print '  ids_point',ids_point
        #print '  weights[26216]',weights[26216],accesslevels[26216]
        #print '  weights[26217]',weights[26217],accesslevels[26217]
        tick = time.time()
        routes = []
        route = None
        route_mindist = None
        intervals_route = []
        length_route = -1.0
        length_bikeway = -1.0
        length_mindist = -1.0
        matchindex = -1.0
        lengthindex = -1.0
        err_dist = -1.0
        err_dist_alg = -1.0
        t_match = -1.0
        duration_gps = -1.0
        duration_est = -1.0
        
        points = self.parent.points
        #trips = self.parent.trips
        #ids_point = trips.ids_points[id_trip]
        #pointset = self.pointsets.get(id_trip)
        
        # create a multi point object with points of traces
        # should no longer be necessary
        #tracepoints = MultiPoint(pointset.get_coords().tolist())
        
        # make a array with time stamps of all points
        pointtimes = points.timestamps[ids_point]
        #segind_to_id_edge = self._segind_to_id_edge #<<<<<<<<<<<
        coords = points.coords[ids_point][:,:2]
        
        x1,y1,x2,y2 = self.parent.get_segvertices_xy()
        edges = self.get_edges()
        get_ids_edge_from_inds_seg = edges.get_ids_edge_from_inds_seg
        get_dist_point_to_edge = edges.get_dist_point_to_edge
        get_closest_edge = edges.get_closest_edge
        #edgehit_counts = np.zeros(len(self._net.getEdges()),int)
        # segind_to_id_edge = self._segind_to_id_edge
        #for p in pointset.get_coords():
        #    print '  ',p
        #    hitinds = get_dist_point_to_segs(p,self._x1,self._y1,self._x2,self._y2, is_ending=True)< self.width_buffer**2
        #    edgehit_counts[segind_to_id_edge[hitinds]]+= 1
        
        ##---------------------------------------------------------------------
        ## 1. initialization
        
        # Set of initial edges 

        # create a list with lists  for each point
        # Each list contans the edges, where the point is in the edge buffer
        #edges_containing_point = np.zeros(len(ids_points), object)
        #edgedists_to_point = np.zeros(len(ids_points), object)
        
        ids_edge_initial = []
        ids_edge_final = []
        dists_initial = []
        n_points = len(ids_point)
        ind_point_initial = -1
        ind_point_final = -1
        t_point_initial = -1
        t_point_final = -1
        
        #print '  search initial edges'
        ind_point = 0
        for p in coords:
            dists2 = get_dist_point_to_segs(p,x1,y1,x2,y2, is_ending=True, is_detect_initial = False, is_detect_final = True)
            inds_hit = np.flatnonzero(dists2 < self.width_buffer_terminal_max**2)
            # inds_hit = np.flatnonzero(np.logical_and(dists2 < self.width_buffer_max**2),np.logical_not(np.isnan(dists2))))
            #print '    id_point',ids_point[ind_point],'width_buffer_max',self.width_buffer_max
            
            #if len(inds_hit)>0:
            #    print '    len(ids_point_hit)',len(set(get_ids_edge_from_inds_seg(inds_hit)))
            #    print '    max(dists)',np.max(np.sqrt(dists2[inds_hit]))
            #else:
            #    print '    no hits'
            #ids_edge_hit = get_ids_edge_from_inds_seg(inds_hit)
            inds_hit = inds_hit[accesslevels[get_ids_edge_from_inds_seg(inds_hit)]>-1]
            #print '  inds_hit',inds_hit
            if len(inds_hit)>0:
                
                if ind_point_initial<0:
                    #print '    set initial points'
                    #print '   inds_hit',ind_point,inds_hit
                    ids_edge_initial = set(get_ids_edge_from_inds_seg(inds_hit))
                    #print '      ids_edge_initial',ids_edge_initial
                    #dists_initial = np.sqrt(dists2[inds_hit])
                    ind_point_initial = ind_point
                    t_point_initial = pointtimes[ind_point]
                    #print '      t_point_initial',t_point_initial
                    #print '   ind_point, inds_hit, ind_point_initial',ind_point,inds_hit,ind_point_initial,ids_edge_initial
                else:
                    #print '    set final points'
                    ids_edge_final = set(get_ids_edge_from_inds_seg(inds_hit))
                    #print '      ids_edge_final',ids_edge_final
                    ind_point_final = ind_point
                    t_point_final = pointtimes[ind_point]
                    #print '      ind_point_final',ind_point_final
                    #print '   ind_point, inds_hit, ind_point_final',ind_point,inds_hit,ind_point_initial,ids_edge_final
            
            ind_point +=1
        
        n_points_eff = ind_point_final - ind_point_initial
        
        duration_gps = pointtimes[ind_point_final] - pointtimes[ind_point_initial]
        
        #if self._logger:
        #    self._logger.w( '>>match_trip_birgil : n_points_eff=%d, len(ids_edge_initial)=%d,len(ids_edge_final)=%d'%(n_points_eff, len(ids_edge_initial),len(ids_edge_final)) )
        #print '  completed init:'
        #########DELETED PRINT
        #print '  ids_edge_initial',ids_edge_initial
        #print '  ids_edge_final',ids_edge_final
        
        #print '  ind_point_initial,ind_point_final,n_points_eff',ind_point_initial,ind_point_final,n_points_eff
        #########DELETED PRINT
        #print '  id_point_initial=%d,id_point_final=%d,n_points_eff=%d'%(ids_point[ind_point_initial],ids_point[ind_point_final],n_points_eff)
        if (ind_point_initial<0)|(ind_point_final<0) | (n_points_eff < self.n_points_min):
            #########DELETED PRINT
            #print 'ABOARD: insufficient valid points' 
            return [], 0.0, 0.0,-1.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
        
        # print '  coords',coords
        # create initial routeset
        routelist = []
        
        #id_edge_unique = []
        for id_edge  in ids_edge_initial:
            
            cost0 = get_dist_point_to_edge(coords[ind_point_initial], id_edge)
            
            #print '  coords of initial point:',coords[ind_point_initial]
            #print '  cost0, id_edge',cost0, id_edge
            
            #get_ind_seg_from_id_edge(self, id_edge)
            
            
            length_edge = edges.lengths[id_edge]
            accesslevel = accesslevels[id_edge]
            
            #cost_tot = cost0+self.weight_cumlength * length_edge - self.weight_access* accesslevel
            
            # initial edge length and angle does not impact initial costs
            cost_tot = cost0- self.weight_access* accesslevel
            routelist.append([cost_tot, cost0, 0.0,length_edge,accesslevel, length_edge,[id_edge],[ids_point[ind_point_initial], -1]])
        
        routelist.sort()    
        #print '  initial routelist',routelist
        
        
        ##---------------------------------------------------------------------
        ## 2. main loop through rest of the points
        
        #for i in xrange(ind_point+1, n_points):#coords[ind_point+1:]:
        #is_connected = True
        ind_point = ind_point_initial+1
        length_gps = 0.0
        
        # for debugging
        route_best_disconnected = [np.inf, 0.0, 0.0,0.0,0, 0.0,[],[]]
        
        while (ind_point <= ind_point_final):# & is_connected:
            is_disconnected = True
            
            point = coords[ind_point]
            id_point = ids_point[ind_point]
            delta_point = point-coords[ind_point-1]
            
           
            
            
            phi_point = np.arctan2(delta_point[1], delta_point[0])
            #
            if 1:
                dist_interpoint = np.sqrt(np.sum(delta_point**2))
                length_gps += dist_interpoint
                #print 79*'_'
                #########DELETED PRINT
                #print '    check ID point %d,  dist_interpoint=%.2fm, length_gps=%.2fm, phi_point %d deg'%(ids_point[ind_point],dist_interpoint,length_gps,phi_point/np.pi*180)
                
            routelist_new = []
            n_routes = len(routelist)
            for ind_route in range(n_routes):
                #routeinfo = 1*routeinfo_orig
                costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = routelist[ind_route]
                routeinfo = costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend
                if np.isinf(costs_tot):
                     break
                #if len(ids_edge) == 0:
                #    break
                
                
                id_edge_last = ids_edge[-1]
                # minimum distance point-edge
                
                #print '  call get_dist_point_to_edge',point, id_edge_last
                dist_point_edge, segment = get_dist_point_to_edge(point, id_edge_last, 
                                        is_ending=True,
                                        is_detect_initial = False,
                                        is_detect_final = True,
                                        is_return_segment = True,
                                        )
                #dist_point_edge = self.get_dist_point_edge(coords[ind_point], id_edge_last, is_detect_final = True)
                # if dist_point_edge is not a number (nan) the point is outside projection of edge
                pos = edges.get_pos_from_coord(id_edge_last, [point[0],point[1],0.0] )
                #########DELETED PRINT
                #print 79*'-'
                #print '      route ',ind_route,'costs_tot',costs_tot,'dist_point_edge',dist_point_edge, 'len',len(ids_edge),'id_edge_last',id_edge_last
                #print '        length_partial',length_partial,'pos',pos,pos-length_partial>self.width_buffer_max
                #print '        cost= %.3f, length_cum=%.3f, access=%d'%(cost,length_cum,accesslevel)
                #print '        Xost= %.3f, Xength_cum=%.3f'%(cost,self.weight_cumlength *length_cum,)
                #print '        ids_edge',ids_edge
                #print '        ids_point_edgeend',ids_point_edgeend
                #if length_partial > self.alpha * length_edge:
                if (np.isnan(dist_point_edge))|(dist_point_edge >self.width_buffer_max ):#|((weights[id_edge_last]>self.dist_backmove)& (pos-length_partial<self.dist_backmove)): 
                    # point is beyond edge
                    
                    
                    # has reached end of edge
                    length_partial = 0
                    
                    # get FSTAR
                    #print '        id_edge_last,',id_edge_last,' fstar',fstar[id_edge_last]
                    ids_edge_next_all = fstar[id_edge_last]
                    
                    # filter allowed edges, by verifying positivness of
                    # travel costs # need access levels, costs is negative if no access??
                    #print '    ids_edge_next_all',ids_edge_next_all
                    #print '    weights[ids_edge_next_all]',weights[ids_edge_next_all]
                    #print '    accesslevels[ids_edge_next_all]',accesslevels[ids_edge_next_all]
                    
                    ids_edge_next = ids_edge_next_all[ (weights[ids_edge_next_all]>=0) & (accesslevels[ids_edge_next_all]>=0) ]
                    #########DELETED PRINT
                    #print '        parse ids_edge_next',ids_edge_next
                    
                    
                    is_inserted_first = False 
                    ids_edge_target = None
                    for id_edge in ids_edge_next:
                        #routeinfo = 1*routeinfo_orig
                        routelist_next, ids_edge_target = self.get_next_routeinfo(\
                            routeinfo, id_edge, point, phi_point, id_point,
                            get_dist_point_to_edge, get_closest_edge, edges, 
                            fstar, weights, accesslevels, ids_edge_target = ids_edge_target)
                        #print '        newroute',newroute
                        #print '        update?',len(newroute[6]),len(newroute[6])>0
                        for newroute in routelist_next:
                            #if len(newroute[6])>0:
                            #    print '        >>extend: id_edge_next',id_edge,is_inserted_first,'costs_tot %.1f'%newroute[0],'lenthcontrib %.1f'%(self.weight_cumlength * newroute[3]),'cumlenth %.1f'%newroute[3],newroute[6]
                            #else:
                            #    print '        >>extend: id_edge_next',id_edge,is_inserted_first,': is dead end.'
                            
                            if len(newroute[6])>0:
                                # new next route is valid
                                if is_inserted_first:
                                    # create new children
                                    routelist_new.append(list(newroute))
                                else:
                                    # update current
                                    #routeinfo_orig[:]
                                    routelist[ind_route][:] = newroute[:]
                                    is_inserted_first = True
                                    #print '      check costs_tot',routelist[ind_route][0]
                    
                    if not is_inserted_first:
                        #########DELETED PRINT
                        #print '        no valid continuation found to reach point',route_best_disconnected[0],costs_tot,len(ids_edge)
                        
                        
                        # deal with best disconnected route
                        is_disconnected = True
                        if (route_best_disconnected[0]>costs_tot) & (len(ids_edge)>0):
                            #########DELETED PRINT
                            #print '        best disconnected route:',ids_edge
                            route_best_disconnected = copy(routelist[ind_route])
                        
                        # set ind routecost
                        routelist[ind_route][0] = np.Inf
                    else:
                        is_disconnected = False

            
                else:
                    #########DELETED PRINT
                    #print '        point can be projected on current edge'
                    # has not reached end of current edge
                    # save updated data of current route
                    if len(ids_edge)>0:
                        #dist += self.get_dist_point_edge(coords[ind_point], ids_edge[-1])
                        cost_birgil_tot = cost + self._get_cost_birgil2( phi_point, accesslevel,
                                                        dist_point_edge, segment)
                    cost_tot = cost_birgil_tot+self.weight_cumlength * length_cum
                    ids_point_edgeend[-1] = id_point
                    #########DELETED PRINT
                    #print '        >>Update: cost_tot %.1f'%costs_tot,'cost_birgil_tot %.1f'%cost_birgil_tot,'lenthcontrib %.1f'%(self.weight_cumlength * length_cum),'cumlenth %.1f'%length_cum
                    
                    #ids_point_edgeend[-1] =  id_point     
                    #length_partial += dist_interpoint 
                    length_partial = pos
                    routelist[ind_route][:] = (cost_tot, cost_birgil_tot, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend)
                    
                    is_disconnected = False
                    
                    
                ind_route +=1
                                
            routelist += routelist_new
            #print '  routelist',routelist
            
            # clean routelist
            routelist.sort()
            
            # take only the chapest route that arrives at a specific edge
            n_routes = len(routelist)
            for route, i in zip(routelist[:-1],range(n_routes-1)):
                #cost_tot = route[0]
                id_edge_last = route[6][-1]
                for j in range(i+1,n_routes):
                    if routelist[j][6][-1] == id_edge_last:
                        routelist[j][0] = np.inf
            # this will throw all infinites to the tail            
            routelist.sort()
            
            # cut off all infinites and cut off at self.n_routes_follow routes
            #print ' '
            is_cont = np.isinf(routelist[-1][0])
            while len(routelist)>self.n_routes_follow  | is_cont:
                route = routelist.pop()
                #print '  pop route',route[0],route[6]
                is_cont = np.isinf(route[0])& (len(routelist)>1)
            #########DELETED PRINT
            #print '  remaining routes:',len(routelist)    
            # cut list to self.n_routes_follow
            #if len(routelist)> self.n_routes_follow+1:
            #    routelist = routelist[:self.n_routes_follow+1]
            
            if not is_disconnected:
                route_best_disconnected = [np.inf, 0.0, 0.0,0.0,0, 0.0,[],[]]
        
        
            ind_point +=1
            
        # recover final edge objects of winner route!
        costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = routelist[0]
        
        if ids_edge[-1] not in ids_edge_final:
            costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = route_best_disconnected
            #########DELETED PRINT
            #print 'DISCONNECTED: take best disconnected route:',ids_edge
            is_connected = False
             
             
        #print '  ids_edge[-1]',ids_edge[-1],ids_edge[-1] in ids_edge_final
        #print '  ids_edge_final',ids_edge_final
        t_match = time.time() - tick 
        
        ##--------------------------------------------------------------------
        ## post matching analisis
        #########DELETED PRINT
        #print '\n'+79*'-'
        #print '  matched route:',len(ids_edge),ids_edge
        #print '  ids_point_edgeend',len(ids_point_edgeend),ids_point_edgeend
        route = ids_edge 
        if len(route) == 0:
            #########DELETED PRINT
            #print 'ABOARD: route contains no edges ids_edge=',ids_edge
            return [], 0.0, 0.0,-1.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
        
        length_gps = np.sum(np.sqrt(np.sum((coords[ind_point_initial+1:ind_point_final]-coords[ind_point_initial:ind_point_final-1])**2,1)))
                     
        length_route =  np.sum(weights[ids_edge]) 
        
        length_route_mixed = 0.0
        length_route_exclusive = 0.0
        length_route_contrary = 0.0
        for id_edge, length in zip(ids_edge,weights[ids_edge]):
            accesslevel = accesslevels[id_edge]
            if self.parent.get_scenario().net.lanes.get_accesslevel_bikecontrary_special(id_lane = self.parent.get_scenario().net.edges.ids_lanes[id_edge][0]) == -2:
                length_route_contrary += length
            elif accesslevel ==1:
                length_route_mixed += length
            elif accesslevel ==2:
                length_route_exclusive += length

        if length_gps>0:
            lengthindex = length_route/length_gps
        else:
            lengthindex = -1.0
        
            
        
        
        
        # check dist error
        inds_point = range(ind_point_initial,ind_point_final)
        n_points = len(inds_point)
        
        
        if (n_points >= 2)&(len(route)>0):
            #print '  initialize distance error measurement',len(route)
            dist_points_tot = 0.0 
            routestring = get_routelinestring(route, edges)
            #print '  routestring =',routestring 
            
            #print '  coords',coords[inds_point].tolist()
            # TODO: avoid using Multipoins from shapely package
            tracepoints = MultiPoint(coords[inds_point].tolist())
        
            # measure precision
            #routeset = set(route)
            #n_points_boundary = 0
            ind_tracepoint = 0
            for ind_point in inds_point:#xrange(n_points):
                d = routestring.distance(tracepoints.geoms[ind_tracepoint])
                dist_points_tot += d
                #print '   v distance measurement',d,tracepoints.geoms[ind_tracepoint],coords[ind_point]#,n_points#,n_points_eff
                
                ind_tracepoint += 1
            
            err_dist = dist_points_tot/float(n_points) 
        
        if ids_edge[-1] in ids_edge_final:#dist!=np.Inf: DID WE ARRIVE?
            #########DELETED PRINT
            #print 'SUCCESS: target',ids_edge[-1],'reached length %.2f, gps length %.2f$'%(length_route,length_gps)
            is_connected = True
        else:
            #########DELETED PRINT
            #print 'DISCONNECTED: last matched edge',ids_edge[-1],' did not reach final edges',ids_edge_final
            is_connected = False
             
             
        #print '  check',(lengthindex<self.lengthindex_max),(lengthindex >= self.lengthindex_min),(duration_gps>self.duration_min),(length_route>self.dist_min),(len(route)>0)
        #print '   twice',(lengthindex<self.lengthindex_max)&(lengthindex >= self.lengthindex_min)&(duration_gps>self.duration_min)&(length_route>self.dist_min)&(len(route)>0)                    
        
        return route, length_route, length_route_mixed,length_route_exclusive, length_route_contrary, duration_gps, lengthindex, err_dist, t_match,ids_point_edgeend ,is_connected
    
                
    
                    
            
    def get_next_routeinfo( self, routeinfo, id_edge, point, phi_point, id_point, 
                                get_dist_point_to_edge, get_closest_edge, 
                                edges, fstar, weights, accesslevels,
                                ids_edge_target = None):
        
        costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = routeinfo
        #########DELETED PRINT
        #print 'get_next_routeinfo id_edge',id_edge,len(ids_edge),'id_point',id_point
        #print '  ids_edge',ids_edge
        #length_partial += dist_interpoint
        
        routelist_next = []
        # minimum distance point-new edge
        tick =  time.time()
        dist_point_edge, segment = get_dist_point_to_edge(point, id_edge, 
                                    is_ending=True,
                                    is_detect_initial = False,
                                    is_detect_final = True,
                                    is_return_segment = True,
                                    )
        
        #########DELETED PRINT
        #print '  get_dist_point_to_edge in %.2fs'%(time.time()-tick),'dist_point_edge',dist_point_edge
        #print '  dist_point_edge',dist_point_edge,dist_point_edge >self.width_buffer_max,(np.isnan(dist_point_edge))|(dist_point_edge >self.width_buffer_max )
        
        
        
        # check if point is projecting on new edge
        if  (np.isnan(dist_point_edge))|(dist_point_edge >self.width_buffer_max ): 
            # point is not projecting.
            # try to route
            # costs_tot, cost, length_cum, ids_edge, is_arrived
            
            if ids_edge_target is None:
                tick =  time.time()
                ids_edge_target, dists = get_closest_edge( point,  n_best = self.n_targetedge_route, 
                                                    d_max = self.width_buffer_max,
                                                    is_ending = True, 
                                                    is_detect_initial = False, 
                                                    is_detect_final = True,
                                                    accesslevels = accesslevels)
                #########DELETED PRINT
                #print '  get_closest_edge in %.2fs'%(time.time()-tick)
            #id_edge_target = get_closest_edge( point,  n_best = 0, d_max = self.width_buffer_max,
            #                                    is_ending = True, 
            #                                    is_detect_initial = False, 
            #                                    is_detect_final = False)
            # 
            #ids_edge_target = [id_edge_target]                                   
            #########DELETED PRINT
            #print '  route id_edge',id_edge,'->ids_edge_target',ids_edge_target
            #print '  dists',dists
            
            if 1:#id_edge not in ids_edge_target:
                tick =  time.time()
                costs, routes = routing.get_mincostroute_edge2edges(id_edge, ids_edge_target, 
                                                            weights = weights, fstar = fstar)
                tock = time.time()-tick
                #########DELETED PRINT
                #print '  routing done in %.2fs'%(tock)
                
                
                tick =  time.time()
                for cost_r, ids_edge_r in zip(costs, routes):
                    # cost_r not used
                    
                    # eliminate loops
                    #edgeset = set(ids_edge_r)
                    
                    #if length_cum_r < length_cum_min:
                    #    length_cum_min = length_cum_r
                         
                    #if is_arrived & (len(ids_edge_r)>0) & (length_cum_r < 2*length_cum_min) & (edgeset.isdisjoint(ids_edge)):
                    if (len(ids_edge_r)>0):
                         # append route information from routing
                         #########DELETED PRINT
                         #if 1:#tock>0.8:
                            
                            #print '  route:',np.sum(edges.lengths[ids_edge_r]),ids_edge_r
                            #print '       cost_r',cost_r
                         
                         id_edge_last = ids_edge_r[-1]
                         
                         dist_point_edge, segment = get_dist_point_to_edge(point, id_edge_last, 
                                        is_ending = True,
                                        is_detect_initial = False,
                                        is_detect_final = True,
                                        is_return_segment = True,
                                        )
                                        
                         accesslevel = accesslevels[id_edge_last]
                         ids_edge_out = ids_edge + ids_edge_r
                         length_cum_out = length_cum + np.sum(edges.lengths[ids_edge_r])
                         
                         # todo: this is not costs of all edges!
                         cost_birgil_tot = cost + self._get_cost_birgil2(  phi_point, accesslevel, 
                                                                dist_point_edge, segment) 
                        
                         costs_tot = cost_birgil_tot + self.weight_cumlength * length_cum_out 
                         #########DELETED PRINT
                         #print '       cost_tot',costs_tot,'cost_birgil_tot',cost_birgil_tot,'cost_length',self.weight_cumlength * length_cum_out
      
                       
    
                    
                         # edge end points have ID = -1 for edges without points
                         ids_point_edgeend_r = len(ids_edge_r)*[-1]
                         # the last edge obtains the point ID it reached
                         ids_point_edgeend_r[-1] = id_point
                         
                         ids_point_edgeend_out = ids_point_edgeend+ids_point_edgeend_r
                         
                         routelist_next.append([costs_tot, cost_birgil_tot, length_partial, length_cum_out, accesslevel, edges.lengths[id_edge_last], ids_edge_out, ids_point_edgeend_out])
                         
                    #else:
                    #    print '    no route to target. failed to append'
                    #    costs_tot = np.Inf
                    #    cost_edge = np.Inf# punish route with infinite costs
                    #    length_edge = np.Inf
                    #    length_cum = np.Inf
                    #    accesslevel = -1
                    #    ids_edge_out = []
                    #    ids_point_edgeend_out = []
                #########DELETED PRINT
                #print '  loop over %d rout evaluations in %.2fs'%(len(costs),time.time()-tick)    
                return routelist_next, ids_edge_target
                        
                 
        #else:
        #########DELETED PRINT
        #print '  point can be projected on id_edge',id_edge 
        ids_edge_out = ids_edge+[id_edge]
        length_edge = edges.lengths[id_edge]
        accesslevel = accesslevels[id_edge]
        length_cum_out = length_cum + length_edge
        cost_birgil_tot = cost + self._get_cost_birgil2(  phi_point, accesslevel, 
                                        dist_point_edge, segment) 
        
        costs_tot = cost_birgil_tot + self.weight_cumlength * length_cum_out 
        #########DELETED PRINT
        #print '    cost_tot',costs_tot,'cost_birgil_tot',cost_birgil_tot,'lenthcontrib',self.weight_cumlength * length_cum_out
        
        ids_point_edgeend_out = ids_point_edgeend+[id_point,]
        
        
        routelist_next.append([costs_tot, cost_birgil_tot, length_partial, length_cum_out, accesslevel, length_edge, ids_edge_out, ids_point_edgeend_out])          
    
        return routelist_next, ids_edge_target
                                    

       

                            
 
    def _get_cost_birgil2(self, phi_point, accesslevel, dist, segment, is_reverse = False):
        #print '_get_cost_birgil p, id_edge',p, id_edge
        
        if accesslevel<0:
            # no access
            return np.inf
        
        # there is a projection, consider directions    
        x1,y1,x2,y2 = segment
        
        
        
        #print '  x1,y1',x1,y1
        #print '  p',p[0],p[1]
        #print '  x2,y2',x2,y2
        #print '  dist',dist
        #seginds = self._id_edge_to_seginds[ind_edge]
        #dists2dists2 = get_dist_point_to_segs( p, self._x1[seginds], self._y1[seginds], self._x2[seginds], self._y2[seginds])
   
        
        #ind_segind = np.argmin(dists2)
        #segind = seginds[ind_segind] 
        #x1,y1,x2,y2 = (self._x1[segind], self._y1[segind], self._x2[segind], self._y2[segind])
        if is_reverse:
            
            phi_seg =  np.arctan2(y1-y2,x1-x2)
        else:
            phi_seg =  np.arctan2(y2-y1,x2-x1)
        
        

        phi_delta = anglediff(phi_point,phi_seg)
        #print '     dist =%.1f phi_point=%.2f,phi_seg=%.2f,phi_point-phi_seg=%.2f, delta_phi=%.2f'%(dist,phi_point/np.pi*180,phi_seg/np.pi*180,np.abs(phi_point-phi_seg)/np.pi*180,phi_delta/np.pi*180)
        
        x_angle = np.abs(phi_delta)
        # (phi_point-phi_seg)%(2 * np.pi)
        cost = dist + self.weight_angle *x_angle- self.weight_access* accesslevel
        #print '     cost_birgil=%.3f, dist=%.3f,  cost_angle=%.3f, x_angle=%.3f,Xccesslevel=%.3f'%(cost,dist,self.weight_angle *x_angle,x_angle,self.weight_access*accesslevel)
        
            
        return cost
    
class PTMatcher(BirgilMatcher):
    def __init__(self, ident, mapmatching,  logger = None, **kwargs):
        print('PTMatcher.__init__')
        
        # TODO: let this be independent, link to it or child??
       
        
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Public transport Map matching', 
                            logger = logger,
                            info ='Public transport Map matching.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.width_buffer_max = attrsman.add(cm.AttrConf( 'width_buffer_max',kwargs.get('width_buffer_max',30.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. buffer width', 
                            unit = 'm',
                            info = 'GPS points are only valid if they are within the given maximum buffer width distant from a network edge.',
                            ))
        
        
        self.width_buffer_terminal_max = attrsman.add(cm.AttrConf( 'width_buffer_terminal_max',kwargs.get('width_buffer_terminal_max',150.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. terminal. buffer width', 
                            unit = 'm',
                            info = """During the initialization phase when potential start and final edges are determined, 
                                  consider only edges  with a distance to  GPS point less of less than maximum terminal buffer width.
                                """,
                            ))
        
        #self.dist_track_max = attrsman.add(cm.AttrConf( 'dist_track_max',kwargs.get('dist_track_max',800.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Max tracking distance', 
        #                    unit = 'm',
        #                    info = """Maximum distance between a GPS point and a PT edge when PT route is tracked.
        #                    PT route with an edge beyond this distance will no longer be considered""",
        #                    ))
                            
        self.n_points_min = attrsman.add(cm.AttrConf( 'n_points_min',kwargs.get('n_points_min',4),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. point number', 
                            info = 'Minimum number of valid GPS points. Only if this minimum number is reached, a map-matching attempt is performed.',
                            ))                    
        
        self.n_edge_max = attrsman.add(cm.AttrConf( 'n_edge_max',kwargs.get('n_edge_max',5),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. overl. edge number', 
                            info = 'Maximum number of overlapping edges on a point.',
                            )) 
        #self.dist_min = attrsman.add(cm.AttrConf( 'dist_min',kwargs.get('dist_min',3.0),
        #                    groupnames = ['options'], 
        #                    name = 'Min. dist', 
        #                    unit = 'm',
        #                    info = 'Minimum distance used in the calculation of the edge cost function.',
        #                    ))
        
        
        self.weight_hits = attrsman.add(cm.AttrConf( 'weight_hits',kwargs.get('weight_hits',30.0),
                            groupnames = ['options'], 
                            name = 'hits weight', 
                            info = 'Cost function weight for number of GPS point in PT link buffer with respect to the estimated travel time.',
                            ))
        
        self.weight_stop_hits = attrsman.add(cm.AttrConf( 'weight_stop_hits',kwargs.get('weight_stop_hits',0.5),
                            groupnames = ['options'], 
                            name = 'stop hits weight', 
                            info = 'Cost function weight GPS points near stops, contributing to a negative cost of boarding links.',
                            ))
        #self.weight_angle = attrsman.add(cm.AttrConf( 'weight_angle',kwargs.get('weight_angle',10.0),
        #                    groupnames = ['options'], 
        #                    name = 'Angle weight', 
        #                    info = 'Cost function weight of angular coincidence.',
        #                    ))
                                                
        #self.weight_access = attrsman.add(cm.AttrConf( 'weight_access',kwargs.get('weight_access',10.0),
        #                    groupnames = ['options'], 
        #                    name = 'Access weight', 
        #                    info = 'Cost function weight of access level for matched mode.',
        #                    ))
                            
        
        
        #self.dist_modespecific = attrsman.add(cm.AttrConf( 'dist_modespecific',kwargs.get('dist_modespecific',5.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Mode dist', 
        #                    unit = 'm',
        #                    info = 'This mode specific distance is multiplied with the access-level and subtracted from the edge distance in case the edge is exclusive for the mode of the matched trip.',
        #                    ))
         
         
        
        #self.dist_min_modespecific = attrsman.add(cm.AttrConf( 'dist_min_modespecific',kwargs.get('dist_min_modespecific',15.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Mode dist min', 
        #                    unit = 'm',
        #                    info = 'Minimum edge distance for which mode specific modifications take place. This is to prevent that a sum of very short edges with exclusive access causes the route to receive too much cost reduction.',
        #                    ))
                                                 
        #self.n_routes_follow = attrsman.add(cm.AttrConf( 'n_routes_follow',kwargs.get('n_routes_follow',30),
        #                    groupnames = ['options'], 
        #                    name = 'Followed routes', 
        #                    info = 'Number of routes which are followed in parallel.',
        #                    ))
        
        #self.is_ignor_connections = attrsman.add(cm.AttrConf( 'is_ignor_connections',kwargs.get('is_ignor_connections',True),
        #                    groupnames = ['options'], 
        #                    name = 'Ignore connections', 
        #                    info = 'Ignore connections during the route reconstruction. This option should be disableled if routes are the bases for successive simulations.',
        #                    ))
        
        #self.n_targetedge_route = attrsman.add(cm.AttrConf( 'n_targetedge_route',kwargs.get('n_targetedge_route',5),
        #                    groupnames = ['options'], 
        #                    name = 'Numb. of target edges', 
        #                    info = """Maximum number of routes to target edges. 
        #                            In absents of GPS points near edges, shortest path routing takes place. 
        #                            This number limits the number of routes to bridge the GPS points.
        #                            """,
        #                    ))                    
        
        
        #self.dist_route_max = attrsman.add(cm.AttrConf( 'dist_route_max',kwargs.get('dist_route_max',500.0),
        #                    groupnames = ['options'], 
        #                    name = 'Max. routing distance', 
        #                    unit = 'm',
        #                    info = 'This is the maximum length the algorithm is routing in the absents of GPS points.',
        #                    ))
                            
        #print '  color_route',kwargs.get('color_route',COLOR_MATCHED_ROUTE),COLOR_MATCHED_ROUTE              
        self.is_random_waits = attrsman.add(cm.AttrConf( 'is_random_waits',kwargs.get('is_random_waits',False),
                            groupnames = ['options'], 
                            name = 'Randomize wait times', 
                            info = 'Randomize wait times at stops.',
                            ))
                            
        self.color_route = attrsman.add(cm.AttrConf(  'color_route', kwargs.get('color_route',COLOR_MATCHED_ROUTE.copy()),
                                        groupnames = ['options'],
                                        perm='wr', 
                                        metatype = 'color',
                                        name = 'Route color', 
                                        info = 'Color of matched routes.',
                                        ))
    
    def do(self):
        logger = self.get_logger()
        timeconst = 0.98
        
        scenario = self.get_scenario()
     
        demand = scenario.demand
        ptlines = demand.ptlines
        ptlinks = ptlines.get_ptlinks()
        mapmatching = self.parent

        net = scenario.net
        edges = net.edges
        lanes = net.lanes
        modes = net.modes
        
        ptstops = net.ptstops
        
        
        ### get mapmatching data
        
        trips = self.parent.trips
        
        ids_edge = edges.get_ids()
        
        id_mode_ped = net.modes.get_id_mode('pedestrian')
        id_mode_bus = net.modes.get_id_mode('bus')
        vtypes = demand.vtypes
        accesslevels_bus = edges.get_accesslevels(id_mode_bus)
        
        busdistances = edges.get_distances(id_mode_bus)
        busedgetimes = edges.get_times(id_mode = id_mode_bus)
        fstar = ptlinks.get_fstar()
        
        if len(fstar) == 0:
            print('WARNING: no PT links built.')
            return False
        
        ids_trip = trips.get_ids_selected()
        
        ids_mode = vtypes.ids_mode[trips.ids_vtype[ids_trip]]
        
        
        
        n_trips = len(ids_trip)
        logger.w('Start mapmatching of %d GPS traces with PT matcher'%n_trips) 
        

        n_trip_matched = 0
        time_match_trace_av = 3.0 # initial guess of match time
        
 
        for id_trip, ids_point, id_mode in zip(ids_trip, trips.ids_points[ids_trip], ids_mode):
            tick_before = time.time()
            if (len(fstar)>0) & (id_mode == id_mode_bus) & (len(ids_point)>=2):
                
                
                ptroute,route, length_route, length_route_mixed,length_route_exclusive, duration_gps, lengthindex,err_dist, t_match, ids_point_edgeend ,is_connected = \
                            self.match_trip(id_trip, ids_point, fstar, ptstops, ptlines, busdistances, busedgetimes, accesslevels_bus)
                        
                
                if len(ptroute)>0:                  
                    trips.set_matched_ptroute(  id_trip, ptroute, route,
                                                length_matched = length_route,  
                                                length_route_mixed = length_route_mixed,
                                                length_route_exclusive = length_route_exclusive,
                                                duration_matched = duration_gps, 
                                                lengthindex = lengthindex,
                                                error_dist = err_dist, 
                                                comptime = t_match,
                                                is_connected = is_connected,
                                                ids_point_edgeend = ids_point_edgeend,
                                                color = self.color_route,
                                                )
                               
                                    
                else:
                    logger.w("  Failed id_trip %d, no route found."%(id_trip,))                     
                                            
            else:
                logger.w("  Failed id_trip %d because %d is not a bus mode or too few points."%(id_trip,id_mode)) 
                
            
            n_trip_matched += 1
            
            time_match_trace =  time.time()-tick_before
            
            time_match_trace_av = timeconst*time_match_trace_av + (1.0-timeconst)*time_match_trace
            time_end_est = (n_trips-n_trip_matched)*time_match_trace_av
            
            progress = 100.0*n_trip_matched/float(n_trips)
            logger.w("Matched %d/%d traces, %.2f%% comleted. Avg match time = %.2fs, Terminated in %.1fh"%(n_trip_matched,n_trips,progress,time_match_trace_av,float(time_end_est)/3600.0),key ='message')
            logger.w(progress, key ='progress')
        return True
    
    def get_scenario(self):
        return self.parent.get_scenario()
  
    
    def match_trip(self,id_trip,  ids_point, ptfstar, ptstops, ptlines, busdistances, busedgetimes, accesslevels):

        print(79*'=')
        print('match_trip',id_trip,'n_points=',len(ids_point))
        tick = time.time()
        routes = []
        route = None
        route_mindist = None
        length_route = -1.0
        length_bikeway = -1.0
        length_mindist = -1.0
        matchindex = -1.0
        lengthindex = -1.0
        err_dist = -1.0
        err_dist_alg = -1.0
        t_match = -1.0
        duration_gps = -1.0
        duration_est = -1.0
        
        scenario = self.get_scenario()
        demand = scenario.demand
        points = self.parent.points
        ptlinks = ptlines.get_ptlinks()
        ptdurations = ptlinks.durations
        ptlinktypes = ptlinks.types
        ptlinktypenames = ptlinktypes.choices
        type_enter = ptlinktypenames['enter']
        type_transit = ptlinktypenames['transit']
        type_board = ptlinktypenames['board']
        type_alight = ptlinktypenames['alight']
        type_transfer = ptlinktypenames['transfer']
        type_walk = ptlinktypenames['walk']
        type_exit = ptlinktypenames['exit']
        
        edges = scenario.net.edges
        
        id_mode_bus = scenario.net.modes.get_id_mode('bus')
        
        edgehits = np.zeros(len(busedgetimes), dtype = np.float32)
        
        
        pttimes = ptlinks.get_times(is_randomwait = self.is_random_waits)# 
        ptlinkhits = np.zeros(len(pttimes), dtype = np.float32)
        
        ids_stop = ptstops.get_ids()
        coords_stop = ptstops.centroids[ids_stop][:,:2]
        print('       len(ids_stop)',len(ids_stop))
        print('       coords_stop.shape',coords_stop.shape)
        
        
        # map stop IDs to PT link IDs with specific type
        map_id_fromstop_to_ids_link = ptlinks.get_map_stop_to_ptlinks(is_fromstop = True, is_tostop = False, linktype = type_board)
        map_id_tostop_to_ids_link = ptlinks.get_map_stop_to_ptlinks(is_fromstop = False, is_tostop = True,  linktype = type_transit)
        

        
        # make a array with time stamps of all points
        pointtimes = points.timestamps[ids_point]
        coords = points.coords[ids_point][:,:2]
        
  
        
        get_ids_edge_from_inds_seg = edges.get_ids_edge_from_inds_seg
        get_dist_point_to_edge = edges.get_dist_point_to_edge
        get_closest_edge = edges.get_closest_edge

        ##---------------------------------------------------------------------
        ## 1. initialization
        
        # Set of initial edges 

        # create a list with lists  for each point
        # Each list contans the edges, where the point is in the edge buffer

        ids_stop_initial = []
        ids_stop_final = []
        dists_initial = []
        n_points = len(ids_point)
        ind_point_initial = -1
        ind_point_final = -1
        ind_hit_initial = -1
        ind_hit_final = -1
        t_point_initial = -1
        t_point_final = -1
        
        print('  search initial stops')
        ind_point = 0
        for p in coords:
            dists2 = np.sum((coords_stop-p)**2,1)
            #inds_hit = np.flatnonzero(dists2 < self.width_buffer_terminal_max**2)
            ind_hit = np.argmin(dists2)
            if dists2[ind_hit]<self.width_buffer_terminal_max**2:#len(inds_hit)>0:
                # TODO: here we could check whether the point stays near
                # an edge used by public transport
                if ind_point_initial<0:
                    #print '    set initial stops',ids_stop[inds_hit]
                    #print '      inds_hit',ind_point,inds_hit
                    #print '        len(ids_stop)',len(ids_stop)
                    ind_hit_initial = ind_hit
                    #ids_stop_initial = ids_stop[ind_hit]
                    #print '      ids_edge_initial',ids_edge_initial
                    #dists_initial = np.sqrt(dists2[inds_hit])
                    ind_point_initial = ind_point
                    t_point_initial = pointtimes[ind_point]
                    #print '      t_point_initial',t_point_initial
                    #print '   ind_point, inds_hit, ind_point_initial',ind_point,inds_hit,ind_point_initial,ids_edge_initial
                else:
                    #print '    set final stops',ids_stop[inds_hit]
                    ind_hit_final = ind_hit
                    #ids_stop_final = ids_stop[ind_hit]
                    #print '      ids_edge_final',ids_edge_final
                    ind_point_final = ind_point
                    t_point_final = pointtimes[ind_point]
                    #print '      ind_point_final',ind_point_final
                    #print '   ind_point, inds_hit, ind_point_final',ind_point,inds_hit,ind_point_initial,ids_edge_final
            
            ind_point +=1
        
        # consider also stops around the initial and final stops
        if ind_hit_initial > -1:
            dists2 = np.sum((coords_stop-coords_stop[ind_hit_initial])**2,1)
            inds_hit = np.flatnonzero(dists2 < self.width_buffer_terminal_max**2)
            for id_stop in ids_stop[inds_hit]:
                if id_stop in map_id_fromstop_to_ids_link:
                    ids_stop_initial.append(id_stop) 
        else:
            print('ABOARD: no initial stop found') 
            return [], [], 0.0, 0.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
            
        if ind_hit_final > -1:
            dists2 = np.sum((coords_stop-coords_stop[ind_hit_final])**2,1)
            inds_hit = np.flatnonzero(dists2 < self.width_buffer_terminal_max**2)
            for id_stop in ids_stop[inds_hit]:
                if id_stop in map_id_tostop_to_ids_link:
                    ids_stop_final.append(id_stop) 
                    

        else:
            print('ABOARD: no final stop found') 
            return [], [], 0.0, 0.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
        
        n_points_eff = ind_point_final - ind_point_initial
        
        duration_gps = pointtimes[ind_point_final] - pointtimes[ind_point_initial]
        
        #if self._logger:
        #    self._logger.w( '>>match_trip_birgil : n_points_eff=%d, len(ids_edge_initial)=%d,len(ids_edge_final)=%d'%(n_points_eff, len(ids_edge_initial),len(ids_edge_final)) )
        print('\n  completed init:')
        print('   ids_stop_initial',ids_stop_initial)
        print('   ids_stop_final',ids_stop_final)
        
        #print '  ind_point_initial,ind_point_final,n_points_eff',ind_point_initial,ind_point_final,n_points_eff
        print('  id_point_initial=%d,id_point_final=%d,n_points_eff=%d'%(ids_point[ind_point_initial],ids_point[ind_point_final],n_points_eff))
        if (ind_point_initial<0)|(ind_point_final<0) | (n_points_eff < self.n_points_min):
            print('ABOARD: insufficient valid points') 
            return [], [], 0.0, 0.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
        

        print('  define initial and final pt links')
        
        ids_ptlink_initial = set()
        for id_stop  in ids_stop_initial:
            ids_ptlink_initial.update(map_id_fromstop_to_ids_link[id_stop])
        
        ids_ptlink_final = set()
        for id_stop  in ids_stop_final:
            ids_ptlink_final.update(map_id_tostop_to_ids_link[id_stop])
            
        print('   ids_ptlink_initial',ids_ptlink_initial)
        print('   ids_ptlink_final',ids_ptlink_final)
        
        
        ##---------------------------------------------------------------------
        ## 2. main loop through rest of the points
        
        length_gps = 0.0
        
        # for debugging
        waittimes_stops = np.zeros(len(ids_stop)+1,dtype = np.float32)
        #route_best_disconnected = [np.inf, np.inf,np.inf,[],[],-1,[]]
        
        
        ind_point = ind_point_initial+1
        while (ind_point <= ind_point_final):# & is_connected:
            is_disconnected = True
            
            point = coords[ind_point]
            id_point = ids_point[ind_point]
            delta_point = point-coords[ind_point-1]
            phi_point = np.arctan2(delta_point[1], delta_point[0])
            
            print(79*'=')
            print('    point ind_point',ind_point,ind_point_final,' id_point',id_point,'coords',point)
            
            ids_edge_buffer, dists = get_closest_edge( point, n_best = self.n_edge_max, 
                                                d_max = self.width_buffer_max,
                                                is_ending = True, 
                                                is_detect_initial = False, 
                                                is_detect_final = False,
                                                accesslevels = accesslevels)
            
            
            
            n_hits = len(ids_edge_buffer)
            print('    n_hits',n_hits,'ids_edge_buffer',ids_edge_buffer)
            if n_hits>0:
                phis_delta = np.zeros(n_hits, dtype = np.float32)
                for ind, id_edge in zip(range(n_hits), ids_edge_buffer):
                    
                    dist_point_edge, segment = get_dist_point_to_edge(point, id_edge, 
                                                is_ending=True,
                                                is_detect_initial = False,
                                                is_detect_final = False,
                                                is_return_segment = True,
                                                )
                    x1,y1,x2,y2 = segment
                    phi_seg =  np.arctan2(y2-y1,x2-x1)
                    phis_delta[ind] = anglediff(phi_point,phi_seg)
                    #print '    Dir id_edge',id_edge,'phi_seg %.2f'%(phi_seg*180/np.pi),'phi_point %.2f'%(phi_point*180/np.pi),'phi_delta %.2f'%(phis_delta[ind]*180/np.pi),'cos %.2f'%(np.cos(phis_delta[ind]))
                
                edgehits[ids_edge_buffer] += np.cos(phis_delta)/n_hits
            
            # points near stops reduce wait times
            ids_stops_near = ids_stop[np.sum((coords_stop-point)**2,1)<(self.width_buffer_max)**2]
            n_near = len(ids_stops_near)
            #print '      near stops',n_near
            for id_stop in ids_stops_near:
                t_wait_point = min(pointtimes[ind_point]-pointtimes[ind_point-1],600.0)
                #print '      near id_stop',id_stop,map_id_fromstop_to_ids_link.has_key(id_stop)
                if id_stop in map_id_fromstop_to_ids_link:
                    waittimes_stops[id_stop] += t_wait_point
                    #ptlinkhits[map_id_fromstop_to_ids_link[id_stop]] += self.weight_stop_hits/n_near
                    pttimes[map_id_fromstop_to_ids_link[id_stop]] -= self.weight_stop_hits*t_wait_point#/n_near
                
                    
            ind_point +=1
            
        for id_ptlink in ptlinks.get_ids():
            ids_ptedge = ptlinks.get_ids_edge(id_ptlink)
            ptlinkhits[id_ptlink] += np.sum(edgehits[ids_ptedge])
        
        if 0: #debug
            print('    Stop waittimes')
            for id_stop, waits in zip(ids_stop, waittimes_stops[ids_stop]):
                if waits>0:
                    print('      waits at id_stop %d = %.2fs'%(id_stop,waits))
                    
            # debug edgelinks
            #ids_edge = edges.get_ids()
            #for id_edge, edgehit in zip(ids_edge, edgehits[ids_edge]):
            #    if edgehit>0:
            #        print '  id_edge',id_edge,'edgehit',edgehit
            
        # regenerate pttimes for each trip  with random wait times
        pttimes -= self.weight_hits * ptlinkhits
        
            
        routelist = []
        for id_ptlink_initial in ids_ptlink_initial:
            for id_ptlink_final in ids_ptlink_final:
                
                routeduration, route = routing.get_mincostroute_edge2edge(\
                                            id_ptlink_initial, id_ptlink_final,
                                            weights = pttimes, 
                                            fstar = ptfstar
                                            )
                #print '  route id_ptlink_initial',id_ptlink_initial,'> ids_ptlink_final',id_ptlink_final,'cost_tot=%.2f'%routeduration
                if (len(route)>0)&(len(route)==len(set(route))):
                    routelist.append((routeduration, route))
                    # debug
                    #for id_ptlink in route:
                    #    ptlinks.print_link(id_ptlink, ident = 4, is_edges = False)
                
        
        routelist.sort()
        #print '  routelist',routelist
        
        
        ##--------------------------------------------------------------------
        ## post matching analisis
        print('\n'+79*'-')
        
        #print '  ids_point_edgeend',len(ids_point_edgeend),ids_point_edgeend
        if len(routelist) == 0:
            print('ABOARD: no routes found')
            return [],[], 0.0, 0.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
        else:
            ids_ptlink = routelist[0][1]
            if len(ids_ptlink) == 0:
                print('ABOARD: route contains no edges ids_ptlink=',ids_ptlink)
                return [],[], 0.0, 0.0,-1.0,-1.0, -1.0, -1.0, 0.0, [], False
            else:
                is_connected = True
                ids_edge = []
                for id_ptlink in ids_ptlink:
                    ids_edge += ptlinks.get_ids_edge(id_ptlink)
                route = ids_edge
                
        print('  matched route: len(ids_edge)',len(ids_edge),'len(ids_ptlink)',len(ids_ptlink),'cost =%.2f'%routelist[0][0])
        
        if 1:
            for id_ptlink in ids_ptlink:
                ptlinks.print_link(id_ptlink, ident = 4, is_edges = False, is_link_forward = False)
            print('                               ids_edge',ids_edge)
            
                
            
        length_gps = np.sum(np.sqrt(np.sum((coords[ind_point_initial+1:ind_point_final]-coords[ind_point_initial:ind_point_final-1])**2,1)))
                     
        length_route =  np.sum(busdistances[ids_edge]) 
        
        length_route_mixed = 0.0
        length_route_exclusive = 0.0
        for id_edge, length in zip(ids_edge,busdistances[ids_edge]):
            accesslevel = accesslevels[id_edge]
            if accesslevel ==1:
                length_route_mixed += length
            elif accesslevel ==2:
                length_route_exclusive += length
            
        if length_gps>0:
            lengthindex = length_route/length_gps
        else:
            lengthindex = -1.0
        
            
        
        
        
        # check dist error
        inds_point = range(ind_point_initial,ind_point_final)
        n_points = len(inds_point)
        
        
        if (n_points >= 2)&(len(route)>0):
            #print '  initialize distance error measurement',len(route)
            dist_points_tot = 0.0 
            routestring = get_routelinestring(route, edges)
            #print '  routestring =',routestring 
            
            #print '  coords',coords[inds_point].tolist()
            # TODO: avoid using Multipoins from shapely package
            tracepoints = MultiPoint(coords[inds_point].tolist())
        
            # measure precision
            #routeset = set(route)
            #n_points_boundary = 0
            ind_tracepoint = 0
            for ind_point in inds_point:#xrange(n_points):
                d = routestring.distance(tracepoints.geoms[ind_tracepoint])
                dist_points_tot += d
                #print '   v distance measurement',d,tracepoints.geoms[ind_tracepoint],coords[ind_point]#,n_points#,n_points_eff
                
                ind_tracepoint += 1
            
            err_dist = dist_points_tot/float(n_points) 
        
        if ptlinks.ids_tostop[ids_ptlink[-1]] in ids_stop_final:
            print('SUCCESS: target id_stop',ptlinks.ids_tostop[ids_ptlink[-1]],'reached length %.2f, gps length %.2f$'%(length_route,length_gps))
            is_connected = True
        else:
            print('DISCONNECTED: last matched id_stop',ptlinks.ids_tostop[ids_ptlink[-1]],' does not reach final ids_stop',ids_stop_final)
            is_connected = False
             
             
        #print '  check',(lengthindex<self.lengthindex_max),(lengthindex >= self.lengthindex_min),(duration_gps>self.duration_min),(length_route>self.dist_min),(len(route)>0)
        #print '   twice',(lengthindex<self.lengthindex_max)&(lengthindex >= self.lengthindex_min)&(duration_gps>self.duration_min)&(length_route>self.dist_min)&(len(route)>0)                    
        ids_point_edgeend = [] # TODO
        return ids_ptlink, route, length_route, length_route_mixed,length_route_exclusive, duration_gps, lengthindex, err_dist, t_match,ids_point_edgeend ,is_connected
    
                
    


def get_colvalue(val, default = 0.0):
    
    if (len(val)>0) & (val != 'NULL'):
        return float(val)
    else:
        return default
 
 
class VirtualpopCreator(Process):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('VirtualpopCreator.__init__')
        self._init_common(  'virtualpopcreator', 
                            parent = mapmatching,
                            name = 'VirtualpopCreator', 
                            logger = logger,
                            info ='Uses person information to create a virtual population.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        choices = scenario.demand.vtypes.get_modechoices()
        
        self.c_popmultiplier = attrsman.add(cm.AttrConf( 'c_popmultiplier',kwargs.get('c_popmultiplier',1.0),
                            groupnames = ['options'], 
                            name = 'Polupation multiplier', 
                            info = 'Number of person in mapmatching database is multiplied by this factor.',
                            ))
                            
        self.id_mode_select = attrsman.add(cm.AttrConf( 'id_mode_select',kwargs.get('id_mode_select',choices['pedestrian']),
                            groupnames = ['options'], 
                            choices = choices,
                            name = 'Select mode', 
                            info = 'Mode to select.',
                            ))
                            
    def do(self):
        mapmatching = self.parent
        trips = mapmatching.trips
        ids_selected = trips.get_ids_selected()
        scenario = mapmatching.get_scenario()
        vtypes = scenario.demand.vtypes
        
        inds_unselect =  np.logical_not(vtypes.ids_mode[trips.ids_vtype[ids_selected]] == self.id_mode_select)
        
        trips.are_selected[ids_selected[inds_unselect]] = False
        return True
    
class ModeSelector(Process):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('ModeSelector.__init__')
        self._init_common(  'modeselector', 
                            parent = mapmatching,
                            name = 'Mode Selector', 
                            logger = logger,
                            info ='Select only traces with specified mode.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        choices = scenario.demand.vtypes.get_modechoices()
        
        self.id_mode_select = attrsman.add(cm.AttrConf( 'id_mode_select',kwargs.get('id_mode_select',choices['pedestrian']),
                            groupnames = ['options'], 
                            choices = choices,
                            name = 'Select mode', 
                            info = 'Mode to select.',
                            ))
                            
    def do(self):
        mapmatching = self.parent
        trips = mapmatching.trips
        ids_selected = trips.get_ids_selected()
        scenario = mapmatching.get_scenario()
        vtypes = scenario.demand.vtypes
        
        inds_unselect =  np.logical_not(vtypes.ids_mode[trips.ids_vtype[ids_selected]] == self.id_mode_select)
        
        trips.are_selected[ids_selected[inds_unselect]] = False
        return True
        
    
    
class FilterMixin(Process):
    def _init_filter_preview(self):
        attrsman = self.get_attrsman()
        trips = self.parent.trips
        self.filterresults = attrsman.add(cm.FuncConf(  'filterresults', 
                                                        'filterpreview',# function attribute of object 
                                                        '%d/%d'%(len(trips.get_ids_selected()),len(trips)),
                                                        name = 'Preview selected trips',
                                                        groupnames = ['options','results'], 
                                                        ))
           
    def filterpreview(self):
        """
        Previews selected trips after filtering.
        """
        trips = self.parent.trips
        n_trips = len(trips)
        n_sel_current = len(trips.get_ids_selected())
        n_sel_eliminate = len(self.filter_ids())
        n_sel_after = n_sel_current-n_sel_eliminate
        return '%d/%d (currently %d/%d)'%(n_sel_after,n_trips,n_sel_current,n_trips)
    
    def _init_traceoptions(self, **kwargs):
        attrsman = self.get_attrsman()
        self.dist_trip_min = attrsman.add(cm.AttrConf( 'dist_trip_min',kwargs.get('dist_trip_min',100.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. trip distance', 
                            unit = 'm',
                            info = 'Minimum distance of one trip. Shorter trips will not be selected.',
                            ))
        
        self.dist_trip_max = attrsman.add(cm.AttrConf( 'dist_trip_max',kwargs.get('dist_trip_max',25000.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. trip distance', 
                            unit = 'm',
                            info = 'Maximum distance of one trip. Shorter trips will not be selected.',
                            ))
                                                                    
        self.duration_trip_min = attrsman.add(cm.AttrConf( 'duration_trip_min',kwargs.get('duration_trip_min',30.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. trip duration', 
                            unit = 's',
                            info = 'Minimum duration of one trip. Trips with shorter duration will not be selected.',
                            ))
        
        self.duration_trip_max = attrsman.add(cm.AttrConf( 'duration_trip_max',kwargs.get('duration_trip_max',999999.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. trip duration', 
                            unit = 's',
                            info = 'Maximum duration of one trip. Trips with longer duration will not be selected.',
                            ))
                            
        self.speed_trip_min = attrsman.add(cm.AttrConf( 'speed_trip_min',kwargs.get('speed_trip_min',1.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. av. trip speed', 
                            unit = 'm/s',
                            info = 'Minimum average trip speed. Trips with lower average speed will not be selected.',
                            ))
                            
        self.speed_trip_max = attrsman.add(cm.AttrConf( 'speed_trip_max',kwargs.get('speed_trip_max',14.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. av. trip speed', 
                            unit = 'm/s',
                            info = 'Maximum average trip speed. Trips with higher average speed will not be selected.',
                            ))
    
    def _init_filter_time(self, **kwargs):
        attrsman = self.get_attrsman()
        
        self.hour_from_morning = attrsman.add(cm.AttrConf( 'hour_from_morning',kwargs.get('hour_from_morning',0.),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'From morning hour', 
                            unit = 'h',
                            info = 'Keep only morning trips which start after this hour. Non-inner numbers are allowed.',
                            ))
        
        self.hour_to_morning = attrsman.add(cm.AttrConf( 'hour_to_morning',kwargs.get('hour_to_morning',12.),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'To morning hour', 
                            unit = 'h',
                            info = 'Keep only morning trips which start before this hour. Non-inner numbers are allowed.',
                            ))
                                                
        self.hour_from_evening = attrsman.add(cm.AttrConf( 'hour_from_evening',kwargs.get('hour_from_evening',12.),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'From evening hour', 
                            unit = 'h',
                            info = 'Keep only evening trips which start after this hour. Non-inner numbers are allowed.',
                            ))
        
        self.hour_to_evening = attrsman.add(cm.AttrConf( 'hour_to_evening',kwargs.get('hour_to_evening',24.),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'To evening hour', 
                            unit = 'h',
                            info = 'Keep only evening trips which start before this hour. Non-inner numbers are allowed.',
                            ))
                                  
        
        
        self.weekdays = attrsman.add(cm.AttrConf( 'weekdays',kwargs.get('weekdays',WEEKDAYCHOICES['all']),
                            groupnames = ['options'], 
                            name = 'Weekdays', 
                            choices = WEEKDAYCHOICES,
                            info = 'Keep only trips at the given weekdays.',
                            ))
        self.weekday = attrsman.add(cm.AttrConf( 'weekday',kwargs.get('weekday',WEEKDAYCHOICE['all']),
                            groupnames = ['options'], 
                            name = 'Weekday', 
                            choices = WEEKDAYCHOICE,
                            info = 'Keep only trips at the given weekday.',
                            ))
                            
        
                            
        self.is_select_month = attrsman.add(cm.AttrConf( 'is_select_month',kwargs.get('is_select_month', False),
                            groupnames = ['options'], 
                            name = 'Select month', 
                            info = 'Use Month selection mode.',
                            ))
                            
        self.month = attrsman.add(cm.AttrConf( 'month',kwargs.get('month',MONTHS['October']),
                            groupnames = ['options'], 
                            perm='rw',
                            name = 'Months',
                            choices = MONTHS, 
                            info = 'Keep only trips at the given month: 1 for january and 12 for december',
                            ))   
        
        self.is_select_year = attrsman.add(cm.AttrConf( 'is_select_year',kwargs.get('is_select_year', False),
                            groupnames = ['options'], 
                            name = 'Select year', 
                            info = 'Use year selection mode.',
                            ))
                                                
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year', 2018),
                            groupnames = ['options'], 
                            perm='rw',
                            name = 'Year',
                            info = 'Keep only trips at the given Year',
                            )) 
                            
        self.is_select_day = attrsman.add(cm.AttrConf( 'is_select_day',kwargs.get('is_select_day', False),
                            groupnames = ['options'], 
                            name = 'Day', 
                            info = 'Keep only trips at the given Day.',
                            ))
                            
        self.day = attrsman.add(cm.AttrConf( 'day',kwargs.get('day',DAYS['1']),
                            groupnames = ['options'], 
                            perm='rw',
                            name = 'Days',
                            choices = DAYS, 
                            info = 'Keep only trips at the given day',
                            ))     
  
    def filter_time(self, timestamps):
        """
        timestamps is an array with timestamps.
        Function returns a binary array, with a True value for each
        time stamp that does NOT SATISFY the specified time constrants.  
        """
        #print 'filter_time'
        #print '  self.hour_from_morning,self.hour_to_morning',self.hour_from_morning,self.hour_to_morning
        localtime = time.localtime
        inds_elim = np.zeros(len(timestamps), dtype = np.bool_)
        i = 0
        for timestamp in timestamps:
            
            dt = localtime(timestamp)
            is_keep = dt.tm_wday in self.weekdays
            is_keep &= dt.tm_wday in self.weekday
            if self.is_select_month:
                is_keep &= dt.tm_mon == self.month
            if self.is_select_day:
                is_keep &= dt.tm_mday == self.day    
                
            #print '   dt.tm_wday,self.weekdays',dt.tm_wday,self.weekdays
            h = dt.tm_hour+dt.tm_min/60.+dt.tm_sec/3600.
            is_keep &=  (  h >= self.hour_from_morning) & (h < self.hour_to_morning)\
                        | (h >= self.hour_from_evening) & (h < self.hour_to_evening)
                  
            #print '  is_keep,w,h=',is_keep,dt.tm_wday,h
            inds_elim[i] = not is_keep
            i += 1
            
        return  inds_elim     
    
    def is_timestamp_ok(self, timestamp):
        #if is_daytimesaving:
        dt = time.localtime(timestamp)
        #else:
        #    dt = time.gmtime(timestamp)
        is_ok = dt.tm_wday in self.weekdays
        is_ok &= dt.tm_wday in self.weekday
        
        if self.is_select_year:
            is_ok &= dt.tm_year == self.year
            
        if self.is_select_month:
            is_ok &= dt.tm_mon == self.month
            
        h = dt.tm_hour+dt.tm_min/60.+dt.tm_sec/3600.
        #print 'is_timestamp_ok h,dt.tm_wday',h,dt.tm_wday,is_ok,
        
        is_ok &=  (  h >= self.hour_from_morning) & (h < self.hour_to_morning)\
                    | (h >= self.hour_from_evening) & (h < self.hour_to_evening)
        
        #print is_ok
        return  is_ok
            
    def filter_ids(self):
        """
        Returns an array of ids to be eliminated or deselected.
        To be overridden
        """
        return []
    
class PostMatchfilter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('PostMatchfilter.__init__')
        self._init_common(  'postmatchfilter', 
                            parent = mapmatching,
                            name = 'Post matchfilter', 
                            logger = logger,
                            info ='Removes matched tripe with defined characteristics from current selection.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        info_lengthindex = "Length index is length of matched route divided by length of line interpolated GPS points in percent."
        self.lengthindex_min = attrsman.add(cm.AttrConf( 'lengthindex_min',kwargs.get('lengthindex_min',80.0),
                            groupnames = ['options'], 
                            name = 'Min. length index', 
                            unit = '%',
                            info = 'Minimum allowed length index.'+info_lengthindex,
                            ))
        self.lengthindex_max = attrsman.add(cm.AttrConf( 'lengthindex_max',kwargs.get('lengthindex_max',110.0),
                            groupnames = ['options'], 
                            name = 'Max. length index', 
                            unit = '%',
                            info = 'Maximum allowed length index '+info_lengthindex,
                            ))                    
        
        
        info_error_dist = 'The distance error is the average distance between the GPS points and the matched route.'
        self.error_dist_max = attrsman.add(cm.AttrConf( 'error_dist_max',kwargs.get('error_dist_max',10000.0),
                            groupnames = ['options'], 
                            name = 'Max. distance err.', 
                            unit = 'mm',
                            info = 'Maximum allowed distance error. '+info_error_dist,
                            ))
        
        
        
        self.is_connected = attrsman.add(cm.AttrConf( 'is_connected',kwargs.get('is_connected',True),
                            groupnames = ['options'], 
                            name = 'Must be connected', 
                            info = 'Matched route must connect first and last identified network edge of trace. Option required for route analyses.',
                            ))

                            
        self.is_loopfree= attrsman.add(cm.AttrConf( 'is_loopfree',kwargs.get('is_loopfree',False),
                            groupnames = ['options'], 
                            name = 'Must be loop-free', 
                            info = 'Matched route must be free of loops, that means all edges occur only once. Option required for alternative route analyses.',
                            ))
        
        self.is_shortest = attrsman.add(cm.AttrConf( 'is_shortest',kwargs.get('is_shortest',False),
                            groupnames = ['options'], 
                            name = 'Must have shortest', 
                            info = 'Matched route must have an existing shortest route. Option required for route analyses.',
                            ))
                            
        self.is_matched = attrsman.add(cm.AttrConf( 'is_matched',kwargs.get('is_matched',True),
                            groupnames = ['options'], 
                            name = 'Must be matched', 
                            info = 'Trips must be matched. Option required for route analyses.',
                            ))
                                           

                                        
        #self._init_traceoptions(**kwargs)
        self.speed_trip_min = attrsman.add(cm.AttrConf( 'speed_trip_min',kwargs.get('speed_trip_min',1.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. av. trip speed', 
                            unit = 'm/s',
                            info = 'Minimum average trip speed. Trips with lower average speed will not be selected.',
                            ))
                            
        self.speed_trip_max = attrsman.add(cm.AttrConf( 'speed_trip_max',kwargs.get('speed_trip_max',14.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. av. trip speed', 
                            unit = 'm/s',
                            info = 'Maximum average trip speed. Trips with higher average speed will not be selected.',
                            ))
                            
        self._init_filter_time(**kwargs)       
                            
            
        self._init_filter_preview()
        
    
    
    def filter_ids(self):
        """
        Returns an array of ids to be eliminated or deselected.
        """
        
        trips = self.parent.trips
        routes = self.parent.trips.routes.get_value()
        ids_selected = trips.get_ids_selected()
        mapmatching = self.parent.parent
        scenario = mapmatching.get_scenario()
        points = scenario.demand.mapmatching.points
        persons = scenario.demand.mapmatching.persons
        ids_person = persons.get_ids()
        zones = scenario.landuse.zones
        n_trips = len(ids_selected)
        ids_trip = trips.get_ids()
        ids_points = trips.ids_points[ids_trip]
        
        print('filter_ids',n_trips,'selected')
        inds_eliminate = np.logical_or(\
            trips.lengthindexes[ids_selected]<self.lengthindex_min,
            trips.lengthindexes[ids_selected]>self.lengthindex_max,
            #trips.errors_dist[ids_selected]>self.error_dist_max,
            #np.logical_not(trips.are_match_connected[ids_selected]),
            #(trips.lengths_route_matched[ids_selected]<1.0), # too many args??
            )
        print('  after lengthindex remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
                    
        
        inds_eliminate |= trips.errors_dist[ids_selected]>self.error_dist_max
        print('  after distance error remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
        
             
        if self.is_connected:
            inds_eliminate |= np.logical_not(trips.are_match_connected[ids_selected])
        
        print('  after connected remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
            
        if self.is_shortest:
            inds_eliminate |= trips.ids_route_shortest[ids_selected] == -1
        if self.is_matched:    
            inds_eliminate |= trips.ids_route_matched[ids_selected] == -1
            print('  after is matched remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
        
            inds_eliminate |= trips.lengths_route_matched[ids_selected]<1.0
            print('  after too short trips remaining:',n_trips-len(np.flatnonzero(inds_eliminate)))
            

        inds_eliminate |= trips.speeds_average[ids_selected] < self.speed_trip_min
        inds_eliminate |= trips.speeds_average[ids_selected] > self.speed_trip_max
        print('  after speed check remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
        

 
                            
        #dist > self.dist_trip_min)\
        #           & (dist < self.dist_trip_max)\
        #           & (duration > self.duration_trip_min)\
        #           & (speed_av > self.speed_trip_min)\
        #           & (speed_av < self.speed_trip_max)
               
        
        #print '  lengths_route_matched',trips.lengths_route_matched[ids_selected]
        #print '  lengthindexes',self.lengthindex_min,self.lengthindex_max,trips.lengthindexes[ids_selected]
        #print '  errors_dist',self.error_dist_max,trips.errors_dist[ids_selected]
        #print '  lengthindex_min',trips.lengthindexes[ids_selected]<self.lengthindex_min
        #print '  lengthindex_max',trips.lengthindexes[ids_selected]>self.lengthindex_max
        #print '  lengths_route_matched',trips.errors_dist[ids_selected]>self.error_dist_max
        #print '  inds_eliminate',inds_eliminate
        
        inds_eliminate |= self.filter_time(trips.timestamps[ids_selected])
        print('  after time check trips remaining:',n_trips-len(np.flatnonzero(inds_eliminate)))
        #print '  inds_eliminate',inds_eliminate
        
        if self.is_loopfree:
            i = 0
            for ids_edge, ind_keep in zip( routes.ids_edges[trips.ids_route_matched[ids_selected]],np.logical_not(inds_eliminate)):
                if ind_keep:
                    # loop-free check
                    inds_eliminate[i] = len(ids_edge) != len(set(ids_edge))
                i += 1
        print('  after loop test trips remaining:',n_trips-len(np.flatnonzero(inds_eliminate)))   

        return ids_selected[inds_eliminate] 
    
    def do(self):
        
        # execute filtering
        self.parent.trips.are_selected[self.filter_ids()] = False
                             
        return True
            
        
class PersonFilter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('Personfilter.__init__')
        self._init_common(  'personfilter', 
                            parent = mapmatching,
                            name = 'Person filter', 
                            logger = logger,
                            info ='Select trips by different user parameters.\
        This should be done after the person analysis process.',
                            )
        
        attrsman = self.set_attrsman(cm.Attrsman(self))              
                            
        mapmatching = self.parent.parent
        scenario = mapmatching.get_scenario()  

                                           
        self.is_select_gender = attrsman.add(cm.AttrConf( 'is_select_gender',kwargs.get('is_select_gender',False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Select persons gender',
                            info = 'Select only trips with users of the selected gender.',
                            ))  
                            
        self.gender = attrsman.add(cm.AttrConf(  'gender', kwargs.get('gender', GENDERS['male']),
                                        groupnames = ['options'], 
                                        choices = GENDERS,
                                        name = 'gender of cyclists', 
                                        info = 'select the gender of users.',
                                        )) 
        self.is_select_age = attrsman.add(cm.AttrConf( 'is_select_age',kwargs.get('is_select_age',False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Select persons age',
                            info = 'Select only trips with users of the selected age.',
                            ))  
                            
        self.birth_min = attrsman.add(cm.AttrConf(  'birth_min', kwargs.get('birth_min', 1990),
                                        groupnames = ['options'], 
                                        name = 'minimum year of birth of cyclists', 
                                        info = 'select the minimum year of birth of users.',
                                        )) 
                                        
        self.birth_max = attrsman.add(cm.AttrConf(  'birth_max', kwargs.get('birth_max', 2000),
                                        groupnames = ['options'], 
                                        name = 'maximum year of birth of cyclists', 
                                        info = 'select the maximum year of birth of users.',
                                        ))       
                            
        self.zones = scenario.landuse.zones
        if len(self.zones)>0:      
            self.is_select_od_trips = attrsman.add(cm.AttrConf( 'is_select_od_trips',kwargs.get('is_select_od_trips',False),
                                groupnames = ['options'], 
                                perm='rw', 
                                name = 'Select OD trips', 
                                info = 'Select only trips from the origin to the destination zone.',
                                ))  
            self.is_select_gen_trips = attrsman.add(cm.AttrConf( 'is_select_gen_trips',kwargs.get('is_select_gen_trips',False),
                                groupnames = ['options'], 
                                perm='rw', 
                                name = 'Select generated trips', 
                                info = 'Select only trips which start from the origin zone.',
                                ))  
            self.is_select_attr_trips = attrsman.add(cm.AttrConf( 'is_select_attr_trips',kwargs.get('is_select_attr_trips',False),
                                groupnames = ['options'], 
                                perm='rw', 
                                name = 'Select attracted trips', 
                                info = 'Select only trips which arrive to the destination zone.',
                                ))                              
                  
            self.origin_zone_name = attrsman.add(cm.AttrConf(  'origin_zone_name', kwargs.get('origin_zone_name', self.zones.ids_sumo[0]),
                                            groupnames = ['options'], 
                                            choices = self.zones.ids_sumo,
                                            name = 'Origin zone name', 
                                            info = 'Generating zone name.',
                                            ))
            self.dest_zone_name = attrsman.add(cm.AttrConf(  'dest_zone_name', kwargs.get('dest_zone_name', self.zones.ids_sumo[0]),
                                            groupnames = ['options'], 
                                            choices = self.zones.ids_sumo,
                                            name = 'Destination zone name', 
                                            info = 'Attracting zone name.',
                                            ))
    
                              
        self._init_filter_preview()
        
    
    
    def filter_ids(self):
        """
        Returns an array of ids to be eliminated or deselected.
        """
        
            
        trips = self.parent.trips
        routes = self.parent.trips.routes.get_value()
        ids_selected = trips.get_ids_selected()
        mapmatching = self.parent.parent
        scenario = mapmatching.get_scenario()
        points = scenario.demand.mapmatching.points
        persons = scenario.demand.mapmatching.persons
        ids_person = persons.get_ids()
        zones = scenario.landuse.zones
        n_trips = len(ids_selected)
        ids_trip = trips.get_ids()
        ids_points = trips.ids_points[ids_trip]
        
        print('filter_ids',n_trips,'selected')  
        inds_eliminate = np.zeros(len(trips.ids_vtype[ids_selected]), dtype=bool)

        if self.is_select_gender:
            inds_eliminate |= persons.ids_gender[trips.ids_person[ids_selected]] != self.gender 
            print('  after gender check remaining',n_trips-len(np.flatnonzero(inds_eliminate)))
            
        if self.is_select_age:
            inds_eliminate |= np.logical_or(\
                persons.years_birth[trips.ids_person[ids_selected]]<=self.birth_min,
                persons.years_birth[trips.ids_person[ids_selected]]>=self.birth_max,) 
            print('  after age check remaining',n_trips-len(np.flatnonzero(inds_eliminate)))

        if len(self.zones)>0:
            if self.is_select_od_trips:
                zone_shape_origin = zones.shapes[zones.ids_sumo.get_id_from_index(self.origin_zone_name)]
                zone_shape_dest = zones.shapes[zones.ids_sumo.get_id_from_index(self.dest_zone_name)]          
                od_trips = np.zeros(np.max(ids_trip+1))
                for  id_trip, ids_point in zip(ids_selected,ids_points):
                    id_final_point = ids_point[-1]
                    id_initial_point = ids_point[0]
                    od_trips[id_trip] =  is_point_in_polygon(points.coords[id_initial_point], zone_shape_origin)*is_point_in_polygon(points.coords[id_final_point], zone_shape_dest)
                inds_eliminate |= np.logical_not(od_trips[ids_selected])
                print(' after od trips remaining ',n_trips-len(np.flatnonzero(inds_eliminate))) 
                    
            if self.is_select_gen_trips:
                zone_shape_origin = zones.shapes[zones.ids_sumo.get_id_from_index(self.origin_zone_name)]
                od_trips = np.zeros(np.max(ids_trip+1))
                for  id_trip, ids_point in zip(ids_selected,ids_points):
                    id_initial_point = ids_point[0]
                    od_trips[id_trip] =  is_point_in_polygon(points.coords[id_initial_point], zone_shape_origin)
                inds_eliminate |= np.logical_not(od_trips[ids_selected])
                print(' after generated trips remaining ',n_trips-len(np.flatnonzero(inds_eliminate)))      
                  
            if self.is_select_attr_trips:
                zone_shape_dest = zones.shapes[zones.ids_sumo.get_id_from_index(self.dest_zone_name)]          
                od_trips = np.zeros(np.max(ids_trip+1))
                for  id_trip, ids_point in zip(ids_selected,ids_points):
                    id_final_point = ids_point[-1]
                    od_trips[id_trip] = is_point_in_polygon(points.coords[id_final_point], zone_shape_dest)
                inds_eliminate |= np.logical_not(od_trips[ids_selected])
                print(' after attracted trips remaining ',n_trips-len(np.flatnonzero(inds_eliminate)))      

        return ids_selected[inds_eliminate] 
    
    def do(self):
        
        # execute filtering
        self.parent.trips.are_selected[self.filter_ids()] = False
                             
        return True
    
    
SELECTTRACES = {'FromOrigin':1,
                'ToDestination':2,
                'FromOriginToDestination':3,
                }
           
class TripGeomfilter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('TripGeomfilter.__init__')
        self._init_common(  'tripGeomfilter', 
                            parent = mapmatching,
                            name = 'Geometry trip filter', 
                            logger = logger,
                            info ='Removes trips with defined geometric characteristics from current selection.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        
        self.is_analyze_points = attrsman.add(cm.AttrConf( 'is_analyze_points',kwargs.get('is_analyze_points',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Point filter', 
                            info = 'Delete invalid points and deselect traces if all their points have been deleted.',
                            ))
                            
        self.is_eliminate_external_points = attrsman.add(cm.AttrConf( 'is_eliminate_external_points',kwargs.get('is_eliminate_external_points',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Delete external points', 
                            info = 'Delete external points for two main reason: 1) The spatial coordinate is usually not precise if the user have just started the GPS 2) Users have to lock and unlock the bike',
                            ))              
        
        self.num_external_points = attrsman.add(cm.AttrConf( 'num_external_points',kwargs.get('num_external_points',2),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Num. Ext. Points.', 
                            info = 'Eliminate this number of points from both the begin and the end of the trace',
                            ))
                                        
        self.is_eliminate_close_points = attrsman.add(cm.AttrConf( 'is_eliminate_close_points',kwargs.get('is_eliminate_close_points',False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Eliminate close points', 
                            info = 'Eliminate points with a distance less then the minimum distance.',
                            ))
                            
        self.dist_point_min_extr = attrsman.add(cm.AttrConf( 'dist_point_min_extr',kwargs.get('dist_point_min_extr',9.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. extr. point dist.', 
                            unit = 'm',
                            info = 'Keep only the point where the distance to the point at both extremities is more than a minimum distance.',
                            ))
        
        self.dist_point_min_inter = attrsman.add(cm.AttrConf( 'dist_point_min_inter',kwargs.get('dist_point_min_inter',2.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. int. point dist.', 
                            unit = 'm',
                            info = 'Keep only the point where the distance to the previous point is more than a minimum distance.',
                            ))
                            
        self.bboxborder = attrsman.add(cm.AttrConf( 'bboxborder',kwargs.get('bboxborder',500.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'BBox border', 
                            unit = 'm',
                            info = 'Keep only traces within the network bounding box, less the width of the specified border.',
                            ))
        
        self.is_apply_bbox_explicit = attrsman.add(cm.AttrConf( 'is_apply_bbox_explicit',kwargs.get('is_apply_bbox_explicit',False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Apply expicit bounding box', 
                            info = 'Apply expicit bounding box to GPS points. If False, network boundaries are applied.',
                            ))
                            
        self.bbox_explicit = attrsman.add(cm.ListConf('bbox_explicit',kwargs.get('bbox_explicit',[0.0,0.0,0.0,0.0]), 
                                            groupnames = ['options'], 
                                            name = 'Explicit bounding box', 
                                            info = """Expicit bounding box of the format [x_min, y_min,x_max, y_max]. Empty list means no explicit bounding borders are applied.""",
                                            ))
        
        self.is_deselect_traces = attrsman.add(cm.AttrConf( 'is_deselect_traces',kwargs.get('is_deselect_traces',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Trace filter', 
                            info = 'Deselect invalid traces.',
                            ))
        
        self.dist_point_max = attrsman.add(cm.AttrConf( 'dist_point_max',kwargs.get('dist_point_max',1000.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. point dist.', 
                            unit = 'm',
                            info = 'Keep only traces where the distance between all successive points is below this maximal distance.',
                            ))
                                

        self.duration_point_max = attrsman.add(cm.AttrConf( 'duration_point_max',kwargs.get('duration_point_max',300.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. point duration.', 
                            unit = 's',
                            info = 'Keep only traces where the duration between all successive points is below this maximal duration.',
                            ))
                            
        self.const_return_max = attrsman.add(cm.AttrConf( 'const_return_max',kwargs.get('const_return_max',0.3),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. return const', 
                            info = 'Keep only traces where the user is not returning more than a share of this constant from the maximum (line of site) distance from the origin.',
                            ))
        self.speed_max = attrsman.add(cm.AttrConf( 'speed_max',kwargs.get('speed_max',50.0/3.6),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. speed', 
                            unit = 'm/s',
                            info = 'Keep only traces where this maximum speed is not reached. Maximum speed is reached if a consecutive number of Max. overspeed points is reached.',
                            ))
        
        self.n_overspeed_max = attrsman.add(cm.AttrConf( 'n_overspeed_max',kwargs.get('n_overspeed_max',3),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. overspeed points', 
                            info = 'Trace gets eliminated if this consecutive number of points have speeds over the set maximum speed.',
                            ))
        

                 
        scenario = self.parent.get_scenario()
        self.zones = scenario.landuse.zones

        
        if   len(self.zones) > 0:                          
            self.is_od_select = attrsman.add(cm.AttrConf(  'is_od_select', kwargs.get('is_od_select', False),
                                            groupnames = ['options'], 
                                            name = 'Select trip from zones', 
                                            info = 'Select trip that start/end in a certain zone',
                                            ))                    
                                           
                                           
            self.select_type = attrsman.add(cm.AttrConf(  'select_type', kwargs.get('select_type', SELECTTRACES['FromOrigin']),
                                            groupnames = ['options'], 
                                            choices = SELECTTRACES,
                                            name = 'Select type', 
                                            info = 'Type of the od selection',
                                            ))                                        
                                           
            self.origin_zone_name = attrsman.add(cm.AttrConf(  'origin_zone_name', kwargs.get('origin_zone_name', self.zones.ids_sumo[0]),
                                            groupnames = ['options'], 
                                            choices = self.zones.ids_sumo,
                                            name = 'Origin zone name', 
                                            info = 'Generating zone name.',
                                            ))
    
            self.dest_zone_name = attrsman.add(cm.AttrConf(  'dest_zone_name', kwargs.get('dest_zone_name', self.zones.ids_sumo[0]),
                                            groupnames = ['options'], 
                                            choices = self.zones.ids_sumo,
                                            name = 'Destination zone name', 
                                            info = 'Attracting zone name.',
                                        ))
                                       
        self.is_deselect_duplicate = attrsman.add(cm.AttrConf(  'is_deselect_duplicate', kwargs.get('is_deselect_duplicate', True),
                                            groupnames = ['options'], 
                                            name = 'Deselect duplicate trips', 
                                            info = 'Deselect duplicate trips by observing trips with identical duration and length',
                                            ))                
                                            
        self._init_filter_preview()
    
    
    def filterpreview(self):
        """
        Previews selected trips after filtering.
        """
        print('TripGeomfilter.filterpreview')
        trips = self.parent.trips
        n_trips = len(trips)
        if n_trips == 0:
            return '0/0 (currently 0/0)'
        
        n_sel_current = len(trips.get_ids_selected())
        n_sel_eliminate = len(self.filter_ids(is_eliminate_points = False))
        n_sel_after = n_sel_current-n_sel_eliminate
        return '%d/%d (currently %d/%d)'%(n_sel_after,n_trips,n_sel_current,n_trips)
            
    def do(self):
        print('TripGeomfilter.do')
        # execute filtering
        if len(self.parent.trips)>0:
            self.parent.trips.are_selected[self.filter_ids(is_eliminate_points = True)] = False
        return True               

    def in_boundaries_explicit(self, points, x_border = 0.0, y_border = 0.0):
            """
            Tests if the given points are in
            the explicit boundaries.

            Returns a binary vector with one element for each point
            If an element is True then the corrisponding 
            point in within the networks bounding box. 
            Otherwise the point is outside.
            Elevation is ignored.
            Format of points:
             [[x1,y1,z1],[x2,y2,z2],...]
        
                

            Returns False otherwise
            """
            #print 'intersects_boundaries'
            return ( (self.bbox_explicit[2]-x_border >= points[:,0]) & (self.bbox_explicit[0]+x_border <= points[:,0]) &
                   (self.bbox_explicit[3]-y_border >= points[:,1]) & (self.bbox_explicit[1]+y_border <= points[:,1]) )
                   
    def filter_ids(self, is_eliminate_points = True):
        """
        Returns an array of ids to be eliminated or deselected.
        """
        c_cutoff = 1.0 - self.const_return_max
        print('TripGeomfilter.filter_ids c_cutoff',c_cutoff,'is_eliminate_points',is_eliminate_points)
        dist_point_max = self.dist_point_max
        dist_point_min = self.dist_point_min_extr
        dist_point_min_inter = self.dist_point_min_inter
        duration_point_max = self.duration_point_max
        trips = self.parent.trips
        points = self.parent.points
        ids_trip = trips.get_ids_selected()
        ids_points = trips.ids_points[ids_trip]
        speed_max = self.speed_max
        print('  n_trips',len(ids_trip))
        print('  n_points',len(ids_points))
        if len(ids_points) == 0:
            print('WARNING: no points found, no traces')
            return True
        intersects_boundaries = self.parent.get_scenario().net.intersects_boundaries
        in_boundaries = self.parent.get_scenario().net.in_boundaries
        inds_elim = np.zeros(len(ids_trip), dtype = np.bool_)
        j = 0
        ids_point_elim_perm = []
        
        distances_gps = np.zeros(np.max(ids_trip)+1, dtype = np.float32)
        durations_gps = np.zeros(np.max(ids_trip)+1, dtype = np.float32)
        speeds_average = np.zeros(np.max(ids_trip)+1, dtype = np.float32)
        for id_trip in ids_trip:
            distances_gps[id_trip] = trips.distances_gps[id_trip]
            durations_gps[id_trip] = trips.durations_gps[id_trip]
            speeds_average[id_trip] = trips.speeds_average[id_trip] 
        max_speeds_intern = np.zeros(np.max(ids_trip)+1, dtype = np.float32) 
        dists_durations = np.zeros((np.max(ids_trip)+1,2), dtype = np.float32).tolist()
        n_duplicate_trips = 0
        surplus = 0
        
        for id_trip, ids_point in  zip(ids_trip, ids_points):
            print(79*'-')
            print('  filter id_trip ',id_trip, end=' ')
            if ids_point is None:
                is_eliminate = True 
                print('    no points')
                
           
            
            elif (len(ids_point) < 2*self.num_external_points+2 and self.is_eliminate_external_points):
                is_eliminate = True 
                print('    not enough points, only',len(ids_point)) 
            
            else:
##                print 'ids_point',ids_point
                n_points = len(ids_point)
                if self.is_eliminate_external_points:
                    ids_point = ids_point[self.num_external_points:(n_points-self.num_external_points)]
                    ext_points_start = list(trips.ids_points[id_trip][:(self.num_external_points)])
                    ext_points_end = list(trips.ids_points[id_trip][(n_points - self.num_external_points):])
                ids_point2 = list(ids_point)
##                print 'ids_point2',ids_point2
                
                coords = points.coords[ids_point]
                times =   points.timestamps[ids_point]
                
                n = len(ids_point)
                
                
                #print '  ids_point=', ids_point
                #print '  times',times
                #print '  coords=', coords
                #print '  duration_point_max',duration_point_max
                #dist = pointset.get_distance()
                
                if ids_point is None:
                    # this happens if the points of a trip in the workout file
                    # have not been imported for some reason
                    is_eliminate = True  
                    print('it has no points')
                elif n<2:
                    is_eliminate = True
                    print('less than 2 points')    
                elif not intersects_boundaries(get_boundary(coords)):
                    is_eliminate = True  
                    print('do not intersect boundaries')
                elif np.any(times<0):
                    is_eliminate = True 
                    print('has negative times') 
                else:
                    dist_max = 0.0
                    is_eliminate = False
                    
                    ###
                    x,y,z = coords[0]
                    ids_point_elim = set([])
                    coord_last = coords[0]
                    coord_start = coords[0]
                    coord_end = coords[-1]
                    ###
                    
                    dists_to_start = np.sqrt(   (coords[:,0]-coords[0,0])**2\
                                                + (coords[:,1]-coords[0,1])**2 )
                                                
                     
                    
                    dist_to_start_max = np.max(dists_to_start)   
                    
##                    print dists_to_start, dist_to_start_max
                    dists_to_end = np.sqrt(   (coords[:,0]-coords[-1,0])**2\
                                                + (coords[:,1]-coords[-1,1])**2 )
                    
                    dists_inter = np.zeros(len(dists_to_end),dtype = np.float32)
                    dists_inter[1:] = np.sqrt(   (coords[1:,0]-coords[:-1,0])**2\
                                                + (coords[1:,1]-coords[:-1,1])**2 )
                                                                         
                    dists_inter2 = np.zeros(len(dists_to_end),dtype = np.float32)
                    dists_inter2[2:] = np.sqrt(   (coords[2:,0]-coords[:-2,0])**2\
                                                + (coords[2:,1]-coords[:-2,1])**2 )
                                        
                    
                    durations_inter = np.zeros(len(dists_to_end),dtype = np.float32)
                    speeds_inter = np.zeros(len(dists_to_end),dtype = np.float32)
                    
                    for i, duration_inter, speed_inter in zip(list(range(len(durations_inter))), durations_inter, speeds_inter):
                        if i>0:
                            
                            durations_inter[i] = times[i]-times[i-1]
                            if durations_inter[i] >0:
                                speeds_inter[i] = dists_inter[i]/durations_inter[i]
                            else:
                                speeds_inter[i] = 0.0
                                    
                    max_speeds_intern[id_trip] = np.max(speeds_inter)
                    
                    #print '  dists_inter',dists_inter

                    #print '  eliminate',is_eliminate
                    #print '  dists_inter2',dists_inter2
                    
                  
                  
                    if self.is_analyze_points: 
                        if self.is_apply_bbox_explicit:
                            are_outside = np.logical_not(self.in_boundaries_explicit(coords, x_border =  self.bboxborder, y_border = self.bboxborder))
                        else:
                            are_outside = np.logical_not(in_boundaries(coords, x_border =  self.bboxborder, y_border = self.bboxborder))
                        
                        #print '    ids_points_outside =',np.array(ids_point, dtype = np.int32)[are_outside]
                        ids_point_elim.update(np.array(ids_point, dtype = np.int32)[are_outside])
                        print(len(ids_point_elim), 'points outside box')
                        i = 0
                        while (i<(n-2)):
                            i+=1
                            #dist_point = np.sqrt(   (coords[i,0]-coords[i-1,0])**2\
                            #                        + (coords[i,1]-coords[i-1,1])**2 )
                            #print '    ',i,'ids_point',ids_point[i],'dt=%.1f'%(times[i]-times[i-1]),'d1 %.1f'%dists_inter[i],'d2 %.1f'%dists_inter2[i+1],dists_inter[i]<dists_inter2[i+1]
                            if self.is_eliminate_close_points:
                                
                                #dist_start = np.sqrt(   (coords[i,0]-coord_start[0])**2\
                                #                    + (coords[i,1]-coord_start[1])**2 )
                                
                                if dists_to_start[i] < dist_point_min:
                                    #print '  eliminate',ids_point[i], dist_check
                                    ids_point_elim.add(ids_point[i])
                                    print('point', ids_point[i], dists_to_start[i], 'meters near start')
                                
                                
                                    
                                else: 
                                    #dist_end = np.sqrt(   (coords[i,0]-coord_end[0])**2\
                                    #                + (coords[i,1]-coord_end[1])**2 )
                                    #                
                                    if dists_to_end[i] < dist_point_min:
                                        #print '  eliminate',ids_point[i], dist_check
                                        ids_point_elim.add(ids_point[i])
                                        print('point', ids_point[i], dists_to_end[i], 'meters near end')
                                    else:
                                        
                                        #dist_check = np.sqrt(   (coords[i,0]-coord_last[0])**2\
                                        #                + (coords[i,1]-coord_last[1])**2 )
                                        if dists_inter[i]< dist_point_min_inter: #or dists_inter[i] > dists_inter2[i+1]:
                                            ids_point_elim.add(ids_point[i])
##                                            surplus += dists_inter[i]
                                            surplus += 1
                                            dists_inter[i+1] = np.sqrt(   (coords[i+1,0]-coords[i-surplus,0])**2\
                                                + (coords[i+1,1]-coords[i-surplus,1])**2 )
                                            
                                            print('point', ids_point[i], (dists_inter[i]), 'meters near other point')
                                        else:
                                            surplus = 0
                                        #else:
                                        #    coord_last = coords[i]
                                        

                        if len(ids_point2)>=2 and len(np.unique(list(ids_point_elim)))<len(ids_point2):
                            if len(ids_point_elim)>0:
                                
                                #print '  before elim ids_point',ids_point2
                                #print '  eliminate',ids_point_elim
    ##                            print 'ids_point_elim', ids_point_elim
                                for id_point in ids_point_elim:
                                    ids_point2.remove(id_point)  
                                if is_eliminate_points:
                                    trips.ids_points[id_trip] = ids_point2
                                    
                                distances_gps[id_trip] = np.sum( np.sqrt(np.sum((points.coords[ids_point2][1:,:]-points.coords[ids_point2][:-1,:])**2,1)) )
                                durations_gps[id_trip] = points.timestamps[ids_point2][-1] - points.timestamps[ids_point2][0]
    
    
                                if durations_gps[id_trip]>0:
                                    speeds_average[id_trip] = distances_gps[id_trip]/durations_gps[id_trip]
                                else:
                                    speeds_average[id_trip] = 0.0
                                print('old dist', trips.distances_gps[id_trip], 'new dist', distances_gps[id_trip])
                                print('old duration', trips.durations_gps[id_trip], 'new duration',durations_gps[id_trip])
                                print('old speed', trips.speeds_average[id_trip], 'new speed', speeds_average[id_trip])
                                print('old max speed', trips.speeds_max[id_trip], 'new max speed', max_speeds_intern[id_trip])
    
    
                            else:
                                if self.is_eliminate_external_points: 
                                    if is_eliminate_points:
                                        trips.ids_points[id_trip] = ids_point2
                                    distances_gps[id_trip] = np.sum( np.sqrt(np.sum((points.coords[ids_point2][1:,:]-points.coords[ids_point2][:-1,:])**2,1)) )
                                    durations_gps[id_trip] = points.timestamps[ids_point2][-1] - points.timestamps[ids_point2][0]
        
                                    if durations_gps[id_trip]>0:
                                        speeds_average[id_trip] = distances_gps[id_trip]/durations_gps[id_trip]
                                    else:
                                        speeds_average[id_trip] = 0.0
                                    print('old dist', trips.distances_gps[id_trip], 'new dist', distances_gps[id_trip])
                                    print('old duration', trips.durations_gps[id_trip], 'new duration',durations_gps[id_trip])
                                    print('old speed', trips.speeds_average[id_trip], 'new speed', speeds_average[id_trip])
                                    print('old max speed', trips.speeds_max[id_trip], 'new max speed', max_speeds_intern[id_trip])
                        
                        elif len(np.unique(list(ids_point_elim)))>=len(ids_point2): 
                            is_eliminate = True  
                            print('all points eliminated')
                        # no points left
                        elif len(ids_point2)<2:
                            is_eliminate = True  
                            print('no points left')      
                                

                        ids_point_elim_perm += list(ids_point_elim)
                        if self.is_eliminate_external_points:
                            ids_point_elim_perm += ext_points_start
                            ids_point_elim_perm += ext_points_end                          
##                        print 'ids_point_elim_perm', ids_point_elim_perm

##                            print 'ids_point_elim_perm', ids_point_elim_perm
                            #print '  after elim ids_point',trips.ids_points[id_trip]

                    if is_eliminate:
                        print('Deselected trace due to the point analysis')
                    if self.is_deselect_traces and not is_eliminate:
                        
                        #ricalculate dist_inter and duration_inter
                        if self.is_analyze_points:
##                            print dists_inter 
##                            print durations_inter
                            dists_inter = np.zeros(len(ids_point2),dtype = np.float32)
##                            print ids_point2
##                            print 'ids_point2', ids_point2
                            coords = points.coords[ids_point2]

                            dists_inter[1:] = np.sqrt(   (coords[1:,0]-coords[:-1,0])**2\
                                                        + (coords[1:,1]-coords[:-1,1])**2 )
                                                                                 
##                            dists_inter2 = np.zeros(len(ids_point2),dtype = np.float32)
##                            dists_inter2[2:] = np.sqrt(   (coords[2:,0]-coords[:-2,0])**2\
##                                                        + (coords[2:,1]-coords[:-2,1])**2 )
                                                
                            times = points.timestamps[ids_point2]
                            durations_inter = np.zeros(len(ids_point2),dtype = np.float32)
                            speeds_inter = np.zeros(len(ids_point2),dtype = np.float32)
                            
                            dists_to_start = np.sqrt(   (coords[:,0]-coords[0,0])**2\
                                                + (coords[:,1]-coords[0,1])**2 )
                                                
                     
                            dist_to_start_max = np.max(dists_to_start)
                            print('dist_to_start_max', dist_to_start_max)
                            
        ##                    print dists_to_start, dist_to_start_max
                            dists_to_end = np.sqrt(   (coords[:,0]-coords[-1,0])**2\
                                                        + (coords[:,1]-coords[-1,1])**2 )
                                                        
                            for i, duration_inter, speed_inter in zip(list(range(len(durations_inter))), durations_inter, speeds_inter):
                                if i>0:
                                    
                                    durations_inter[i] = times[i]-times[i-1]
                                    if durations_inter[i] >0:
                                        speeds_inter[i] = dists_inter[i]/durations_inter[i]
                                    else:
                                        speeds_inter[i] = 0.0
                                            
                            max_speeds_intern[id_trip] = np.max(speeds_inter)
##                            print dists_inter 
##                            print durations_inter
                        
                        if np.any(dists_inter>dist_point_max):
                            is_eliminate = True
                            print('one internal distance over the tollerance. Max value:', np.max(dists_inter), 'meters')
                            
                        if self.is_deselect_duplicate:
                            dist_duration = [trips.distances_gps[id_trip], trips.durations_gps[id_trip]] 

                            if dist_duration in dists_durations and dist_duration != [0.,0.]:
                                n_duplicate_trips += 1
                                print(dist_duration)
                                print('duplicated trip')
                                is_eliminate = True
##                            else:
                            dists_durations[id_trip] = [trips.distances_gps[id_trip], trips.durations_gps[id_trip]]
                            
                        if np.any(durations_inter>duration_point_max):
                            is_eliminate = True
                            print('one internal duration over the tollerance. Max value:', np.max(durations_inter), 'seconds')

                        n_invalid_speeds = 0
                        i = 0
                        while not is_eliminate and i < len(speeds_inter):
                            if speeds_inter[i] > self.speed_max:
                                n_invalid_speeds += 1
                            else:
                                n_invalid_speeds = 0

                            if n_invalid_speeds == self.n_overspeed_max:
                                is_eliminate = True
                                print('invalid internal speeds')
                            i += 1   
                        if dists_to_start[-1] <= c_cutoff*dist_to_start_max:
                            is_eliminate = True 
                            print('round trip: return distance over the tollerance')
                       
                        if len(self.zones) > 0 and  not is_eliminate:    
                            if self.is_od_select:
                                zone_shape_origin = self.zones.shapes[self.zones.ids_sumo.get_id_from_index(self.origin_zone_name)]
                                zone_shape_dest = self.zones.shapes[self.zones.ids_sumo.get_id_from_index(self.dest_zone_name)]
                                id_final_point = ids_point[-1]
                                id_initial_point = ids_point[0]
                                if self.select_type == 1:
                                    is_eliminate = not is_point_in_polygon(points.coords[id_initial_point], zone_shape_origin)
                                    if is_eliminate == True:
                                        print('deselected for the zone filter')
                                if self.select_type == 2:
                                    is_eliminate = not is_point_in_polygon(points.coords[id_final_point], zone_shape_dest)
                                    if is_eliminate == True:
                                        print('deselected for the zone filter')
                                if self.select_type == 3:
                                    is_eliminate = not is_point_in_polygon(points.coords[id_initial_point], zone_shape_origin)*is_point_in_polygon(points.coords[id_final_point], zone_shape_dest)
                                    if is_eliminate == True:
                                        print('deselected for the zone filter')
            if is_eliminate:
                print('Deselected trace')
            inds_elim[j] = is_eliminate  
            j +=1 
            
        if (len(ids_point_elim_perm)>0) & is_eliminate_points:
            points.del_rows(np.unique(ids_point_elim_perm))
            trips.distances_gps[ids_trip] = distances_gps[ids_trip]
            trips.durations_gps[ids_trip] = durations_gps[ids_trip]
            trips.speeds_average[ids_trip] = speeds_average[ids_trip] 
            trips.speeds_max[ids_trip] = max_speeds_intern[ids_trip] 


                
            print('  permanently eliminated %d GPS points'%(len(ids_point_elim_perm))) 
        if self.is_analyze_points:   
            print('%d Invalid GPS points'%(len(np.unique(ids_point_elim_perm))))    
        print('%d Invalid GPS traces'%(np.sum(inds_elim)))
        if self.is_deselect_duplicate:
            print('%d Duplicated GPS traces'%(np.sum(n_duplicate_trips)))
        #print '+++++++++++ids_point_elim_perm',ids_point_elim_perm
        #print '            eliminate points?',(len(ids_point_elim_perm)>0),is_eliminate_points,(len(ids_point_elim_perm)>0) & is_eliminate_points

        #print '  eliminate ids_trips',ids_trips[inds_elim]      
        return ids_trip[inds_elim]

class MobikeImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('MobikeImporter.__init__',mapmatching.get_ident())
        self._init_common(  'mobikeimporter', 
                            parent = mapmatching,
                            name = 'Mobike Importer', 
                            logger = logger,
                            info ='Imports GPS traces and bike IDs information from the Mobike data file.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        self.get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        rootfilepath = scenario.get_rootfilepath()
        

        
                        
        self.pointsfilepath = attrsman.add(cm.AttrConf('pointsfilepath',kwargs.get('pointsfilepath',rootfilepath+'.points.csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Mobike data file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = "CSV text file with Mobike data.",
                                    ))
        self._init_traceoptions(**kwargs)                    
                            
        
        
        self._init_filter_time(**kwargs)
        #self.sep = attrsman.add(cm.AttrConf( 'sep',kwargs.get('sep',';'),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Column separator', 
        #                    info = 'Column separator character.',
        #                    ))
        
        self.format = attrsman.add(cm.AttrConf( 'format',kwargs.get('format','Bologna'),
                            groupnames = ['options'], 
                            choices = ['Bologna old','Bologna2022','Firenze2024'],
                            perm='rw', 
                            name = 'CSV file format', 
                            info = 'CSV file format, determins the information contents of the csv file.',
                            ))
                            
        self._proj = None
        self._offset = None
        
    def calc_seconds(self, t_data, 
                    sep_date_clock = ' ', sep_date = '/', sep_clock = ':', 
                    ):
        """
        Returns time in seconds after 1/1/1970.
        Time format for time data string used:
            01/07/2018 06:00, SUNTRACTING 7h to meet chinese time
        """ 
        #
        
        if len(t_data.split(sep_date_clock))!=2:
            return -1
        (date, clock) = t_data.split(sep_date_clock) 
        clockdata = clock.split( sep_clock)
        
        if (len(clockdata) in [2,3])&(len(date.split(sep_date))==3):
            (day_str,month_str,year_str) = date.split(sep_date)
            
            #print '  year_str,month_str,day_str',year_str,month_str,day_str
            if len(clockdata) == 2:
                (hours_str,minutes_str) = clockdata
                seconds_str = "0"
            else:
                (hours_str,minutes_str,seconds_str) = clockdata
            #print '  hours_str,minutes_str,seconds_str',hours_str,minutes_str,seconds_str
            
            t = time.mktime(( int(year_str),int(month_str),int(day_str),
                              int(hours_str),int(minutes_str),int(float(seconds_str)),-1,-1,-1))-25200# -7h
            
            #print 'calc_seconds',t
            #print '  t_data'
            #print '  tupel',int(year_str),int(month_str),int(day_str), int(hours_str),int(minutes_str),int(float(seconds_str)),0,0,0
            return int(t)
        else:
            return -1
                                    
    def project(self, lons,lats):
        if self._proj is None:
            self._proj, self._offset = self.parent.get_proj_and_offset()
        x,y = self._proj(lons, lats)
        return np.transpose(np.concatenate(([x+self._offset[0]],[y+self._offset[1]]),axis=0))
        
    def validate_trip(self, dist, duration, speed_av = -1):
        if speed_av<0:
            if duration>1.0:
                speed_av = dist/duration
            else:
                speed_av = 0.0
        
        return (dist > self.dist_trip_min)\
                   & (dist < self.dist_trip_max)\
                   & (duration > self.duration_trip_min)\
                   & (speed_av > self.speed_trip_min)\
                   & (speed_av < self.speed_trip_max)
                       

    
    def validate_and_add_trip(self, persons, trips, points, id_bici_sumo, id_trip_sumo,
                                id_vtype, timestamps, longitudes, latitudes,
                                id_user_sumo,
                                duration,
                                distance,):
                                    
        #print 'validate_and_add_trip id_bici_sumo, id_trip_sumo',id_bici_sumo, id_trip_sumo
        if (len(timestamps)>1):
            # here we could check whether the already
            coords = self.project( longitudes,latitudes)
            #print '  coords',coords
            distance_gps = np.sum( np.sqrt(np.sum((coords[1:,:]-coords[:-1,:])**2,1)) )
            # no get_length_polypoints(coords)
            duration_gps = timestamps[-1]-timestamps[0]
            if duration_gps>0:
                speed_av = distance_gps/duration_gps
            else:
                speed_av = 0.0
            #print '   len(trace)',len(timestamps)
            #print '   distance_gps',   distance_gps 
            #print '   duration_gps',   duration_gps
            #print '   speed_av',   speed_av 
            #print '   timestamp_ok',self.is_timestamp_ok(timestamps[0])
           
            # recorded trip is valid
            if self.validate_trip(distance_gps, duration_gps, speed_av)\
                    & self.is_timestamp_ok(timestamps[0]):
                    #print '  store past points for valid trip',id_trip
                    
                    # create new trip
                    
                    id_trip = trips.add_row(ids_sumo = id_trip_sumo,
                                            timestamps = timestamps[0],
                                            ids_vtype = id_vtype,
                                            durations_gps = duration_gps,
                                            distances_gps = distance_gps,
                                            speeds_average = speed_av,
                                            ids_veh = id_bici_sumo,
                                            )
                                            
                    
                    ids_point = points.add_rows(\
                                timestamps = timestamps,
                                ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                                longitudes = longitudes,
                                latitudes = latitudes,
                                altitudes = np.zeros(len(longitudes), dtype = np.float32),
                                )
                    if id_user_sumo != '':
                        id_user = persons.make(id_user_sumo, id_trip = id_trip)
                        trips.ids_person[id_trip] = id_user
                        
                    # mobike does provide no altitude
                    # will be recalculated and assigned more efficiently after import
                    # points.coords[ids_point][:,:2] =  coords       
                    trips.set_points(id_trip, ids_point)
                    #print '    timestamps',timestamps
                    if len(timestamps)>1:
                        trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
                                       
    def do(self):
        print('TraceImporter.do')
        log = self.get_logger()
        
        
        
        # defaults
        ind_id_user = -1
        mobike_scooter = ''
        ind_id_trip = -1
        ind_duration = -1
        ind_distance = -1
        ind_type = -1
        
        # CSV formats
        #['Bologna old','Bologna2022','Firenze2024'],
        if self.format == 'Bologna old':
            #ID Bicicletta;Data e ora inizio;Data e ora fine;Latitudine inizio;Longitudine inizio;Latitudine fine;Longitudine fine;Durata;Distanza
            #        0       1                    2                 3                4                  5              6              7        8 
            self.sep = ';'
            ind_id_bici = 0
            ind_time_begin = 1
            ind_time_end = 2
            ind_lat_begin = 3
            ind_lon_begin = 4
            ind_lat_end = 5
            ind_lon_end = 6
            ind_duration = 7
            ind_distance = 8
            n_cols = 9
            
            
        elif self.format == 'Bologna2022':
            # attention needed to substitute ',' with '.' first
            # sed -i 's/,/./g' BikeSharing_UniBo2018-2022_test.csv
            #Inizio noleggio;Fine noleggio;Latitudine Start;Longitudine Start;Latitudine Stop;Longitudine Stop;DISTANCE (Km);BikeID;Tipo bici
            #     0               1                 2               3              4               5                6           7      8  
            # 
            self.sep = ';'
            ind_id_bici = 7
            ind_time_begin = 0
            ind_time_end = 1
            ind_lat_begin = 2 
            ind_lon_begin = 3
            ind_lat_end = 4
            ind_lon_end = 5
            #ind_duration = 7
            #ind_distance = 6
            ind_type = 8
            n_cols = 9
            mobike_muscle = 'Muscolari'
            mobike_electric = 'E-bike'
            
        elif self.format == 'Firenze2024':
            #    0        1     2    3         4            5      6           7       8              9               10             11            12        13
            #order_id;country;City;bike_no;vehicle_type;user_id;start_time;end_time;start_latitude;start_longitude;end_latitude;end_longitude;ride_time;ride_distance
            #163342531810424217IE12H08054;Italy;Firenze;IE12H08054;ebike;17;05/10/2021 11:15;05/10/2021 11:33;43,775841;11,24733;43,768901;11,234947;1089;1700

            self.sep = ';'
            ind_id_trip = 0
            ind_id_bici = 3
            ind_time_begin = 6
            ind_time_end = 7
            ind_lat_begin = 8 
            ind_lon_begin = 9
            ind_lat_end = 10
            ind_lon_end = 11
            ind_duration = 12
            ind_distance = 13
            ind_type = 4
            ind_id_user = 5
            n_cols = 14
            mobike_muscle = 'bike'
            mobike_electric = 'ebike'
            mobike_scooter = 'escooter'
            
            
            
            
        mapmatching = self.parent
        scenario = mapmatching.get_scenario()
        
        trips = mapmatching.trips# 
        
        # make sure trips table has an entry for vehicle ID
        trips.add_col(am.ArrayConf('ids_veh', default = '',
                                    groupnames = ['parameters'], 
                                    name = 'Vehicle ID',
                                    info = 'ID or serial numberof physical vehicle.',
                                    )) 
        
        persons = mapmatching.persons
        
        vtypes = scenario.demand.vtypes
        vtypechoices = vtypes.get_vtypechoices()
        print ('vtypechoices',vtypechoices)
        id_vtype = vtypes.ids_sumo.get_id_from_index('bicycle')# default
        
        points = mapmatching.points
        
        #exist_id_trip_sumo = trips.ids_sumo.has_index
        #exist_id_user_sumo = persons.ids_sumo.has_index
        
        #get_id_trip = trips.ids_sumo.get_id_from_index
        #get_id_bike = persons.ids_sumo.get_id_from_index
        
        sep = self.sep
            
        f = open(self.pointsfilepath,'r', encoding="utf-8")
        if log: log.w('import data %s'%os.path.basename(self.pointsfilepath))
        
        i_line = 0
        for line in f.readlines()[1:]:# first line contains header

            cols = line.strip().split(sep)
            #print('    i_line',i_line,'len(cols)',len(cols),n_cols)
            if len(cols)==n_cols:
                
                # sep_date_clock = ' ', sep_date = '-', sep_clock = ':', 
                #    is_float = False, is_ddmmjjj = False, is_secs = True)
                if ind_type != -1:
                    mobiketype = cols[ind_type].strip()
                    #print ('  mobiketype',mobiketype,mobiketype==mobike_electric)
                    if mobiketype == mobike_electric:
                        id_vtype = vtypes.ids_sumo.get_id_from_index('ebicycle')# vtypechoices['bicycle']
                        
                    elif mobiketype == mobike_scooter:
                        id_vtype = vtypes.ids_sumo.get_id_from_index('scooter')
                        
                    else:
                        id_vtype =  vtypes.ids_sumo.get_id_from_index('bicycle')
                
                
                if ind_id_user != -1:
                    id_user_sumo = cols[ind_id_user]
                else:
                    id_user_sumo = ''
                    
                if ind_id_trip != -1:
                    id_trip_sumo = cols[ind_id_trip]
                else:
                    id_trip_sumo = str(i_line+1)
                 
                if ind_duration != -1:
                    duration = cols[ind_duration]
                else:
                    duration = -1
                    
                if ind_distance != -1:
                    distance = cols[ind_distance]
                else:
                    distance = -1
        
        
            
                self.validate_and_add_trip(persons, trips, points, 
                                cols[ind_id_bici],
                                id_trip_sumo, # string type ID
                                id_vtype, 
                                [self.calc_seconds(cols[ind_time_begin]),self.calc_seconds(cols[ind_time_end])],
                                [get_colvalue(cols[ind_lon_begin]), get_colvalue(cols[ind_lon_end])], 
                                [get_colvalue(cols[ind_lat_begin]), get_colvalue(cols[ind_lat_end])], 
                                id_user_sumo, # string type ID
                                duration,
                                distance,
                                )
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                print('  cols =',cols)
            
                
            i_line += 1
        


     
        f.close()
        # project here efficiently for all coords
        self.parent.points.project()
        return True

class StravaImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('StravaImporter.__init__',mapmatching.get_ident())
        self._init_common(  'traceimporter', 
                            parent = mapmatching,
                            name = 'Strava Trace Importer', 
                            logger = logger,
                            info ='Imports GPS traces and person information from Strava App.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        
        # here we ged classes not vehicle type
        # specific vehicle type within a class will be generated later 
        #modechoices = scenario.net.modes.names.get_indexmap()
        
        #print '  modechoices',modechoices
        #self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'], 
        #                                groupnames = ['options'], 
        #                                choices = modechoices,
        #                                name = 'Mode', 
        #                                info = 'Transport mode to be matched.',
        #                                ))
                                            
        
                        
        self.pointsfilepath = attrsman.add(cm.AttrConf('pointsfilepath',kwargs.get('pointsfilepath','/home/cristian/Downloads/50_cleaned_gps_traces(1).csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Points file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = "CSV text file with GPS point database.",
                                    ))
                                    

        
        
        self.userinfofilepath = attrsman.add(
                    cm.AttrConf('userinfofilepath',kwargs.get('userinfofilepath','/home/cristian/Downloads/50_cleaned_users(1).csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'User info file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = """CSV text file with user info (optional).""",
                                    ))
        
                                                                                                       
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2013),
                            groupnames = ['options'], 
                            choices = {'2013':2013,},
                            perm='rw', 
                            name = 'Year of Strava data', 
                            info = 'Year of Strava is used to identify the correct database formats.',
                            ))
        
        
        
        
                            
        modechoices = scenario.demand.vtypes.get_modechoices()
        modechoices['All'] = 0 # add import all modes option
        self.id_mode_select = attrsman.add(cm.AttrConf( 'id_mode_select',kwargs.get('id_mode_select',modechoices['bicycle']),
                            groupnames = ['options'], 
                            choices = modechoices,
                            name = 'Select mode', 
                            info = 'Mode to select.',
                            ))
                            
        self._init_traceoptions(**kwargs)                    
                            

        
        self._init_filter_time(**kwargs)
        self.get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        self._proj = None
        self._offset = None
    
    def project(self, lons,lats):
        if self._proj is None:
            self._proj, self._offset = self.parent.get_proj_and_offset()
        x,y = self._proj(lons, lats)
        return np.transpose(np.concatenate(([x+self._offset[0]],[y+self._offset[1]]),axis=0))
        
        
    
    def validate_trip(self, dist, duration, speed_av = -1):
        if speed_av<0:
            if duration>1.0:
                speed_av = dist/duration
            else:
                speed_av = 0.0
        
        return (dist > self.dist_trip_min)\
                   & (dist < self.dist_trip_max)\
                   & (duration > self.duration_trip_min)\
                   & (speed_av > self.speed_trip_min)\
                   & (speed_av < self.speed_trip_max)
                       
    
                   
    def do(self):
        print('TraceImporter.do')
        if self.year == 2013:
            self.import_points_2013()
            self.import_users_2013()
            
        

        # done during import     
        self.parent.points.project()
        return True
        
    def import_users_2013(self):
        log = self.get_logger()
        
        
        print('import users')
        if  (self.userinfofilepath == ''):
            return
        
        
        null = 'NULL'
        na = '#N/A'
        trips = self.parent.trips
        persons = self.parent.persons
        persons.clear()
        ## read users
        
        persons.add_col(am.ArrayConf('ages_strava', default = AGES_STRAVA['unknown'],
                                    choices = AGES_STRAVA,
                                    dtype = np.int32,
                                    symbol = 'Ages Str.',
                                    groupnames = ['parameters'], 
                                    name = 'Ages Strava',
                                    info = 'Ages with Strava dictionary'
                                    ))
                                    
        persons.add_col(am.ArrayConf('incomes', default = INCOMES_STRAVA['unknown'],
                                    dtype = np.int32,
                                    symbol = 'Incomes Str.',
                                    choices = INCOMES_STRAVA,
                                    groupnames = ['parameters'], 
                                    name = 'Incomes Strava',
                                    info = 'Incomes of cyclists'
                                    )) 
                                    
        persons.add_col(am.ArrayConf('ethnicities', default = ETHNICITIES_STRAVA['unknown'],
                                    dtype = np.int32,
                                    symbol = 'Ethn. Str.',
                                    choices = ETHNICITIES_STRAVA,
                                    groupnames = ['parameters'], 
                                    name = 'Ethnicities Strava',
                                    info = 'Ethnicities of cyclists'
                                    )) 

        persons.add_col(am.ArrayConf('cycling_frequencies', default = CYCLING_FREQUENCIES_STRAVA['unknown'],
                                    dtype = np.int32,
                                    symbol = 'Freq. Str.',
                                    choices = CYCLING_FREQUENCIES_STRAVA,
                                    groupnames = ['parameters'], 
                                    name = 'Cycling frequencies Strava',
                                    info = 'Cycling frequencies of cyclists'
                                    )) 
                                    
        persons.add_col(am.ArrayConf('cycling_histories', default = RIDER_HISTORIES_STRAVA['unknown'],
                                    dtype = np.int32,
                                    symbol = 'Hist. Str.',
                                    choices = RIDER_HISTORIES_STRAVA,
                                    groupnames = ['parameters'], 
                                    name = 'Cycling histories Strava',
                                    info = 'Cycling histories of cyclists'
                                    )) 
                                    
        persons.add_col(am.ArrayConf('cyclist_types', default = RIDER_TYPES_STRAVA['unknown'],
                                    dtype = np.int32,
                                    symbol = 'Type Str.',
                                    choices = RIDER_TYPES_STRAVA,
                                    groupnames = ['parameters'], 
                                    name = 'Cyclist types Strava',
                                    info = 'Cyclist types of cyclists'
                                    )) 
                                    
                                    
                                    
                                    
                                    
                                            
        # change from int ti string
        #print 'GpsPersons.versio,',self.get_version(),self.get_version()<0.2
        
        
        if hasattr(self,'home_zips'):
                #print '  zips',self.zips.get_value().dtype,self.zips.get_value()
            if self.home_zips.get_value().dtype in [np.dtype(np.int32),np.dtype(np.int64)]:
                print('WARNING: delete old person.home_zips')
                self.delete('home_zips')
        if hasattr(self,'school_zips'):
                #print '  zips',self.zips.get_value().dtype,self.zips.get_value()
                if self.school_zips.get_value().dtype in [np.dtype(np.int32),np.dtype(np.int64)]:
                    print('WARNING: delete old person.school_zips')
                    self.delete('school_zips')
        if hasattr(self,'work_zips'):
                #print '  zips',self.zips.get_value().dtype,self.zips.get_value()
                if self.work_zips.get_value().dtype in [np.dtype(np.int32),np.dtype(np.int64)]:
                    print('WARNING: delete old person.work_zips')
                    self.delete('work_zips')
                                        
        #if self.get_version()<0.2:
        #    #if hasattr(self,'zips'):
        #   self.delete('zips')
        #Id_utente,Gender,YearOfBirth
#ID tripid    userid    trip_type    created_date          age    gender    income    ethnicity    homeZIP    schoolZip    workZip    cyclingfreq    rider_history    rider_type
#0    7490    311        Commute        2012-11-05 05:32:04      5        2        6        1            30306    -1            30309     4            1                1
#1    7561    35        Commute        2012-10-12 15:07:22      0      0        0        1            30316    -1            30308     3            1                2

        n_cols = 15
        j_id_line, j_tripid, j_userid, j_trip_type, j_created_date, j_age, j_gender, j_income, j_ethnicity, j_homeZIP, j_schoolZip, j_workZip, j_cyclingfreq, j_rider_history, j_rider_type  = list(range(15))
        
        #dd
        
        ids_person_sumo = {}
        
        #ids_trips = []
        scenario = self.parent.get_scenario()
        

        f=open(self.userinfofilepath,'r', encoding="utf-8")
        if log: log.w('import_users_2013 import user file %s'%os.path.basename(self.userinfofilepath))
        sep = ','
        i_line = 0
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        persons_temp = {}
        lines = f.readlines()[1:]
        user_ids = []
        
        
        for line in lines:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            #print '    cols',cols
            id_person_sumo = cols[j_userid].strip()
            id_trip_gps = cols[j_tripid].strip()
                #print '  id_trip_gps',id_trip_gps
##                print id_trip_gps

            if trips.ids_sumo.has_index(id_trip_gps):
                id_trip = trips.ids_sumo.get_id_from_index(id_trip_gps) 
                trips.ids_purpose[id_trip] = TRIPPUROPSES[cols[j_trip_type].strip()]
                if not persons.ids_sumo.has_index(id_person_sumo):
                    if len(cols)>=n_cols:
                        # row is complete
                        #print '  id_trip_sumo',cols[j_id_trip]
                        persons_temp[cols[j_userid].strip()] = (cols[j_age], 
                        cols[j_gender].strip(), 
                        cols[j_income].strip(), 
                        cols[j_ethnicity].strip(), 
                        cols[j_homeZIP].strip(), 
                        cols[j_schoolZip].strip(), 
                        cols[j_workZip].strip(), 
                        cols[j_cyclingfreq].strip(), 
                        cols[j_rider_history].strip(), 
                        cols[j_rider_type].strip())
                        
                                                    
                    else:
                        print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.userinfofilepath))
                        print('  cols =',cols)
                    if i_line%1000 == 0:
                        print(i_line,'/',len(lines), 'users imported')
                    i_line += 1
                    
                    
                    
                    if id_person_sumo != na:
                        age, gender, income, ethnicity, home_zip, school_zip, work_zip, cycling_frequency, cycling_history, cyclist_type = persons_temp[cols[j_userid].strip()]
                        id_pers = persons.make(     id_sumo = id_person_sumo,
                                                                gender = GENDERS_STRAVA[gender],
                                                                income=income,
                                                                ethnicity=ethnicity,
                                                                home_zip=home_zip,
                                                                school_zip=school_zip,
                                                                work_zip=work_zip,
                                                                cycling_frequency=cycling_frequency,
                                                                cycling_history=cycling_history,
                                                                cyclist_type=cyclist_type,
                                                                age_strava = age
                                                                ) 
                        persons.add_trip(cols[j_userid].strip(), id_trip)
                    
                else:
                    persons.add_trip(cols[j_userid].strip(), id_trip)

        f.close()
        
        
    def add_trips(self, trips, points, distances_gps, durations_gps, speeds_av, ids_trip_sumo, ids_trips_points,
                                ids_mode, timestamps_trips, timestamps_points, longitudes, latitudes, altitudes):
            
##            print timestamps_points - timestamps_points[0]
##            print timestamps_trips - timestamps_trips[0]

            
            ids_vtype = np.zeros(len(ids_trip_sumo), dtype = np.int32)
            i = 0
            for id_mode in ids_mode:
                ids_vtype[i] = self.get_vtype_for_mode(id_mode)
                i+=1                   
            # create new trip
            print('add trips')
            ids_trip = trips.add_rows(ids_sumo = ids_trip_sumo,
                                    timestamps = timestamps_trips,
                                    ids_vtype = ids_vtype,
                                    durations_gps = durations_gps,
                                    distances_gps = distances_gps,
                                    speeds_average = speeds_av,
                                    )
            print(len(ids_trip), 'trips added')

            
            i_changes = [0]
            current_id_trips_points = ids_trips_points[0]
            for id_trips_points, i in zip(ids_trips_points, list(range(len(ids_trips_points)))): 
                if id_trips_points != current_id_trips_points:
                    i_changes.append(i)
                current_id_trips_points = id_trips_points
            i_changes.append(len(ids_trips_points))
            print(len(i_changes)-1, len(ids_trip))
            for i, id_trip in zip(list(range(len(i_changes)-1)), ids_trip):
                ids_trips_points[i_changes[i]:i_changes[i+1]] = id_trip*np.ones(len(ids_trips_points[i_changes[i]:i_changes[i+1]]))
                
                
            print('add points')

##            print timestamps_points, timestamps_trips
            ids_point = points.add_rows(\
                        timestamps = timestamps_points,
                        ids_trip = ids_trips_points,
                        longitudes = longitudes,
                        latitudes = latitudes,
                        altitudes = altitudes,
                        )
                        
            print(len(ids_point), 'points added')           
            print('add ids points')

            # bellamossa does provide no altitude
            #points.coords[ids_point][:,:2] =  coords 
            for i, id_trip in zip(list(range(len(i_changes)-1)), ids_trip):
##                print points.get_ids()[(points.ids_trip[points.get_ids()] == id_trip)]
                trips.set_points(id_trip, ids_point[i_changes[i]:i_changes[i+1]])
            #print '    timestamps',timestamps
##            if len(timestamps)>1:
##                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
            for id_trip in ids_trip:
                positive_elevation = 0.0
                negative_elevation = 0.0
                ids_point_trip = trips.ids_points[id_trip]
                altitude_ahead = points.altitudes[ids_point_trip[0]]
                if len(ids_point_trip)>3:
                    i = 0
                    for id_point in ids_point_trip:
                        i+=1
                        if i%20 == 0:
                            if points.altitudes[id_point] >= altitude_ahead:
                                positive_elevation += (points.altitudes[id_point] - altitude_ahead)
                                altitude_ahead = points.altitudes[id_point]
                            else:
                                negative_elevation +=  (altitude_ahead - points.altitudes[id_point])
                                altitude_ahead = points.altitudes[id_point]
    
                trips.positive_elevations[id_trip] = positive_elevation
                trips.negative_elevations[id_trip] = negative_elevation
                

    def check_trip(self, trips, points, id_trip_sumo, 
                                id_mode, timestamps, longitudes, latitudes):
        
        is_valid = False
        #print 'validate_and_add_trip id_trip_sumo',id_trip_sumo
        if (len(timestamps)>1):
            # here we could check whether the already
            coords = self.project( longitudes,latitudes)
            #print '  coords',coords
            distance_gps = np.sum( np.sqrt(np.sum((coords[1:,:]-coords[:-1,:])**2,1)) )
            # no get_length_polypoints(coords)
            duration_gps = timestamps[-1]-timestamps[0]
            if duration_gps>0:
                speed_av = distance_gps/duration_gps
            else:
                speed_av = 0.0
            #print '   len(trace)',len(timestamps)
            #print '   distance_gps',   distance_gps 
            #print '   duration_gps',   duration_gps
            #print '   speed_av',   speed_av 
            #print '   id_mode',   id_mode, (self.id_mode_select == 0) | (id_mode == self.id_mode_select)
            #print '   timestamp_ok',self.is_timestamp_ok(timestamps[0])
           
            # recorded trip is valid
            if (self.id_mode_select == 0) | (id_mode == self.id_mode_select):
                if self.validate_trip(distance_gps, duration_gps, speed_av)\
                    & self.is_timestamp_ok(timestamps[0]):
                    #print '  store past points for valid trip',id_trip
                    is_valid = True
                    
##                    # create new trip
##                    id_trip = trips.add_row(ids_sumo = id_trip_sumo,
##                                            timestamps = timestamps[0],
##                                            ids_vtype = self.get_vtype_for_mode(id_mode),
##                                            durations_gps = duration_gps,
##                                            distances_gps = distance_gps,
##                                            speeds_average = speed_av,
##                                            )
##                                            
##                    
##                    ids_point = points.add_rows(\
##                                timestamps = timestamps,
##                                ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
##                                longitudes = longitudes,
##                                latitudes = latitudes,
##                                altitudes = np.zeros(len(longitudes), dtype = np.float32),
##                                )
##                    # bellamossa does provide no altitude
##                    #points.coords[ids_point][:,:2] =  coords       
##                    trips.set_points(id_trip, ids_point)
##                    #print '    timestamps',timestamps
##                    if len(timestamps)>1:
##                        trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
        if is_valid:
            return is_valid, distance_gps, duration_gps, speed_av
        else:
            return is_valid, 0., 0., 0.
                                
    def import_points_2013(self):
        print('import_points_2013')
        log = self.get_logger()
        #pointDBNode,   pointPathId,    id,     timestamp,          latitude,   longitude,  altitude,distance,  heartRate,instruction,speed
        #4,             61565791,   23648171762,2013-05-01 06:33:58,44.501085,  11.372906,  NULL,       0,      NULL,       2,          NULL
        #0                  1         2          3                      4          5            6       7       8           9           10
    #      tripid    datetime               lat           lon             altitude    speed hAccuracy    vAccuracy
    #221515    7490    2013-05-08 07:59:29    33.78492    -84.356894    259.6000061035    9.25       3        3
    #221516    7490    2013-05-08 07:59:30    33.784921    -84.357001    259.700012207    9.5    3       3
    #221517    7490    2013-05-08 07:59:32    33.784918    -84.357205    259.5    9.5    4    4
    #221518    7490    2013-05-08 07:59:34    33.784923    -84.357416    259.200012207    9.75       3         3

        
        ind_id_path = 1
        ind_id_point = 0
        ind_time = 2
        ind_lat = 3
        ind_lon = 4
        ind_alt = 5
        #ind_dist = 7
        ind_speed = 6
        
        n_cols = 9
        
        mapmatching = self.parent
        scenario = mapmatching.get_scenario()
        
        trips = mapmatching.trips# 
        
        modechoices = scenario.demand.vtypes.get_modechoices()
        map_bellamossamode_to_id_mode = { 'Walk':modechoices['pedestrian'],
                                           'Cycle':modechoices['bicycle'],
                                           'Bus':modechoices['bus'],
                                           'Train':modechoices['rail_urban'],
                                      }
        
        id_mode_default = modechoices['pedestrian']
        points = mapmatching.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if log: log.w('import_points_2013 %s'%os.path.basename(self.pointsfilepath))
        
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        id_vtype = -1
        is_valid_trip = False
        timestamps = []
        ids_trip = []
        longitudes = []
        latitudes = []
        i_trip = -1
        i_point = -1
        i_points = []
        id_mode = -1
        #altitudes = []
        n_points_imported = 0
        lines = f.readlines()[1:]
        n_lines = len(lines)
        n_trips = len(lines)
        print('analyze', n_lines, 'points and', n_trips, 'trips')

        ids_trip_sumo = np.zeros(n_trips,dtype = 'object')                      
        ids_mode  = np.zeros(n_trips,dtype = np.int32)                            
        timestamps_trips = np.zeros(n_trips,dtype = np.int32)
        distances_gps= np.zeros(n_trips,dtype = np.float32)
        durations_gps = np.zeros(n_trips,dtype = np.float32)
        speeds_av = np.zeros(n_trips,dtype = np.float32)
        
        ids_trips_points = np.zeros(n_lines,dtype = 'object') 
        timestamps_points = np.zeros(n_lines,dtype = np.int32)       
        longitudes_points = np.zeros(n_lines,dtype = np.float32)
        latitudes_points = np.zeros(n_lines,dtype = np.float32)
        altitudes_points = np.zeros(n_lines,dtype = np.float32)
        for line in lines:# first line contains header
            
            
            cols = line.strip().split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                i_point += 1
                i_points.append(i_point)
                id_trip_sumo_current = cols[ind_id_path]
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip,id_mode
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    i_trip += 1
                    if is_valid_trip:
                        
                        is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                                id_mode, timestamps, longitudes, latitudes)
                        if is_valid:
##                            print len(timestamps), len(longitudes), len(i_points)
                            
                            ids_trip_sumo[i_trip] = id_trip_sumo                      
                            ids_mode[i_trip]  =  id_mode                           
                            timestamps_trips[i_trip] = timestamps[0]
                            distances_gps[i_trip] = distance_gps
                            durations_gps[i_trip] = duration_gps
                            speeds_av[i_trip] = speed_av
                            
                            i_points = np.array(i_points, dtype = np.int32)
                            for i_p, timestamp, longitude, latitude, altitude in zip(i_points, timestamps, longitudes, latitudes, altitudes):
                                timestamps_points[i_p] = timestamp      
                                longitudes_points[i_p] = longitude
                                latitudes_points[i_p] = latitude
                                altitudes_points[i_p] = altitude
                                ids_trips_points[i_p] = id_trip_sumo
##                                print timestamp
##                                print timestamps_points[i_p]
##                                print timestamps[0], timestamps_points[i_p] - timestamps[0]
##                                print timestamps_points[i_p], timestamps_trips[i_trip]
                        
                        i_points = []
                    else:
                        # one point old trip will not be registered
                        pass
                    i_points = []
                    
                    # check if new trip is valid # trips.ids_sumo.has_index
                    #if exist_id_trip_sumo(id_trip_sumo_current):
                        
                    is_valid_trip = True # start recording
                    #id_trip = get_id_trip(id_trip_sumo_current)
                    #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                    
                    # ids_point_sumo = [] # useless?
                    timestamps = []
                    #ids_trip = []
                    longitudes = []
                    latitudes = []
                    altitudes = []
                        
                    #else:
                    #    #print '    trip',id_trip_sumo_current,'does not exisit'
                    #    is_valid_trip  = False
                    #    id_trip = -1
                        
                    id_trip_sumo = id_trip_sumo_current
                    #id_vtype = get_vtype_for_mode(mode = map_bellamossamode_to_sumomode.get(cols[ind_mode], 'pedestrian'))
                    id_mode =  self.id_mode_select
                    #print '   id_mode',id_mode,cols[ind_mode]
                    is_adding = True
                else: 
                    is_adding = False
                    
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(calc_seconds(cols[ind_time]))
                    
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    altitudes.append(get_colvalue(cols[ind_alt]))
                    
      
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                print('  cols =',cols)
            
            if i_line%500000 == 0:
                print(i_line,'/',len(lines), 'points imported')
            i_line += 1
        
        # register points of last trip after loop ended
        if is_adding == False:
                i_trip += 1
                if is_valid_trip:
                    
                    is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                            id_mode, timestamps, longitudes, latitudes)
                    if is_valid:
##                            print len(timestamps), len(longitudes), len(i_points)
                        
                        ids_trip_sumo[i_trip] = id_trip_sumo                      
                        ids_mode[i_trip]  =  id_mode                           
                        timestamps_trips[i_trip] = timestamps[0]
                        distances_gps[i_trip] = distance_gps
                        durations_gps[i_trip] = duration_gps
                        speeds_av[i_trip] = speed_av
                        
                        i_points = np.array(i_points, dtype = np.int32)
                        for i_p, timestamp, longitude, latitude, altitude in zip(i_points, timestamps, longitudes, latitudes, altitudes):
                            timestamps_points[i_p] = timestamp      
                            longitudes_points[i_p] = longitude
                            altitudes_points[i_p] = altitude
                            latitudes_points[i_p] = latitude
                            altitudes_points[i_p] = altitude
                            ids_trips_points[i_p] = id_trip_sumo
        
                         
        ids_mode  =  ids_mode[( ids_trip_sumo  > 0 )]  
        distances_gps =  distances_gps[( ids_trip_sumo  > 0 )] 
        durations_gps =  durations_gps[( ids_trip_sumo  > 0 )] 
        speeds_av  =  speeds_av[( ids_trip_sumo  > 0 )] 
        timestamps_trips = timestamps_trips[( ids_trip_sumo  > 0 )] 
        ids_trip_sumo = ids_trip_sumo[( ids_trip_sumo  > 0 )] 
        
        longitudes_points = longitudes_points[( ids_trips_points  > 0 )] 
        latitudes_points = latitudes_points[( ids_trips_points  > 0 )] 
        altitudes_points = altitudes_points[( ids_trips_points  > 0 )] 
        timestamps_points =   timestamps_points[( ids_trips_points  > 0 )]     
        ids_trips_points =  ids_trips_points[( ids_trips_points  > 0 )] 
        

                
        self.add_trips(trips, points, distances_gps, durations_gps, speeds_av, ids_trip_sumo, ids_trips_points,
                                ids_mode, timestamps_trips, timestamps_points, longitudes_points, latitudes_points, altitudes_points)
        
        f.close()
             

class BellamossaImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('BellamossaImporter.__init__',mapmatching.get_ident())
        self._init_common(  'traceimporter', 
                            parent = mapmatching,
                            name = 'Bellamossa Trace Importer', 
                            logger = logger,
                            info ='Imports GPS traces and person information from the Bellamossa initiative.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        
        # here we ged classes not vehicle type
        # specific vehicle type within a class will be generated later 
        #modechoices = scenario.net.modes.names.get_indexmap()
        
        #print '  modechoices',modechoices
        #self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'], 
        #                                groupnames = ['options'], 
        #                                choices = modechoices,
        #                                name = 'Mode', 
        #                                info = 'Transport mode to be matched.',
        #                                ))
                                            
        
                        
        self.pointsfilepath = attrsman.add(cm.AttrConf('pointsfilepath',kwargs.get('pointsfilepath',rootfilepath+'.points.csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Points file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = "CSV text file with GPS point database.",
                                    ))
                                    

        
        
        self.userinfofilepath = attrsman.add(
                    cm.AttrConf('userinfofilepath',kwargs.get('userinfofilepath',''),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'User info file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = """CSV text file with user info (optional).""",
                                    ))
        
        self.tripinfofilepath = attrsman.add(
                    cm.AttrConf('tripinfofilepath',kwargs.get('tripinfofilepath',''),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Trip info file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = """CSV text file with trip info (optional).""",
                                    ))
                                                                                                       
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2017),
                            groupnames = ['options'], 
                            choices = {'2017':2017,'2024':2024,},
                            perm='rw', 
                            name = 'Year of Bellamossa', 
                            info = 'Year of Bellamossa initiative is used to identify the correct database formats.',
                            ))
        
        
        
        
                            
        modechoices = scenario.demand.vtypes.get_modechoices()
        modechoices['All'] = 0 # add import all modes option
        self.id_mode_select = attrsman.add(cm.AttrConf( 'id_mode_select',kwargs.get('id_mode_select',modechoices['pedestrian']),
                            groupnames = ['options'], 
                            choices = modechoices,
                            name = 'Select mode', 
                            info = 'Mode to select.',
                            ))
                            
        self._init_traceoptions(**kwargs)                    
                            

        
        self._init_filter_time(**kwargs)
        self.get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        self._proj = None
        self._offset = None
    
    def project(self, lons,lats):
        if self._proj is None:
            self._proj, self._offset = self.parent.get_proj_and_offset()
        x,y = self._proj(lons, lats)
        return np.transpose(np.concatenate(([x+self._offset[0]],[y+self._offset[1]]),axis=0))
        
        
    
    def validate_trip(self, dist, duration, speed_av = -1):
        if speed_av<0:
            if duration>1.0:
                speed_av = dist/duration
            else:
                speed_av = 0.0
        
        return (dist > self.dist_trip_min)\
                   & (dist < self.dist_trip_max)\
                   & (duration > self.duration_trip_min)\
                   & (speed_av > self.speed_trip_min)\
                   & (speed_av < self.speed_trip_max)
                       
    
                   
    def do(self):
        print('TraceImporter.do')
        if self.year == 2017:
            self.import_points_2017()
            self.import_users_2017()
            
        elif self.year == 2024: 
            self.import_points_2024()

        # done during import     
        self.parent.points.project()
        return True
        
    def import_users_2017(self):
        log = self.get_logger()
        
        print('import users')
        if (self.tripinfofilepath == '') | (self.userinfofilepath == ''):
            return
        
        
        null = 'NULL'
        na = '#N/A'
        trips = self.parent.trips
        persons = self.parent.persons
        persons.clear()
        ## read users
        
        #Id_utente,Gender,YearOfBirth
        #81511,Female,1981
        #81507,Male,1983
        n_cols = 3
        j_id_user, j_sex, j_year = list(range(3))
        
        
        #exist_id_person_sumo = persons.ids_sumo.has_index
        ids_person_sumo = {}
        
        #ids_trips = []
        scenario = self.parent.get_scenario()
        

        f=open(self.userinfofilepath,'r', encoding="utf-8")
        if log: log.w('import_users_2017 import user file %s'%os.path.basename(self.userinfofilepath))
        sep = ','
        i_line = 0
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        persons_temp = {}
        lines = f.readlines()[1:]
        
        for line in lines:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            #print '    cols',cols
            if len(cols)>=n_cols:
                # row is complete
                #print '  id_trip_sumo',cols[j_id_trip]
                persons_temp[cols[j_id_user].strip()] = (cols[j_sex].strip(), int(cols[j_year]))
                
                                            
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.userinfofilepath))
                print('  cols =',cols)
            if i_line%1000 == 0:
                print(i_line,'/',len(lines), 'users imported')
            i_line += 1

        f.close()
        
        
        ## read trip-user file
        n_cols = 2
        j_id_trip, j_id_user = list(range(2))
        f = open(self.tripinfofilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_users_2017 import tripfile %s'%os.path.basename(self.tripinfofilepath))
        sep = ','
        i_line = 0
        lines = f.readlines()[1:]
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        for line in lines:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            #print '    cols',cols
            if len(cols)>=n_cols:
                # row is complete
                
                id_trip_gps = cols[j_id_trip].strip()
                #print '  id_trip_gps',id_trip_gps
##                print id_trip_gps
                
                if trips.ids_sumo.has_index(id_trip_gps):
                    id_trip = trips.ids_sumo.get_id_from_index(id_trip_gps)
                    id_person_sumo = cols[j_id_user].strip()
                    if id_person_sumo != na:
                        if not persons.ids_sumo.has_index(id_person_sumo):
##                            # person with this trip not in person...add person
                            gender, year_birth = persons_temp[id_person_sumo]
                            id_pers = persons.make(     id_sumo = id_person_sumo,
                                                        gender = gender,
                                                        year_birth = year_birth,
                                                        ) 
                        
                        persons.add_trip(cols[j_id_user].strip(), id_trip)
                    
                                            
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.tripinfofilepath))
                print('  cols =',cols)
            if i_line%500000 == 0:
                print(i_line,'/',len(lines), 'users imported')
            i_line += 1                          
    
    def add_trips(self, trips, points, distances_gps, durations_gps, speeds_av, ids_trip_sumo, ids_trips_points,
                                ids_mode=None, timestamps_trips=None,
                                 timestamps_points=None, 
                                 longitudes=None, latitudes=None,
                                 ids_vtype = None,
                                 ):
            
##            print timestamps_points - timestamps_points[0]
##            print timestamps_trips - timestamps_trips[0]

            
            if ids_vtype is None:
                ids_vtype = np.zeros(len(ids_trip_sumo), dtype = np.int32)
                i = 0
                for id_mode in ids_mode:
                    ids_vtype[i] = self.get_vtype_for_mode(id_mode)
                    i+=1                   
            # create new trip
            print('add trips')
            ids_trip = trips.add_rows(ids_sumo = ids_trip_sumo,
                                    timestamps = timestamps_trips,
                                    ids_vtype = ids_vtype,
                                    durations_gps = durations_gps,
                                    distances_gps = distances_gps,
                                    speeds_average = speeds_av,
                                    )
            print(len(ids_trip), 'trips added')

            if len(ids_trip) > 0:
                i_changes = [0]
                current_id_trips_points = ids_trips_points[0]
                for id_trips_points, i in zip(ids_trips_points, list(range(len(ids_trips_points)))): 
                    if id_trips_points != current_id_trips_points:
                        i_changes.append(i)
                    current_id_trips_points = id_trips_points
                i_changes.append(len(ids_trips_points))
                #print(len(i_changes)-1, len(ids_trip))
                for i, id_trip in zip(list(range(len(i_changes)-1)), ids_trip):
                    ids_trips_points[i_changes[i]:i_changes[i+1]] = id_trip*np.ones(len(ids_trips_points[i_changes[i]:i_changes[i+1]]))
                    
                    
                print('add points')
    
                #print timestamps_points, timestamps_trips
                ids_point = points.add_rows(\
                            timestamps = timestamps_points,
                            ids_trip = ids_trips_points,
                            longitudes = longitudes,
                            latitudes = latitudes,
                            altitudes = np.zeros(len(ids_trips_points), dtype = np.float32),
                            )
                            
                print(len(ids_point), 'points added')           
                print('add ids points')
    
                # bellamossa does provide no altitude
                #points.coords[ids_point][:,:2] =  coords 
                for i, id_trip in zip(list(range(len(i_changes)-1)), ids_trip):
    ##                print points.get_ids()[(points.ids_trip[points.get_ids()] == id_trip)]
                    trips.set_points(id_trip, ids_point[i_changes[i]:i_changes[i+1]])
                #print '    timestamps',timestamps
    ##            if len(timestamps)>1:
    ##                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]

    def check_trip(self, trips, points, id_trip_sumo, 
                                id_mode, timestamps, longitudes, latitudes):
        
        is_valid = False
        #print ('check_trip id_trip_sumo',id_trip_sumo,'len(timestamps)',len(timestamps))
        if (len(timestamps)>1):
            # here we could check whether the already
            coords = self.project( longitudes,latitudes)
            #print '  coords',coords
            distance_gps = np.sum( np.sqrt(np.sum((coords[1:,:]-coords[:-1,:])**2,1)) )
            # no get_length_polypoints(coords)
            duration_gps = timestamps[-1]-timestamps[0]
            if duration_gps>0:
                speed_av = distance_gps/duration_gps
            else:
                speed_av = 0.0
            #print ('   len(trace)',len(timestamps),'t[0]',timestamps[0])
            #print ('   distance_gps',   distance_gps)
            #print ('   duration_gps',   duration_gps)
            #print ('   speed_av',   speed_av )
            #print ('   id_mode',   id_mode, (self.id_mode_select == 0) | (id_mode == self.id_mode_select))
            #print ('   timestamp_ok',self.is_timestamp_ok(timestamps[0]))
            #print ('   validate_trip',self.validate_trip(distance_gps, duration_gps, speed_av))
           
            
            if (self.id_mode_select == 0) | (id_mode == self.id_mode_select):
                if self.validate_trip(distance_gps, duration_gps, speed_av)\
                    & self.is_timestamp_ok(timestamps[0]):
                    #print '  store past points for valid trip',id_trip
                    is_valid = True
                    
        
        if is_valid:
            #print ('  recorded trip is valid')
            return is_valid, distance_gps, duration_gps, speed_av
        else:
            return is_valid, 0., 0., 0.
                                
    def import_points_2017(self):
        print('import_points_2017')
        log = self.get_logger()
        #    0          1           2     3        4        5        6       7              8
        # ActivityId,ActivityType,Time,Latitude,Longitude,Accuracy,Speed,IdentifiedType,IdentifiedConfidence
        
        # 2633705,Cycle,2017-07-14 16:29:10,44.50077,11.34439,9,-1,OnBicycle,92      
        # TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
        # 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
        #5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid

        ind_id_path = 0
        ind_mode = 1
        ind_time = 2
        ind_lat = 3
        ind_lon = 4
        ind_speed = 6 # currently not imported
        ind_type = 7  # currently not imported
        
        
        
        n_cols = 9
        
        mapmatching = self.parent
        scenario = mapmatching.get_scenario()
        
        trips = mapmatching.trips# 
        
        modechoices = scenario.demand.vtypes.get_modechoices()
        map_bellamossamode_to_id_mode = { 'Walk':modechoices['pedestrian'],
                                           'Cycle':modechoices['bicycle'],
                                           'Bus':modechoices['bus'],
                                           'Train':modechoices['rail_urban'],
                                      }
        
        id_mode_default = modechoices['pedestrian']
        points = mapmatching.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if log: log.w('import_points_2017 %s'%os.path.basename(self.pointsfilepath))
        
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        id_vtype = -1
        is_valid_trip = False
        timestamps = []
        ids_trip = []
        longitudes = []
        latitudes = []
        i_trip = -1
        i_point = -1
        i_points = []
        id_mode = -1
        #altitudes = []
        n_points_imported = 0
        lines = f.readlines()[1:]
        n_lines = len(lines)
        n_trips = len(lines)
        print('analyze', n_lines, 'points and', n_trips, 'trips')

        ids_trip_sumo = np.zeros(n_trips,dtype = 'object')                      
        ids_mode  = np.zeros(n_trips,dtype = np.int32)                            
        timestamps_trips = np.zeros(n_trips,dtype = np.int32)
        distances_gps= np.zeros(n_trips,dtype = np.float32)
        durations_gps = np.zeros(n_trips,dtype = np.float32)
        speeds_av = np.zeros(n_trips,dtype = np.float32)
        
        ids_trips_points = np.zeros(n_lines,dtype = 'object') 
        timestamps_points = np.zeros(n_lines,dtype = np.int32)       
        longitudes_points = np.zeros(n_lines,dtype = np.float32)
        latitudes_points = np.zeros(n_lines,dtype = np.float32)
        
        for line in lines:# first line contains header
            
            
            cols = line.strip().split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                i_point += 1
                i_points.append(i_point)
                id_trip_sumo_current = cols[ind_id_path]
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip,id_mode
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    i_trip += 1
                    if is_valid_trip:
                        
                        is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                                id_mode, timestamps, longitudes, latitudes)
                        if is_valid:
##                            print len(timestamps), len(longitudes), len(i_points)
                            
                            ids_trip_sumo[i_trip] = id_trip_sumo                      
                            ids_mode[i_trip]  =  id_mode                           
                            timestamps_trips[i_trip] = timestamps[0]
                            distances_gps[i_trip] = distance_gps
                            durations_gps[i_trip] = duration_gps
                            speeds_av[i_trip] = speed_av
                            
                            i_points = np.array(i_points, dtype = np.int32)
                            for i_p, timestamp, longitude, latitude in zip(i_points, timestamps, longitudes, latitudes):
                                timestamps_points[i_p] = timestamp      
                                longitudes_points[i_p] = longitude
                                latitudes_points[i_p] = latitude
                                ids_trips_points[i_p] = id_trip_sumo
##                                print timestamp
##                                print timestamps_points[i_p]
##                                print timestamps[0], timestamps_points[i_p] - timestamps[0]
##                                print timestamps_points[i_p], timestamps_trips[i_trip]
                        
                        i_points = []
                    else:
                        # one point old trip will not be registered
                        pass
                    i_points = []
                    
                    # check if new trip is valid # trips.ids_sumo.has_index
                    #if exist_id_trip_sumo(id_trip_sumo_current):
                        
                    is_valid_trip = True # start recording
                    #id_trip = get_id_trip(id_trip_sumo_current)
                    #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                    
                    # ids_point_sumo = [] # useless?
                    timestamps = []
                    #ids_trip = []
                    longitudes = []
                    latitudes = []
                    #altitudes = []
                        
                    #else:
                    #    #print '    trip',id_trip_sumo_current,'does not exisit'
                    #    is_valid_trip  = False
                    #    id_trip = -1
                        
                    id_trip_sumo = id_trip_sumo_current
                    #id_vtype = get_vtype_for_mode(mode = map_bellamossamode_to_sumomode.get(cols[ind_mode], 'pedestrian'))
                    id_mode =  map_bellamossamode_to_id_mode.get(cols[ind_mode], id_mode_default)
                    #print '   id_mode',id_mode,cols[ind_mode]
                    is_adding = True
                else: 
                    is_adding = False
                    
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(calc_seconds(cols[ind_time]))
                    
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    #altitudes.append(-1.0)
                    
      
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                print('  cols =',cols)
            
            if i_line%500000 == 0:
                print(i_line,'/',len(lines), 'points imported')
            i_line += 1
        
        # register points of last trip after loop ended
        if is_adding == False:
                i_trip += 1
                if is_valid_trip:
                    
                    is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                            id_mode, timestamps, longitudes, latitudes)
                    if is_valid:
##                            print len(timestamps), len(longitudes), len(i_points)
                        
                        ids_trip_sumo[i_trip] = id_trip_sumo                      
                        ids_mode[i_trip]  =  id_mode                           
                        timestamps_trips[i_trip] = timestamps[0]
                        distances_gps[i_trip] = distance_gps
                        durations_gps[i_trip] = duration_gps
                        speeds_av[i_trip] = speed_av
                        
                        i_points = np.array(i_points, dtype = np.int32)
                        for i_p, timestamp, longitude, latitude in zip(i_points, timestamps, longitudes, latitudes):
                            timestamps_points[i_p] = timestamp      
                            longitudes_points[i_p] = longitude
                            latitudes_points[i_p] = latitude
                            ids_trips_points[i_p] = id_trip_sumo
        
                         
        ids_mode  =  ids_mode[( ids_trip_sumo  > 0 )]  
        distances_gps =  distances_gps[( ids_trip_sumo  > 0 )] 
        durations_gps =  durations_gps[( ids_trip_sumo  > 0 )] 
        speeds_av  =  speeds_av[( ids_trip_sumo  > 0 )] 
        timestamps_trips = timestamps_trips[( ids_trip_sumo  > 0 )] 
        ids_trip_sumo = ids_trip_sumo[( ids_trip_sumo  > 0 )] 
        
        longitudes_points = longitudes_points[( ids_trips_points  > 0 )] 
        latitudes_points = latitudes_points[( ids_trips_points  > 0 )] 
        timestamps_points =   timestamps_points[( ids_trips_points  > 0 )]     
        ids_trips_points =  ids_trips_points[( ids_trips_points  > 0 )] 

                
        self.add_trips(trips, points, distances_gps, durations_gps, speeds_av, ids_trip_sumo, ids_trips_points,
                                ids_mode, timestamps_trips, timestamps_points, longitudes_points, latitudes_points)
        
        f.close()
                     
    def import_points_2024(self):
        print('import_points_2024')
        log = self.get_logger()
        #    0      1              2                      3   t_trip_start            4                   5                        6                           7                     8                    9   t_trip_end            10                     11                        12                            13                   14                  15             16         17          18       19                             20                    21              22     23 XX
        #"enum";"id_recorrido";"duracion_recorrido";"fecha_origen_recorrido";"id_estacion_origen";"nombre_estacion_origen";"direccion_estacion_origen";"long_estacion_origen";"lat_estacion_origen";"fecha_destino_recorrido";"id_estacion_destino";"nombre_estacion_destino";"direccion_estacion_destino";"long_estacion_destino";"lat_estacion_destino";"tipo_bicicleta";"genero";"perfil_viagem";"sexo"; "date_gps";                  "position_latitude";"position_longitude";"mes";"gender"
        #"1";   "173233BikeSt";   "5218.0";        "2023-01-01T16:28:44Z"   ;"322BikeSampa";      "322mtrô Eucaliptos";    "Av. Cotovia, 3068,Av. Ibi";"-46.6681444400963"   ;"-23.609452586953"   ;"2023-01-01T17:55:42Z";   "132BikeSampa";       "132 - Ibirapuera Portão";"Av. 415 / próxi Ibirapuera";"-46.6553996394387";    "-23.5921163886911";   "MECHANICAL";    "";      "Utilitária";   "OTHER";"2023-01-01 16:29:17+00:00";"-23.609703064";    "-46.667617798";      1;   "other"
        
        # 2633705,Cycle,2017-07-14 16:29:10,44.50077,11.34439,9,-1,OnBicycle,92      
        # TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
        # 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
        #5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid

        ind_id_path = 1
        ind_time = 19
        ind_lat = 20
        ind_lon = 21
        ind_type = 15

        
        n_cols = 24
        
        mapmatching = self.parent
        scenario = mapmatching.get_scenario()
        vtypes = scenario.demand.vtypes
        trips = mapmatching.trips# 
        
        #modechoices = scenario.demand.vtypes.get_modechoices()
        
        
        map_biketype = { 'ELECTRIC':vtypes.ids_sumo.get_id_from_index('ebicycle'),
                         'MECHANICAL':vtypes.ids_sumo.get_id_from_index('bicycle'),
                        }
        
        points = mapmatching.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ';'#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if log: log.w('import_points_2024 %s'%os.path.basename(self.pointsfilepath))
        
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        id_vtype = -1
        is_valid_trip = False
        timestamps = []
        ids_trip = []
        longitudes = []
        latitudes = []
        i_trip = -1
        i_point = -1
        i_points = []
        id_mode = -1
        #altitudes = []
        n_points_imported = 0
        lines = f.readlines()[1:]
        n_lines = len(lines)
        n_trips = len(lines)
        print('analyze', n_lines, 'points and', n_trips, 'trips')

        ids_trip_sumo = np.ones(n_trips,dtype = 'object') * ''                     
        ids_vtype  = np.zeros(n_trips,dtype = np.int32)                            
        timestamps_trips = np.zeros(n_trips,dtype = np.int32)
        distances_gps= np.zeros(n_trips,dtype = np.float32)
        durations_gps = np.zeros(n_trips,dtype = np.float32)
        speeds_av = np.zeros(n_trips,dtype = np.float32)
        
        ids_trips_points = np.zeros(n_lines,dtype = 'object') *''
        timestamps_points = np.zeros(n_lines,dtype = np.int32)       
        longitudes_points = np.zeros(n_lines,dtype = np.float32)
        latitudes_points = np.zeros(n_lines,dtype = np.float32)
        
        id_mode_dummy = 0
        for line in lines:# first line contains header
            
            
            cols = line.strip().split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                i_point += 1
                i_points.append(i_point)
                id_trip_sumo_current = cols[ind_id_path]
                #print ('    id_trip_sumo_current,id_trip_sumo id_new',id_trip_sumo_current,'old',id_trip_sumo,is_valid_trip)
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    i_trip += 1
                    if is_valid_trip:
                        id_vtype = map_biketype[cols[ind_type]]
                        is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                                id_mode_dummy, timestamps, longitudes, latitudes)
                        
                        if is_valid:
##                          #print len(timestamps), len(longitudes), len(i_points)
                            
                            ids_trip_sumo[i_trip] = id_trip_sumo                      
                            ids_vtype[i_trip]  =  id_vtype                        
                            timestamps_trips[i_trip] = timestamps[0]
                            distances_gps[i_trip] = distance_gps
                            durations_gps[i_trip] = duration_gps
                            speeds_av[i_trip] = speed_av
                            
                            i_points = np.array(i_points, dtype = np.int32)
                            for i_p, timestamp, longitude, latitude in zip(i_points, timestamps, longitudes, latitudes):
                                timestamps_points[i_p] = timestamp      
                                longitudes_points[i_p] = longitude
                                latitudes_points[i_p] = latitude
                                ids_trips_points[i_p] = id_trip_sumo
##                                print timestamp
##                                print timestamps_points[i_p]
##                                print timestamps[0], timestamps_points[i_p] - timestamps[0]
##                                print timestamps_points[i_p], timestamps_trips[i_trip]
                        
                        i_points = []
                    else:
                        # one point old trip will not be registered
                        pass
                    
                    i_points = []
                    
                    # check if new trip is valid # trips.ids_sumo.has_index
                    #if exist_id_trip_sumo(id_trip_sumo_current):
                        
                    is_valid_trip = True # start recording
                    #id_trip = get_id_trip(id_trip_sumo_current)
                    #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                    
                    # ids_point_sumo = [] # useless?
                    timestamps = []
                    #ids_trip = []
                    longitudes = []
                    latitudes = []
                    #altitudes = []
                        
                    #else:
                    #    #print '    trip',id_trip_sumo_current,'does not exisit'
                    #    is_valid_trip  = False
                    #    id_trip = -1
                        
                    id_trip_sumo = id_trip_sumo_current
                    #id_vtype = get_vtype_for_mode(mode = map_bellamossamode_to_sumomode.get(cols[ind_mode], 'pedestrian'))
                    #id_mode =  map_bellamossamode_to_id_mode.get(cols[ind_mode], id_mode_default)
                    #print '   id_mode',id_mode,cols[ind_mode]
                    is_adding = True
                else: 
                    is_adding = False
                    
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(calc_seconds(cols[ind_time], sep_date_clock = ' ', sep_subsec = '+'))
                    
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    #altitudes.append(-1.0)
                    
      
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                print('  cols =',cols)
            
            if i_line%500000 == 0:
                print(i_line,'/',len(lines), 'points imported')
            i_line += 1
        
        # register points of last trip after loop ended
        if is_adding == False:
                i_trip += 1
                if is_valid_trip:
                    
                    is_valid, distance_gps, duration_gps, speed_av = self.check_trip(trips, points, id_trip_sumo, 
                            id_mode_dummy, timestamps, longitudes, latitudes)
                    if is_valid:
                        #print ('  trip',i_trip,'lents',len(timestamps),'lenlon' ,len(longitudes),'lenpoints', len(i_points))
                        #if len(longitudes) != len(latitudes):
                        #    print ('ERROR: unequal coord length!!')
                        #    sys.exit(1)
                        ids_trip_sumo[i_trip] = id_trip_sumo                      
                        ids_vtype[i_trip]  =  id_vtype                           
                        timestamps_trips[i_trip] = timestamps[0]
                        distances_gps[i_trip] = distance_gps
                        durations_gps[i_trip] = duration_gps
                        speeds_av[i_trip] = speed_av
                        
                        i_points = np.array(i_points, dtype = np.int32)
                        for i_p, timestamp, longitude, latitude in zip(i_points, timestamps, longitudes, latitudes):
                            timestamps_points[i_p] = timestamp      
                            longitudes_points[i_p] = longitude
                            latitudes_points[i_p] = latitude
                            ids_trips_points[i_p] = id_trip_sumo
        
                         
        #ids_mode  =  ids_mode[( ids_trip_sumo  > 0 )]  
        inds_valid = ids_trip_sumo != ''
        #distances_gps =  distances_gps[inds_valid] 
        #durations_gps =  durations_gps[inds_valid] 
        #speeds_av  =  speeds_av[inds_valid] 
        #timestamps_trips = timestamps_trips[inds_valid] 
        #ids_trip_sumo = ids_trip_sumo[inds_valid] 
        
        inds_valid_point = ids_trips_points != ''
        #longitudes_points = longitudes_points[inds_valid_point] 
        #latitudes_points = latitudes_points[inds_valid_point] 
        #timestamps_points =   timestamps_points[inds_valid_point]     
        #ids_trips_points =  ids_trips_points[inds_valid_point] 

                
        self.add_trips(trips, points, 
                                distances_gps[inds_valid], 
                                durations_gps[inds_valid], 
                                speeds_av[inds_valid], 
                                ids_trip_sumo[inds_valid], 
                                ids_trips_points[inds_valid_point] ,
                                ids_vtype = ids_vtype[inds_valid], 
                                timestamps_trips=timestamps_trips[inds_valid], 
                                timestamps_points=timestamps_points[inds_valid_point], 
                                longitudes=longitudes_points[inds_valid_point] , 
                                latitudes=latitudes_points[inds_valid_point])
        
        f.close()
        
class EccTracesImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('EccTracesImporter.__init__',mapmatching.get_ident())
        self._init_common(  'traceimporter', 
                            parent = mapmatching,
                            name = 'ECC Trace Importer', 
                            logger = logger,
                            info ='Import workouts and GPS points of a European cycling challange.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        
        # here we ged classes not vehicle type
        # specific vehicle type within a class will be generated later 
        modechoices = scenario.net.modes.names.get_indexmap()
        
        #print '  modechoices',modechoices
        self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'], 
                                        groupnames = ['options'], 
                                        choices = modechoices,
                                        name = 'Mode', 
                                        info = 'Transport mode to be matched.',
                                        ))
                                            
        self.workoutsfilepath = attrsman.add(
                    cm.AttrConf('workoutsfilepath',kwargs.get('workoutsfilepath',rootfilepath+'.workouts.csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Workout file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = """CSV text file with workout database.""",
                                    ))
                        
        self.pointsfilepath = attrsman.add(cm.AttrConf('pointsfilepath',kwargs.get('pointsfilepath',rootfilepath+'.points.csv'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'Points file', 
                                    wildcards = 'CSV file (*.csv)|*.csv',
                                    metatype = 'filepath',
                                    info = "CSV text file with GPS point database.",
                                    ))
                                    

        
                                               
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2014),
                            groupnames = ['options'], 
                            choices = {'2013':2013,'2014':2014,'2015':2015, '2016':2016},
                            perm='rw', 
                            name = 'Year of challange', 
                            info = 'Year of challange is used to identify the correct database format.',
                            ))
        
        
        self._init_traceoptions(**kwargs)                    
                            
        #self.sep_column_workout = attrsman.add(cm.AttrConf( 'sep_column_workout',kwargs.get('sep_column_workout',','),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Workoutdata seperator', 
        #                    info = 'Workout column seperator of CSV file',
        #                    ))
                            
        #self.sep_column_points = attrsman.add(cm.AttrConf( 'sep_column_points',kwargs.get('sep_column_points',','),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Point data seperator', 
        #                    info = 'Pointdata column seperator of CSV file',
        #                    ))
        
        self._init_filter_time(**kwargs)
        
        self._proj = None
        self._offset = None
    
    def project(self, lons,lats):
        if self._proj is None:
            self._proj, self._offset = self.parent.get_proj_and_offset()
        x,y = self._proj(lons, lats)
        return np.transpose(np.concatenate(([x+self._offset[0]],[y+self._offset[1]]),axis=0))
        
        
        
    def validate_trip(self, dist, duration, speed_av = -1):
        if speed_av<0:
            if duration>1.0:
                speed_av = dist/duration
            else:
                speed_av = 0.0
        
        return (dist > self.dist_trip_min)\
                   & (dist < self.dist_trip_max)\
                   & (duration > self.duration_trip_min)\
                   & (speed_av > self.speed_trip_min)\
                   & (speed_av < self.speed_trip_max)
                   
    def do(self):
        print('TraceImporter.do')
        if self.year == 2014:
            self.import_workouts_2014()
            self.import_points_2014()
        
        if self.year == 2015:
            self.import_workouts_2015()
            self.import_points_2015()
        
        if self.year == 2016:
            self.import_workouts_2016()
            self.import_points_2016()  
        
        if self.year == 2013:    
            self.import_points_2013()
             
        self.parent.points.project()
        return True
        
    def import_workouts_2016(self):
        #UserID                        TripID                        TimeStamp    Start DT                    Distance     ECC     AvgSpeed     TrackType     Sex     Year     Profession     Frequent User     ZIP     Source      TypeOfBike     TipeOfTrip     Max Spd
        #57249bcd88c537874f9fa1ae    57515edc88c537576ca3e16f    1464945480    2016-06-03T09:18:00.000Z    4.75    4.75        10.16        urban bicycle    F    1999    Studente        yes                    cy-web-gpx    MyBike        HomeToSchool    25.45

        j_id_user, j_id_trip, j_timestamp, j_date, j_dist, j_dist_copy, j_speed_av, j_tracktype, j_sex, j_year, j_profession, j_frequent, j_zip, j_device, j_biketype, j_purpose, j_speedmax = list(range(17))
        n_cols = 17
        null = 'NULL'
        trips = self.parent.trips
        persons = self.parent.persons
        #exist_id_person_sumo = persons.ids_sumo.has_index
        ids_person_sumo = {}
        
        #ids_trips = []
        scenario = self.parent.get_scenario()
        
        get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        id_type = get_vtype_for_mode(self.id_mode)
        
        f=open(self.workoutsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_workouts_2016 %s'%os.path.basename(self.workoutsfilepath))
        i_line = 0
        sep = ';'
        i_line = 0
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        for line in f.readlines()[1:]:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            #print '    cols',cols
            if len(cols)>=n_cols:
                # row is complete
                #print '  id_trip_sumo',cols[j_id_trip]
                #if cols[j_dist] != null:
                dist = get_colvalue(cols[j_dist])*1000.0
                speed_av =  get_colvalue(cols[j_speed_av])/3.6
                if speed_av>0:
                    duration = dist/speed_av
                else:
                    duration = 0.0
                
                if self.validate_trip(dist, duration, speed_av):
                    timestamp = get_colvalue(cols[j_timestamp])
                    
                    if timestamp is not None:
                        #print '    valid time stamp',timestamp,self.is_timestamp_ok(timestamp)
                        
                        if self.is_timestamp_ok(timestamp):
                            id_trip = trips.make(   id_sumo = cols[j_id_trip],
                                                    id_vtype = id_type,
                                                    timestamp = timestamp,
                                                    distance_gps = dist,
                                                    duration_gps = duration,
                                                    speed_average = speed_av,
                                                    speed_max = get_colvalue(cols[j_speedmax])/3.6,
                                                    purpose = cols[j_purpose].strip(),
                                                    device = cols[j_device].strip(),
                                                    )
                                                    
                            #ids_trips.append(id_trip)
                            #j_id_user, j_id_trip, j_timestamp, j_date, j_dist, j_speed_av, j_tracktype, j_sex, j_year, j_profession, j_frequent, j_zip 
                            
                            zipcode=   cols[j_zip].strip()
                            if zipcode == 'undefined':
                                zipcode = ''
                            
                            year=cols[j_year]
                            if year.isdigit():
                                year_birth = int(year)
                            else:
                                year_birth = -1
                            
                            
                            
                            id_pers = persons.make(   id_sumo = cols[j_id_user].strip(),
                                            id_trip = id_trip,
                                            gender = cols[j_sex].strip(),
                                            year_birth = year_birth,
                                            occupation = cols[j_profession].strip(),
                                            is_frequent_user = cols[j_frequent].strip().lower() == 'yes',
                                            zip = zipcode,
                                            ) 
                            #print '  id_trip,id_trip_sumo,id_pers',id_trip,cols[j_id_trip], id_pers
                            #print
                
                else:
                    print('  invalid trip',cols[j_id_trip])
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.workoutsfilepath))
                print('  cols =',cols)
            
            i_line += 1
                                            
    
    def import_points_2016(self):
        print('import_points_2016')
        #    0          1    2            3         4          5         6    7        
        # TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
        # 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
        #5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid

        ind_id_path = 0
        ind_time = 1
        ind_lat = 2
        ind_lon = 3
        ind_alt = 4
        ind_dist = 5
        ind_speed = 6
        ind_type = 7
        
        n_cols = 8
        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
        
        trips = self.parent.trips
        points = self.parent.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_points_2016 %s'%os.path.basename(self.pointsfilepath))
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        is_valid_trip = False
                  
        n_points_imported = 0
        for line in f.readlines():

            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                id_trip_sumo_current = cols[ind_id_path]
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    
                    if is_valid_trip:
                        #print '  store past points for valid trip',id_trip
                        ids_point = points.add_rows(\
                                        timestamps = timestamps,
                                        ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                                        longitudes = longitudes,
                                        latitudes = latitudes,
                                        altitudes = altitudes,
                                        )
                                        
                        trips.set_points(id_trip, ids_point)
                        #print '    timestamps',timestamps
                        if len(timestamps)>1:
                            trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
                            #print '    durations_gps',timestamps[-1]-timestamps[0]
                    # check if new trip is valid
                    if exist_id_trip_sumo(id_trip_sumo_current):
                        
                        is_valid_trip = True # start recording
                        id_trip = get_id_trip(id_trip_sumo_current)
                        #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                        
                        # ids_point_sumo = [] # useless?
                        timestamps = []
                        ids_trip = []
                        longitudes = []
                        latitudes = []
                        altitudes = []
                        
                    else:
                        #print '    trip',id_trip_sumo_current,'does not exisit'
                        is_valid_trip  = False
                        id_trip = -1
                        
                    id_trip_sumo = id_trip_sumo_current
                
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(get_colvalue(cols[ind_time]))
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    altitudes.append(get_colvalue(cols[ind_alt]))
      
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                print('  cols =',cols)
            
                
            i_line += 1
        
        # register points of last trip
        if is_valid_trip:
            # store past points for valid trip
            ids_point = points.add_rows(\
                            timestamps = timestamps,
                            ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                            longitudes = longitudes,
                            latitudes = latitudes,
                            altitudes = altitudes,
                            )
                            
            trips.set_points(id_trip, ids_point)
            
            if len(timestamps)>1:
                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
                            
        #self.odtab.print_rows() 
     
        f.close()
        
        
        
    def import_workouts_2015(self):
        
        # 2015 csv 
        # workouts
        #UserID     TripID     TimeStamp     Start DT      Distance     AvgSpeed     TrackType     Sex     Year     Profession     Frequent User     ZIP
        #54eb068de71f393530a9a74d    54eb0737e71f394c2fa9a74d    1424692504    Mon, 23 Feb 2015 11:55:04 GMT    0    0    urban bicycle    M    1987    Developer    no    
        #54eb9374e71f39f02fa9a750    5505cb04e71f39542e25e2d4    1426442994    Sun, 15 Mar 2015 18:09:54 GMT    0    0.7    urban bicycle    M    1974    Worker    yes    40128

        j_id_user, j_id_trip, j_timestamp, j_date, j_dist, j_speed_av, j_tracktype, j_sex, j_year, j_profession, j_frequent, j_zip = list(range(12))
        n_cols = 12
        null = 'NULL'
        trips = self.parent.trips
        persons = self.parent.persons
        #exist_id_person_sumo = persons.ids_sumo.has_index
        ids_person_sumo = {}
        
        #ids_trips = []
        scenario = self.parent.get_scenario()
        
        get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        id_type = get_vtype_for_mode(self.id_mode)
        
        f=open(self.workoutsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_workouts_2015 %s'%os.path.basename(self.workoutsfilepath))
        i_line = 0
        sep = ';'
        i_line = 1
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        for line in f.readlines()[1:]:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '\n    len(cols)',len(cols),n_cols
            #print '    cols',cols
            if len(cols)>=n_cols:
                # row is complete
                #print '  id_trip_sumo "%s"'%cols[j_id_trip]
                #if cols[j_dist] != null:
                dist = get_colvalue(cols[j_dist])*1000.0
                speed_av =  get_colvalue(cols[j_speed_av])/3.6
                #duration = get_colvalue(cols[j_duration])
                #if duration>0:
                #    speed_av = dist/duration
                #else:
                #    speed_av = 0.0
                #print '  dist',dist,'speed_av',speed_av
                if speed_av>0:
                    duration = dist/speed_av
                else:
                    duration = 0.0
                
                if self.validate_trip(dist, duration, speed_av):
                    #print  '    parametric conditions verified'
                    timestamp = get_colvalue(cols[j_timestamp])
                    if timestamp is not None:
                        #print '    valid time stamp',timestamp#,self.is_timestamp_ok(timestamp)
                        if self.is_timestamp_ok(timestamp):
                            id_trip = trips.make(   id_sumo = cols[j_id_trip],
                                                    id_vtype = id_type,
                                                    timestamp = timestamp,
                                                    distance_gps = dist,
                                                    duration_gps = duration,
                                                    speed_average = speed_av,
                                                    )
                            #ids_trips.append(id_trip)
                            #j_id_user, j_id_trip, j_timestamp, j_date, j_dist, j_speed_av, j_tracktype, j_sex, j_year, j_profession, j_frequent, j_zip 
                            
                            zip=   cols[j_zip].strip()
                            if zip == 'undefined':
                                zip = ''
                            
                            year=cols[j_year]
                            if year.isdigit():
                                year_birth = int(year)
                            else:
                                year_birth = -1
                                
                            persons.make(   id_sumo = cols[j_id_user].strip(),
                                            id_trip = id_trip,
                                            gender = cols[j_sex].strip(),
                                            year_birth = year_birth,
                                            occupation = cols[j_profession].strip(),
                                            is_frequent_user = cols[j_frequent].strip() == 'yes',
                                            zip = zip,
                                            )  
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.workoutsfilepath))
                #print '  cols =',cols
            
            i_line += 1
    
    def import_points_2015(self):
        print('import_points_2015')
        #    0          1    2   3         4          5         6         7       8  
        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
        #54eb0737e71f394c2fa9a74d,1424692509,"Mon, 23 Feb 2015 11:55:09 GMT",44.499096,11.361185,49.419395,0,0.000815,start

        ind_id_path = 0
        ind_time = 1
        ind_day = 2
        ind_date = 3
        ind_lat = 4
        ind_lon = 5
        ind_alt = 6
        ind_dist = 7
        ind_speed = 8
        ind_type = 9
        
        n_cols = 10
        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
        
        trips = self.parent.trips
        points = self.parent.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_points_2015 %s'%os.path.basename(self.pointsfilepath))
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        is_valid_trip = False
                  
        n_points_imported = 0
        for line in f.readlines():

            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                id_trip_sumo_current = cols[ind_id_path]
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    
                    if is_valid_trip:
                        #print '  store past points for valid trip',id_trip
                        ids_point = points.add_rows(\
                                        timestamps = timestamps,
                                        ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                                        longitudes = longitudes,
                                        latitudes = latitudes,
                                        altitudes = altitudes,
                                        )
                                        
                        trips.set_points(id_trip, ids_point)
                        #print '    timestamps',timestamps
                        if len(timestamps)>1:
                            trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
                            #print '    durations_gps',timestamps[-1]-timestamps[0]
                    # check if new trip is valid
                    if exist_id_trip_sumo(id_trip_sumo_current):
                        
                        is_valid_trip = True # start recording
                        id_trip = get_id_trip(id_trip_sumo_current)
                        #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                        
                        # ids_point_sumo = [] # useless?
                        timestamps = []
                        ids_trip = []
                        longitudes = []
                        latitudes = []
                        altitudes = []
                        
                    else:
                        #print '    trip',id_trip_sumo_current,'does not exisit'
                        is_valid_trip  = False
                    
                    id_trip_sumo = id_trip_sumo_current
                
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(get_colvalue(cols[ind_time]))
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    altitudes.append(get_colvalue(cols[ind_alt]))
      
                
            else:
                print('WARNING: inconsistent number of columns (%d) in line %d, file %s'%(len(cols),i_line,self.pointsfilepath))
                #print '  cols =',cols
            
                
            i_line += 1
        
        # register points of last trip
        if is_valid_trip:
            # store past points for valid trip
            ids_point = points.add_rows(\
                            timestamps = timestamps,
                            ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                            longitudes = longitudes,
                            latitudes = latitudes,
                            altitudes = altitudes,
                            )
                            
            trips.set_points(id_trip, ids_point)
            
            if len(timestamps)>1:
                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
                            
       
     
        f.close()
          
 
                                
    
    def import_workouts_2014(self):
        print('import_workouts_2014')
        # 2014 ecomondo workouts
        #id            pointDBNode    pointPathId    startTime            distance    duration    sport    calories    maxSpeed    altitudeMin    altitudeMax    metersAscent    metersDescent
        #329308466    7            37073516    2014-05-01 19:00:00        26        15600        1        1182.64        NULL        NULL        NULL        NULL            NULL 
        # 0         1           2            3                       4          5       6          7        8           9          10          11                   12             
        
        j_id, j_node, j_id_trip, j_time, j_dist,j_duration = list(range(6))
        j_v_max = 8
        n_cols = 13
        null = 'NULL'
        trips = self.parent.trips
        #persons = self.parent.persons # no person data in 2014 :(
        ids_person_sumo = {}
        
        ids_trips = []
        scenario = self.parent.get_scenario()
        
        #get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
        id_vtype = scenario.demand.vtypes.get_vtype_for_mode(self.id_mode)
        
        f=open(self.workoutsfilepath,'r', encoding="utf-8")
        #if self._logger: self._logger.w('import_workouts_2014 %s'%os.path.basename(self.workoutsfilepath))
        i_line = 0
        sep = ','#self.sep_column_workout
        
        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
        for line in f.readlines()[1:]:
            #if True:#i_line>1:
            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                # row is complete
                
                #if cols[j_dist] != null:
                dist = get_colvalue(cols[j_dist])*1000.0
                duration = get_colvalue(cols[j_duration])
                if duration>0:
                    speed_av = dist/duration
                else:
                    speed_av = 0.0
                
                
                if self.validate_trip(dist, duration, speed_av):
                    #print  'parametric conditions verified'
                    timestamp = calc_seconds(cols[j_time])
                    if timestamp is not None:
                        #print '  valid time stamp',timestamp
                        if self.is_timestamp_ok(timestamp):
                            id_trip = trips.make(   id_sumo = cols[j_id_trip],
                                                    id_vtype = id_vtype,
                                                    timestamp = timestamp,
                                                    distance_gps = dist,
                                                    duration_gps = duration,
                                                    speed_average = speed_av,
                                                    speed_max = get_colvalue(cols[j_v_max])/3.6,
                                                    )
                            ids_trips.append(id_trip)
        
    
    def import_points_2014(self):
        print('import_points_2014')
        # csv2014
        #pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
        #4,61565791,23648171762,2013-05-01 06:33:58,44.501085,11.372906,NULL,0,NULL,2,NULL
        
        #pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
        #7,37073516,15138524460,NULL,44.51579,11.36257,NULL,NULL,NULL,NULL,NULL
        
        #pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
        #7,37073516,15138524460,NULL,44.51579,11.36257,NULL,NULL,NULL,NULL,NULL
                            
        # Endomondo export format
        #pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
        #    0          1         2   3         4          5         6       7       8          9          10
        ind_id_path = 1
        ind_id_point = 2
        ind_time = 3
        ind_lat = 4
        ind_lon = 5
        ind_alt = 6
        ind_dist = 7
        ind_speed = 10
        
        n_cols = 11
        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
        
        trips = self.parent.trips
        points = self.parent.points
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_points_2014 %s'%os.path.basename(self.pointsfilepath))
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        is_valid_trip = False
                  
        n_points_imported = 0
        for line in f.readlines():

            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            if len(cols)==n_cols:
                id_trip_sumo_current = cols[ind_id_path]
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    
                    if is_valid_trip:
                        #print '  store past points for valid trip',id_trip
                        ids_point = points.add_rows(\
                                        timestamps = timestamps,
                                        ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                                        longitudes = longitudes,
                                        latitudes = latitudes,
                                        altitudes = altitudes,
                                        )
                                        
                        trips.set_points(id_trip, ids_point)
                    
                    # check if new trip is valid
                    if exist_id_trip_sumo(id_trip_sumo_current):
                        
                        is_valid_trip = True # start recording
                        id_trip = get_id_trip(id_trip_sumo_current)
                        #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                        
                        # ids_point_sumo = [] # useless?
                        timestamps = []
                        ids_trip = []
                        longitudes = []
                        latitudes = []
                        altitudes = []
                        
                    else:
                        #print '    trip',id_trip_sumo_current,'does not exisit'
                        is_valid_trip  = False
                    
                    id_trip_sumo = id_trip_sumo_current
                
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(calc_seconds(cols[ind_time]))
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    altitudes.append(get_colvalue(cols[ind_alt]))
      
                
            else:
                print('WARNING: inconsistent columns in line %d, file %s'%(i_line,os.path.basename(self.pointsfilepath)))
            
                
            i_line += 1
        
        # register points of last trip
        if is_valid_trip:
            # store past points for valid trip
            ids_point = points.add_rows(\
                            timestamps = timestamps,
                            ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                            longitudes = longitudes,
                            latitudes = latitudes,
                            altitudes = altitudes,
                            )
                            
            trips.set_points(id_trip, ids_point)
                            
        #self.odtab.print_rows() 
     
        f.close()
    
    
    
    
    def import_points_2013(self):
        print('import_points_2013')
        #pointDBNode,   pointPathId,    id,     timestamp,          latitude,   longitude,  altitude,distance,  heartRate,instruction,speed
        #4,             61565791,   23648171762,2013-05-01 06:33:58,44.501085,  11.372906,  NULL,       0,      NULL,       2,          NULL
        #0                  1         2          3                      4          5            6       7       8           9           10
        ind_id_path = 1
        ind_id_point = 2
        ind_time = 3
        ind_lat = 4
        ind_lon = 5
        ind_alt = 6
        ind_dist = 7
        ind_speed = 10
        
        n_cols = 11
        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
        
        trips = self.parent.trips
        points = self.parent.points
        
        
        exist_id_trip_sumo = trips.ids_sumo.has_index
        get_id_trip = trips.ids_sumo.get_id_from_index
        id_vtype = self.parent.get_scenario().demand.vtypes.get_vtype_for_mode(self.id_mode)
        sep = ','#self.sep_column_points
            
        f=open(self.pointsfilepath,'r', encoding="utf-8")
        if self._logger: self._logger.w('import_points_2013 %s'%os.path.basename(self.pointsfilepath))
        i_line = 0
        id_trip_sumo = None
        id_trip = -1
        is_valid_trip = False
        timestamp_last = -1
        n_points_imported = 0
        timestamps = []
        
        for line in f.readlines():

            cols = line.split(sep)
            #print '    len(cols)',len(cols),n_cols
            if (len(cols)==n_cols) & (i_line>0):
                id_trip_sumo_current = cols[ind_id_path]
                if timestamp_last < 0:
                    timestamp_last = calc_seconds(cols[ind_time])
                    
                #print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
                if id_trip_sumo_current != id_trip_sumo:
                    # this point is part of new trip
                    
                    # validate trip data
                    if len(timestamps)==0:
                        is_valid_trip = False
                        
                    else:
                        coords = self.project(latitudes,longitudes)
                        distance = np.sum( np.sqrt(np.sum((coords[1:,:]-coords[:-1,:])**2,1)) )
                        duration = timestamps[-1]-timestamps[0]
                        if duration>0:
                            speed_av = distance/duration
                        else:
                            speed_av = 0.0
                        is_valid_trip = self.validate_trip(distance, duration,speed_av)
                    
                    if is_valid_trip:
                        #print '  store past points for valid trip',id_trip
                        id_trip = trips.make(   id_sumo = id_trip_sumo_current,
                                                    id_vtype = id_vtype,
                                                    timestamp = timestamp_last,
                                                    distance_gps = distance,
                                                    duration_gps = duration,
                                                    speed_average = speed_av,
                                                    )
                        
                        
                        ids_point = points.add_rows(\
                                        timestamps = timestamps,
                                        ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                                        longitudes = longitudes,
                                        latitudes = latitudes,
                                        altitudes = altitudes,
                                        )
                                        
                        trips.set_points(id_trip, ids_point)
                    
                    # check if new trip is valid
                    #if exist_id_trip_sumo(id_trip_sumo_current):
                        
                    is_valid_trip = True # start recording
                    #id_trip = get_id_trip(id_trip_sumo_current)
                    #print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
                    
                    # ids_point_sumo = [] # useless?
                    timestamp_last = calc_seconds(cols[ind_time])
                    timestamps = []
                    ids_trip = []
                    longitudes = []
                    latitudes = []
                    altitudes = []
                        
                    #else:
                    #    #print '    trip',id_trip_sumo_current,'does not exisit'
                    #    is_valid_trip  = False
                    
                    id_trip_sumo = id_trip_sumo_current
                
                if is_valid_trip:
                    #print '    store point timestamp',cols[ind_time]
                    # current point belongs to a valid trip
                    #ids_point_sumo.append(cols[ind_id_point])
                    timestamps.append(calc_seconds(cols[ind_time]))
                    #ids_trip.append(id_trip)
                    longitudes.append(get_colvalue(cols[ind_lon]))
                    latitudes.append(get_colvalue(cols[ind_lat]))
                    altitudes.append(get_colvalue(cols[ind_alt]))
      
                
            else:
                print('WARNING: inconsistent columns in line %d, file %s'%(i_line,os.path.basename(self.pointsfilepath)))
            
                
            i_line += 1
        
        # register points of last trip
        if is_valid_trip:
            # store past points for valid trip
            ids_point = points.add_rows(\
                            timestamps = timestamps,
                            ids_trip = id_trip*np.ones(len(timestamps), dtype = np.int32),
                            longitudes = longitudes,
                            latitudes = latitudes,
                            altitudes = altitudes,
                            )
                            
            trips.set_points(id_trip, ids_point)
                            
        #self.odtab.print_rows() 
     
        f.close()
    




class GpxImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('GpxImporter.__init__',mapmatching.get_ident())
        self._init_common(  'gpximporter', 
                            parent = mapmatching,
                            name = 'GPX Importer', 
                            logger = logger,
                            info ='Import GPS traces from GPX files.',
                            
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        
        # here we ged classes not vehicle type
        # specific vehicle type within a class will be generated later 
        modechoices = scenario.net.modes.names.get_indexmap()
        
        #print '  modechoices',modechoices
        self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'], 
                                        groupnames = ['options'], 
                                        choices = modechoices,
                                        name = 'Mode', 
                                        info = 'Transport mode to be matched.',
                                        ))
                                            
        self.filepaths = attrsman.add(
                    cm.AttrConf('filepaths',kwargs.get('filepath',rootfilepath+'.gpx'),
                                    groupnames = ['options'],
                                    perm='rw', 
                                    name = 'filenames', 
                                    wildcards = 'GPX file (*.gpx)|*.gpx|XML file (*.xml)|*.xml',
                                    metatype = 'filepaths',
                                    info = """Paths and file name of GPX file with GPS traces in XML format.""",
                                    ))
        self._init_traceoptions(**kwargs)
                        
    def do(self):
        """
        Reads endomondo gpx xml file and stores point data in traces table.
        If there is no traces table, one will be initialized and returned
        """
        print('GpxImporter.do',self.filepaths)
        mapmatching = self.parent
        #scenario = mapmatching.get_scenario()
        logger = self.get_logger()
        
        
        get_vtype_for_mode = mapmatching.get_scenario().demand.vtypes.get_vtype_for_mode
                            
        parser = GpxParser(mapmatching.trips, mapmatching.points, 
                            logger = logger,
                            id_vtype = get_vtype_for_mode(self.id_mode),
                            dist_trip_min = self.dist_trip_min,
                            dist_trip_max = self.dist_trip_max,
                            duration_trip_min = self.speed_trip_min,
                            duration_trip_max = self.duration_trip_max, 
                            speed_trip_min = self.speed_trip_min,
                            speed_trip_max = self.speed_trip_max,
                            )
        for filepath in self.filepaths.split(','):
            print('  parse gpx file',    filepath)            
            parse(filepath.strip(), parser)
            
        ids_trips = parser.get_ids_trip()
        if logger: logger.w('imported %d traces, project coordinates...'%len(ids_trips))
        
        # recalculate projection for network in scenario
        #for id_trace in ids_traces:
        #    traces.pointsets.get(id_trace).project( traces._proj, traces.offset)
            
        mapmatching.points.project()
        
        for id_trip in mapmatching.trips.get_ids():
            ids_points = mapmatching.trips.ids_points[id_trip]
            init_point = mapmatching.points.coords[ids_points[0]]
            final_point = mapmatching.points.coords[ids_points[-1]]
              
            dist = np.sqrt(   (init_point[0]- final_point[0])**2\
                                                + (init_point[1]- final_point[1])**2 )
            duration = mapmatching.trips.durations_gps[id_trip]
##            print init_point, final_point, dist
            mapmatching.trips.distances_gps[id_trip] = dist
            if duration >0:
                mapmatching.trips.speeds_average[id_trip] = dist/duration
        
        if logger: logger.w('imported %d trips done.'%len(ids_trips))
        return True
                                                                                                            
class GpxParser(handler.ContentHandler):
    """Reads endomondo gpx xml file and parses lat,lon, ele and time.
    """

    def __init__(self, trips, points, logger = None,
                            id_vtype = 0,
                            dist_trip_min = 10.0,
                            dist_trip_max = 50000.0,
                            duration_trip_min = 0.5,
                            duration_trip_max = 999999.0, 
                            speed_trip_min = 0.1,
                            speed_trip_max = 100.0,
                            ):
        
        self._logger = logger
        self._points = points
        self._trips = trips
        self._id_vtype = id_vtype
        self._dist_trip_min = dist_trip_min
        self._dist_trip_max = dist_trip_max
        self._duration_trip_min = speed_trip_min
        self._duration_trip_max = duration_trip_max
        self._speed_trip_min = speed_trip_min
        self._speed_trip_max = speed_trip_max
                            
        self.reset_trip()
    
    
    
    def reset_trip(self):
        self._lons = []
        self._lats = []
        self._eles = []
        self._times =  []
        self._is_record_segment = False
        self._is_record_point = False
        self._is_time = False
        self._is_ele = False
        
        self._ids_trip = []
        self._ids_point = []
            
    def get_ids_trip(self):
        return self._ids_trip
    
    def get_ids_point(self):
        return self._ids_point
    

    def startElement(self, name, attrs):
        #if self._pointset  is None:
        
        

        
        if name == 'trkpt':
            self._is_record_point = True
            self._lons.append(float(attrs['lon']))
            self._lats.append(float(attrs['lat']))
        
        if name == 'time':
                self._is_time = True
                
        if name == 'ele':
                self._is_ele = True
        
        if name == 'trkseg':
            self._is_record_segment = True
            
        if name == 'trk':
            self._is_record_trip = True


                
    def characters(self, content):
        
        if self._is_time:
            #print '  got time',content[:-1]
            
            if self._is_record_point:
                # 2013-05-30T16:42:33Z 2014-01-26T12:32:21Z
                self._times.append( calc_seconds( content[:-1], 
                                                    sep_date_clock = 'T', 
                                                    sep_date = '-', 
                                                    sep_clock = ':', 
                                                    is_float = False))
                    
        if self._is_ele:
            #print 'characters content',content
            self._eles.append( float(content))
        
                
                
    def endElement(self, name):
        if name == 'ele':
            self._is_ele = False
            
        if name == 'time':
            self._is_time = False
            
        if name == 'trkpt':
            #print 'endElement: point',self._lon,self._lat,self._ele,self._time
            #self._is_ele = False
            #self._is_time = False
            self._is_record_point = False
        
        if name == 'trk':
            # here we could stop recording a segment
            # but currently we join all segments together
            pass
        
        if name == 'trk':
            #print 'endElement',len(self._lons)
            
            #print '  self._lons',self._lons
            #print '  self._lats',self._lats
            #print '  self._times',self._times
            
            # trip recording ends
            n_points = len(self._lons) 
            
            if (n_points > 0):
                timestamp = self._times[0]
                duration = self._times[-1]-timestamp
                #print '    timestamp',timestamp
                #print '    duration',duration,self._duration_trip_min,self._duration_trip_max
                # TODO: make this filter a functin of filtermixin
                if (duration > self._duration_trip_min)\
                    & (duration < self._duration_trip_max):
                        
                    id_trip = self._trips.make( id_vtype = self._id_vtype,
                                                timestamp = timestamp,
                                                #distance_gps = dist,
                                                duration_gps = duration
                                                #speed_average = speed_av,
                                                )
                    self._ids_trip.append(id_trip)
                    
                    if len(self._eles) == n_points:
                        altitudes = self._eles
                    else:
                        altitudes = np.zeros(n_points, dtype = np.float32)
                        
                    ids_point = self._points.add_rows(\
                                            timestamps = self._times,
                                            ids_trip = id_trip*np.ones(n_points, dtype = np.int32),
                                            longitudes = self._lons,
                                            latitudes = self._lats,
                                            altitudes = altitudes,
                                            )
                                            
                    self._trips.set_points(id_trip, ids_point)
                    self._trips.ids_sumo[id_trip] = str(id_trip)
                    

            
            self.reset_trip()
                    




class GtfsShapeImporter(FilterMixin):
    def __init__(self,  mapmatching, logger = None, **kwargs):
        print('GtfsShapeImporter.__init__',mapmatching.get_ident())
        self._init_common(  'gtfsshapeimporter', 
                            parent = mapmatching,
                            name = 'GTFS Importer', 
                            logger = logger,
                            info ='Import points from GTFS shape file definitions.',
                            
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        
        # here we ged classes not vehicle type
        # specific vehicle type within a class will be generated later 
        modechoices = scenario.net.modes.names.get_indexmap()
        
        #print '  modechoices',modechoices
        self.id_mode = attrsman.add(am.AttrConf('id_mode',  kwargs.get('id_mode',modechoices['bus']), 
                                        groupnames = ['options'], 
                                        choices = modechoices,
                                        name = 'Mode', 
                                        info = 'Transport mode to be matched.',
                                        ))
                                            
        self.gtfsdirpath = attrsman.add(cm.AttrConf(  'gtfsdirpath',kwargs.get('gtfsdirpath',rootfilepath),
                                        groupnames = ['options'], 
                                        perm='rw', 
                                        name = 'GTFS directory', 
                                        metatype = 'dirpath',
                                        info = 'GTFS directory.',
                                        ))
        
        separators = {'Comma':',','Quoted comma':'","'}
        self.separator = attrsman.add(cm.AttrConf( 'separator',kwargs.get('separator',","),
                            choices = separators,
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Column separation',
                            info = 'Characters used for column separation.',
                            ))
        self._init_traceoptions(**kwargs)
                        
    def do(self):
        """
        Reads endomondo gpx xml file and stores point data in traces table.
        If there is no traces table, one will be initialized and returned
        """
        print('GtfsShapeImporter.do',self.gtfsdirpath)
        mapmatching = self.parent
        
        mapmatching.get_attrsman().do_not_save_attrs(['gtfsdirpath'])
        mapmatching.gtfsdirpath = self.gtfsdirpath
    
            
        scenario = mapmatching.get_scenario()
        logger = self.get_logger()
        
        net = self.parent
        get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode#( id_mode)
        id_vtype = get_vtype_for_mode(self.id_mode)
        print('  self.id_mode',self.id_mode,'id_vtype',id_vtype)
        tripshapes = mapmatching.trips
        points = mapmatching.points
        

        id_mode_pt = self.id_mode#net.modes.get_id_mode("bus")
        sep = self.separator
        aa = '\"'
        filepath = os.path.join(self.gtfsdirpath, 'shapes.txt')
        f=open(filepath,'r', encoding="utf-8")
        ind_stop_desc = -1
        ind_lstop_url = -1
        ind_location_type = -1
        ind_parent_station = -1
        i = 0
        
        
        #shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence
        #line = f.readline()
        #print '  line[:-1] = *%s*'%(line[:-1],)
        for fileattr_raw in f.readline().split(','):# here sep is always "," (hopefully)
            
            fileattr = fileattr_raw.strip().strip(aa)
            fileattr = re.sub('\W+','', fileattr )
            #print '  check fileattr *%s*, %d'%(fileattr,i),fileattr=='shape_id',len(fileattr)
            if fileattr == 'shape_id':
                ind_shape_id = i
                
            elif fileattr == 'shape_pt_lat':
                ind_shape_pt_lat = i
            
            elif fileattr == 'shape_pt_lon':
                ind_shape_pt_lon = i
                
            elif fileattr == 'shape_pt_sequence':
                ind_shape_pt_sequence = i
                
           
            i += 1
        
        #n_cols = len(attrsinds)
        if logger: logger.w('import shapes from %s'%os.path.basename(filepath))
        n_imported = 0
        id_shape = -1
        ind_shape = 10**8
        for line in f.readlines():
            cols = line.split(sep)
            ind_shape_new = int(cols[ind_shape_pt_sequence].strip().strip(aa))
            if ind_shape_new < ind_shape:
                # save old shape
                if id_shape > -1:
                    ids_point = points.add_rows(    ids_trip = id_shape*np.ones(len(lons), dtype = np.int32),
                                                    longitudes = lons,
                                                    latitudes = lats,
                                                    altitudes = np.zeros(len(lons), dtype = np.float32),
                                                    )
                    tripshapes.set_points(id_shape, ids_point)
                
                # init new shape
                id_shape = tripshapes.add_row(  ids_sumo = cols[ind_shape_id].strip(aa),
                                                ids_vtype = id_vtype)
                                                
                print('  id_shape',id_shape,tripshapes.ids_vtype[id_shape],id_vtype)
                lons = []
                lats = []
                ind_shape = ind_shape_new
                
            lons.append(float(cols[ind_shape_pt_lon].strip(aa))) 
            lats.append(float(cols[ind_shape_pt_lat].strip(aa))) 
            
            ind_shape = ind_shape_new
        
        # save last
        if len(lats)>0:
            ids_point = points.add_rows(    ids_trip = id_shape*np.ones(len(lons), dtype = np.int32),
                                            longitudes = lons,
                                            latitudes = lats,
                                            altitudes = np.zeros(len(lons), dtype = np.float32),
                                            )
            tripshapes.set_points(id_shape, ids_point)        
        
        f.close()
        
        # recalculate projection for network in scenario
        #for id_trace in ids_traces:
        #    traces.pointsets.get(id_trace).project( traces._proj, traces.offset)
            
        mapmatching.points.project()
        
        if logger: logger.w('imported %d shape trips done.'%len(tripshapes))
        return True
    
        # project lon lat to net coordinates
        #points.project(net = net)
        
        #netmatcher.do()
        #tripshapes.print_attrs()
        
        
        return True  
    
class GtfsStopGenerator(Process):
    def __init__(self, ident='gtfsstopgenerator', mapmatching=None, results = None,  logger = None, **kwargs):
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
        print('GtfsGenerator.__init__')
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'GTFS Stop Generator', 
                            logger = logger,
                            info ='Imports information from GTFS data and generate stops on network.',
                            )
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        if hasattr(mapmatching,'gtfsdirpath'):
            gtfsdirpath =  mapmatching.gtfsdirpath
        else:
            gtfsdirpath =  rootfilepath
                      
        self.gtfsdirpath = attrsman.add(cm.AttrConf(  'gtfsdirpath',kwargs.get('gtfsdirpath',gtfsdirpath),
                                        groupnames = ['options'], 
                                        perm='rw', 
                                        name = 'GTFS directory', 
                                        metatype = 'dirpath',
                                        info = 'GTFS directory.',
                                        ))
                                    

        separators = {'Comma':',','Quoted comma':'","'}
        self.separator = attrsman.add(cm.AttrConf( 'separator',kwargs.get('separator',","),
                            choices = separators,
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Column separation',
                            info = 'Characters used for column separation.',
                            ))
        
        self.width_stop = attrsman.add(cm.AttrConf( 'width_stop',kwargs.get('width_stop',1.5),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Stop width',
                            unit = 'm',
                            info = 'Width of public transport stops.',
                            ))
                                                        
        self.width_stop = attrsman.add(cm.AttrConf( 'width_stop',kwargs.get('width_stop',1.5),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Stop width',
                            unit = 'm',
                            info = 'Width of public transport stops.',
                            ))
                            
        self.length_stop = attrsman.add(cm.AttrConf( 'length_stop',kwargs.get('length_stop',20.0),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Stop length',
                            unit = 'm',
                            info = 'Length of public transport stops.',
                            ))
        
        self.length_stop_min = attrsman.add(cm.AttrConf( 'length_stop_min',kwargs.get('length_stop_min',10.0),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Minmal stop length',
                            unit = 'm',
                            info = 'Minimal length of public transport stops. Minimal stop length is used in case the link is too short for regular stop length',
                            ))
                                                
        self.length_stopedge_min = attrsman.add(cm.AttrConf( 'length_stopedge_min',kwargs.get('length_stopedge_min',22.0),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Min. length Stopedge',
                            unit = 'm',
                            info = 'Minimum length edge .',
                            ))
                            
        self.dist_edge_stop_min = attrsman.add(cm.AttrConf( 'dist_edge_stop_min',kwargs.get('dist_edge_stop_min',5.0),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Min. stop-edge dist',
                            unit = 'm',
                            info = 'Minimum distance between edge and public transport stop.',
                            ))
        
        self.dist_edgesearch = attrsman.add(cm.AttrConf( 'dist_edgesearch',kwargs.get('dist_edgesearch',40.0),
                            groupnames = ['options'],
                            perm='rw',
                            name = 'Edge search range',
                            unit = 'm',
                            info = 'Range in which alorithm is detecting edges around given stop coordinates.',
                            ))
        
        self.n_edgesearch = attrsman.add(cm.AttrConf( 'n_edgesearch',kwargs.get('n_edgesearch',20),
                            groupnames = ['options'],
                            name = 'Edge search number',
                            info = 'Maximum number of closest edges which alorithm considers when detecting edges around given stop coordinates.',
                            ))
        self.is_clear_ptstops = attrsman.add(cm.AttrConf( 'is_clear_ptstops', kwargs.get('is_clear_ptstops',True),
                                    groupnames = ['options'], 
                                    perm='rw', 
                                    name = 'Clear PT stops',
                                    info = 'Clear previously existing public transport line stops.' 
                                    )) 
                                                                                        
        
    #def place_stop(self, id_stop, name_stop, lat_stop, lon_ stop, **kwargs):
        
    def do(self):
        print('GtfsStopGenerator.do')
        # go through shapes.txt and match shapes with id_edges
        # > shape_id -> ids_edge
        # done through mapmatching
        
        mapmatching = self.parent
        mapmatching.gtfsdirpath = self.gtfsdirpath
        
        logger = self.get_logger()
        scenario = mapmatching.get_scenario()
        net = scenario.net
        edges = net.edges
        lanes = net.lanes
        ptstops = net.ptstops
        
        mapmatching.get_attrsman().do_not_save_attrs([  'trip_stops',\
                                                        'map_service_to_trips',
                                                        'map_trip_to_shape',
                                                        'trip_times',
                                                        'gtfsstop_to_simstop',
                                                        'map_trip_to_route',
                                                        'ids_service_done',
                                                        ])
        
        if self.is_clear_ptstops:
            ptstops.clear()
            mapmatching.clear_gtfs()
            
            
            
        
        
        map_service_to_trips = mapmatching.map_service_to_trips
        map_trip_to_shape = {}
        map_trip_to_route  = mapmatching.map_trip_to_route
        trip_stops = mapmatching.trip_stops
        trip_times = mapmatching.trip_times
        ids_service_done = mapmatching.ids_service_done
            
            

        
        gpstrips = mapmatching.trips
        get_id_gpstrip = gpstrips.ids_sumo.get_id_from_index
        ids_edges_shape = self.parent.trips.routes.get_value().ids_edges
        ids_valid = gpstrips.select_ids(gpstrips.are_selected.get_value())
        ids_gtsshape_set = set(gpstrips.ids_sumo[ids_valid])# = ids of matched trips
        
        # define global variables which will not be saved by mapmatching
        #mapmatching.get_attrsman().do_not_save_attrs(['gtfsdirpath'])
        
        
        
        
        proj, offset = self.parent.get_proj_and_offset()

        sep = self.separator
        
            
        aa = '\"'
        
        ## go through trips.txt and fetch for each shape_id all trip_ids
        ## > shape_id -> trip_ids , ids_edge
        
        #
        filepath = os.path.join(self.gtfsdirpath, 'trips.txt')
        f=open(filepath,'r', encoding="utf-8")
        # shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence
        #ind_route_id = 0
        #ind_service_id = 1
        #ind_trip_id = 2
        #ind_direction_id = 3
        #ind_shape_id = 4

        
        
        #f.readline()
        
        ## procedure to identify colums
        i = 0
        for fileattr_raw in f.readline().split(','):# here use always comma, heading not quoted (hopefully)
           fileattr = fileattr_raw.strip().strip(aa)
           fileattr = re.sub('\W+','', fileattr )
           print('  check fileattr *%s*, %d'%(fileattr,i))
           if fileattr == 'route_id':
               ind_route_id = i
               
           elif fileattr == 'service_id':
               ind_service_id = i
           
           elif fileattr == 'trip_id':
               ind_trip_id = i
               
           elif fileattr == 'direction_id':
               ind_direction_id = i
               
           elif fileattr == 'shape_id':
               ind_shape_id = i
               
           i += 1
        #print '  check ind_route_id',ind_route_id
        #n_cols = len(attrsinds)
        if logger: logger.w('import trips from %s'%os.path.basename(filepath))
        id_shape = -1
        
        
        
        
        
        
        for line in f.readlines():
            cols = line.split(sep)
            #map_shape_to_trip[cols[ind_shape_id].strip().strip(aa)] = cols[ind_shape_id].strip().strip(aa)
            #print '    trips.txt',cols
            #if int(cols[ind_direction_id].strip().strip(aa)) == 0:
            id_shape = cols[ind_shape_id].strip().strip(aa)
            id_shape = re.sub('^[a-zA-Z_-]*$','', id_shape)
            print('        found id_shape',ind_shape_id,cols[ind_shape_id].strip().strip(aa),'id_shape',id_shape,id_shape in ids_gtsshape_set)
            if id_shape in ids_gtsshape_set:
                id_trip = cols[ind_trip_id].strip().strip(aa)
                id_trip = re.sub('^[a-zA-Z_-]*$','', id_trip)
                
                map_trip_to_shape[id_trip] = id_shape
                
                id_service = cols[ind_service_id].strip().strip(aa)
                id_service = re.sub('^[a-zA-Z_-]*$','', id_service)
                
                if 1:#id_service not in ids_service_done:
                
                    if id_service not in map_service_to_trips:
                        map_service_to_trips[id_service] = []
                    
                    map_service_to_trips[id_service].append(id_trip)
                    
                    #if 1:# debug
                    #id_route = cols[ind_route_id].strip().strip(aa)
                    #if map_service_to_route.has_key(id_service):
                    #    if id_route != map_service_to_route[id_service]:
                    #        print '      WARNING: id_service',id_service,'already defined for id_route',map_service_to_route[id_service]
                    #    
                    #else:
                    #    map_service_to_route[id_service] = cols[ind_route_id].strip().strip(aa)
                    id_route = cols[ind_route_id].strip().strip(aa)
                    id_route = re.sub('^[a-zA-Z_-]*$','', id_route)
                    map_trip_to_route[id_trip] = id_route
                    print('      *id_trip',id_trip,'id_service',id_service,'id_route',id_route,'-> id_shape',id_shape)
                    
        f.close()
        
        print('  result: len(map_trip_to_shape)',len(map_trip_to_shape),'different shapes',len(set(map_trip_to_shape.values())),'of',len(ids_gtsshape_set),'len(map_service_to_trips)',len(map_service_to_trips))
         
        # debug
        #if 0:
        #    print '  check map_service_to_trips'
        #    for id_service, ids_trip in map_service_to_trips.items():
        #        print '    check id_service',id_service,'ids_trip',ids_trip
        
        
        #if input('Press 1 to continue and 0 to exit') == 0:
        #    return 1
            
        #ids_trip = {}
        #for id_shape in gpstrips.ids_sumo[gpstrips.get_ids()]:
        #    ids_trip[id_shape] = map_shape_to_trip[id_shape]
        
        ## go through stop_times.txt and fetch for each shape_id the trip_id  
        ## with the maximum number of    stop_ids_max
        ## > shape_id -> trip_id , ids_edge , ids_stop_max
        #trip_id    arrival_time    departure_time    stop_id    stop_sequence

        filepath = os.path.join(self.gtfsdirpath, 'stop_times.txt')
        f = open(filepath,'r', encoding="utf-8")
        ind_trip_id = 0
        ind_arrival_time = 1
        ind_departure_time = 2
        ind_stop_id = 3
        
        
        i = 0
        f.readline()
        for line in f.readlines():
            cols = line.split(sep)
            id_trip = cols[ind_trip_id].strip().strip(aa)
            id_trip = re.sub('^[a-zA-Z_-]*$','', id_trip)
            id_stop = cols[ind_stop_id].strip().strip(aa)
            id_stop = re.sub('^[a-zA-Z_-]*$','', id_stop)
            print('  id_trip',id_trip,id_trip in map_trip_to_shape,'found stop',id_stop)
            
            if id_trip in map_trip_to_shape:
                if id_trip not in trip_stops:
                    trip_stops[id_trip] = []
                    trip_times[id_trip] = []
                print('    for id_trip',id_trip,'* found id_stop',id_stop,'at id_shape',map_trip_to_shape[id_trip])
                trip_stops[id_trip].append(id_stop)
                t_depart = cols[ind_departure_time].strip().strip(aa)
                t_depart = re.sub('^[a-zA-Z_-]*$','', t_depart)
                trip_times[id_trip].append(t_depart)
            
            
        f.close()
        print('  result: len(trip_stops)',len(trip_stops))
        
        # dictionary to map stop ID to shape ID that will be used 
        # to identify edge of stop
        stop_shapes = OrderedDict()
        for id_trip, ids_stop in trip_stops.items():
            print('  stop_to_shape id_trip',id_trip,'ids_stop',ids_stop)
            #print '    ',
            for id_stop in ids_stop:
                #print (map_trip_to_shape.has_key(id_trip),map_trip_to_shape.has_key(id_trip)),
                # currently not all trips have shapes (unless include direction = 1)
                if (id_trip in map_trip_to_shape):
                    #print '*',
                    ids_edges = ids_edges_shape[gpstrips.ids_route_matched[get_id_gpstrip(map_trip_to_shape[id_trip])]]
                    # edges of  shape of new trip
                    if ids_edges is not None:
                        n_edges = len(ids_edges)
                    else:
                        n_edges = 0
                                
                    if id_stop in stop_shapes:
                        # use new shape if shape is longer than previous shape
                        if  n_edges > len(ids_edges_shape[gpstrips.ids_route_matched[get_id_gpstrip(stop_shapes[id_stop])]]):
                            stop_shapes[id_stop] = map_trip_to_shape[id_trip]
                        
                            #print '    Update id_stop',id_stop,'-> id_shape',map_trip_to_shape[id_trip],'of id_trip',id_trip,n_edges
                    else:
                        # first trip associated with stop
                        #print '    Add id_stop',id_stop,'-> id_shape',map_trip_to_shape[id_trip],'of id_trip',id_trip,n_edges
                        stop_shapes[id_stop] = map_trip_to_shape[id_trip]
                    
            #print '\n'
        print('  result: len(stop_shapes)',len(stop_shapes),'len(map_trip_to_shape)',len(map_trip_to_shape))
            
        ## go through stops.txt and find for each id_stop the 
        ## id_edge_stop and pos_edge_stop
        ## > id_stop -> id_edge_stop , pos_edge_stop
        
        filepath = os.path.join(self.gtfsdirpath, 'stops.txt')
        f=open(filepath,'r', encoding="utf-8")
        #stop_id    stop_name    stop_lat    stop_lon    location_type    parent_station

        ind_stop_id = 0
        ind_stop_name = 1
        ind_stop_lat = 2
        ind_stop_lon = 3
        
        n_stop = len(stop_shapes)
        ids_stop_gtfs = []
        stopnames = []
        lats = []
        lons = []
        
        #lons_stops = np.zeros(n_stop+1, dtype = np.float32)
        #lans_stops = np.zeros(n_stop+1, dtype = np.float32)
        f.readline()
        for line in f.readlines():
            cols = line.split(sep)
            id_stop = cols[ind_stop_id].strip().strip(aa)
            id_stop = re.sub('^[a-zA-Z_-]*$','', id_stop)
            #print '  found id_stop',id_stop,stop_shapes.has_key(id_stop),cols
            if id_stop in stop_shapes:
                ids_stop_gtfs.append(id_stop)
                lat_str = cols[ind_stop_lat].strip().strip(aa)
                lat_str = re.sub('^[w\.]+','', lat_str)
                print('    lat_str=*%s*'%(lat_str)) 
                lats.append(float(lat_str))
                
                lon_str = cols[ind_stop_lon].strip().strip(aa)
                lon_str = re.sub('^[w\.]+','', lon_str)
                lons.append(float(lon_str))
                
                stopname = cols[ind_stop_name].strip().strip(aa)
                stopname = re.sub('^[a-zA-Z_-]*$','', stopname)
                stopnames.append(stopname)

            
        f.close()
        
        print('  result: len(ids_stop_gtfs)',len(ids_stop_gtfs),len(lats))
        
        x,y = proj(lons, lats)
        coords = np.transpose(np.concatenate(([x+offset[0]],[y+offset[1]],[len(x)*[0.0]]),axis=0))
        #print '  lons[:10]',lons[:10]
        #print '  lats[:10]',lats[:10]
        #print '  coords[:10]',coords[:10]
        #print '  ids_stop_gtfs[:10]',ids_stop_gtfs[:10]
        id_mode_pt = scenario.net.modes.names.get_indexmap()['bus']
        id_mode_ped = scenario.net.modes.names.get_indexmap()['pedestrian']
        accesslevelsmap = self.parent.get_accesslevelsmap()
        #gtfsstop_to_simstop = {}
        for id_stop, coord, stopname in zip(ids_stop_gtfs,coords, stopnames):
            print('\n  id_stop',id_stop,'id_gpstrip',get_id_gpstrip(stop_shapes[id_stop]))#,'coord',coord
            #print '    id_gpsroute',gpstrips.ids_route_matched[get_id_gpstrip(stop_shapes[id_stop])]
            ids_edge_target = ids_edges_shape[gpstrips.ids_route_matched[get_id_gpstrip(stop_shapes[id_stop])]]
            #print '    ids_edge_target',ids_edge_target
            
            ids_edge_hit, dists = edges.get_closest_edge( coord,  
                                                    n_best = self.n_edgesearch, 
                                                    d_max = self.dist_edgesearch,
                                                    #is_ending = True, 
                                                    #is_detect_initial = True, 
                                                    #is_detect_final = True,
                                                    #accesslevels = accesslevelsmap[id_mode_pt]
                                                    )
            
            ind = 0
            is_hit = False
            n_hits = len(ids_edge_hit)
            print('    n_hits',n_hits,'len(ids_edge_target)',len(ids_edge_target))
            while (not is_hit) & (ind<n_hits):
                #for id_edge in  ids_edge_hit:
                id_edge =  ids_edge_hit[ind]
                
                print('      check id_edge', id_edge,id_edge in ids_edge_target)
                if  id_edge in ids_edge_target:
                    is_hit = self.place_stop(id_stop, stopname, id_edge, coord, edges, lanes, ptstops, id_mode_pt, id_mode_ped)
                    
            
                ind += 1
                
            if not is_hit:
                print('WARNING: no edge found for stop_id',id_stop,'id_gpstrip' ,get_id_gpstrip(stop_shapes[id_stop]))
                         
        
        ptstops.update_centroids()
        print('  result: len(ptstops)',len(ptstops))
        #mapmatching.ptstops = ptstops
        mapmatching.trip_stops = trip_stops
        #mapmatching.gtfsstop_to_simstop = gtfsstop_to_simstop
        mapmatching.trip_times = trip_times
        mapmatching.map_service_to_trips = map_service_to_trips
        mapmatching.map_trip_to_shape = map_trip_to_shape
        mapmatching.map_trip_to_route = map_trip_to_route
        mapmatching.ids_service_done += list(map_service_to_trips.keys())
        
        
                
        
        
        
        
        # debug
        if 0:
            print('  check map_service_to_trips')
            for id_service, ids_trip in mapmatching.map_service_to_trips.items():
                print('    check id_service',id_service,'ids_trip',ids_trip)
                
        return True

    def place_stop(self, id_stop, stopname, id_edge, coord, edges, lanes, ptstops, id_mode_pt, id_mode_ped):
        
        # stop already placed
        if ptstops.stopnames.has_index(id_stop):
            return True
             
        ids_lane = edges.ids_lanes[id_edge]
        edgelength = edges.lengths[id_edge]
        print('place_stop on id_edge',id_edge,'edgelength',edgelength,'len(ids_lane)',len(ids_lane))
        if (len(ids_lane)>=2):
            print('      check ped access',lanes.get_accesslevel([ids_lane[0]], id_mode_ped))
            
            
            
            if lanes.get_accesslevel([ids_lane[0]], id_mode_ped)>-1:
                print('      check bus access',lanes.get_accesslevel([ids_lane[1]], id_mode_pt))
                if lanes.get_accesslevel([ids_lane[1]], id_mode_pt)>-1:
                    
                    ids_simstop_exist = ptstops.select_ids(ptstops.ids_lane.get_value() == ids_lane[1])
                    if len(ids_simstop_exist)>0:
                        print('     there are already stops ids_simstop_exist',ids_simstop_exist)
                        d_init = np.min(ptstops.positions_from[ids_simstop_exist])
                        d_end = edgelength-np.max(ptstops.positions_to[ids_simstop_exist])
                        print('        d_init,d_end',d_init,d_end)
                        if d_init>d_end:
                            print('       place stop in front of previous stops')
                            pos_from = d_init - self.length_stop
                            pos_to = d_init
                            print('       try pos_from',pos_from)
                            if pos_from < self.dist_edge_stop_min:
                                pos_from = d_init - self.length_stop_min
                                print('       try with shorter stop pos_from',pos_from)

                            is_hit = pos_from > self.dist_edge_stop_min
                            
                        else:
                            print('      place stop in behind previous stops')
                            pos_from = edgelength-d_end
                            pos_to = pos_from+self.length_stop
                            print('       try pos_to',pos_to)
                            if pos_to > edgelength-self.dist_edge_stop_min:
                                pos_to = pos_from+self.length_stop_min
                                print('       try with shorter stop pos_to',pos_to)
                                
                            is_hit = pos_to < edgelength-self.dist_edge_stop_min
                            
                    
                    else:
                        
                        pos = edges.get_pos_from_coord(id_edge, coord)-0.5*self.dist_edge_stop_min
                        print('     place stop nearest to GPS coordinate pos',pos)
                        if pos < self.dist_edge_stop_min:
                            pos_from = self.dist_edge_stop_min
                            pos_to = self.dist_edge_stop_min + self.length_stop
                            print('       try pos_to',pos_to)
                            if pos_to > edgelength-self.dist_edge_stop_min:
                                pos_to = self.dist_edge_stop_min + self.length_stop_min
                                print('       try with shorter stop pos_to',pos_to)
                            is_hit = pos_to < edgelength-self.dist_edge_stop_min
                            
                        elif pos+ self.length_stop > edgelength-self.dist_edge_stop_min:
                            pos_from = edgelength-self.dist_edge_stop_min- self.length_stop
                            pos_to = edgelength-self.dist_edge_stop_min
                            print('       try pos_from',pos_from)
                            if pos_from < self.dist_edge_stop_min:
                                pos_from = edgelength-self.dist_edge_stop_min- self.length_stop_min
                                print('       try with shorter stop pos_from',pos_from)
                            
                            is_hit =   pos_from > self.dist_edge_stop_min
                             
                        else:
                            print('       stop fits')
                            pos_from = pos
                            pos_to = pos + self.length_stop
                            is_hit = True
                            
                        
                    if  is_hit:
                        print('    Add stop',id_stop,'at id_edge',id_edge,'pos_from %d'%pos_from,'pos_to %d'%pos_to,'edgelength %d'%edgelength)
                        id_simstop = ptstops.make( id_stop,
                                                    id_lane = ids_lane[1],
                                                    position_from = pos_from,
                                                    position_to = pos_to,
                                                    width =  self.width_stop,
                                                    ids_line = None,
                                                    stopname_human = stopname,
                                                    is_friendly_pos = False,
                                                    )
                        #gtfsstop_to_simstop[id_stop] = id_simstop
                    
                    return is_hit
                                
    
class GtfsServiceGenerator(Process):
    def __init__(self, ident='gtfsservicegenerator', mapmatching=None, results = None,  logger = None, **kwargs):
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
        print('GtfsServiceGenerator.__init__')
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'GTFS Service Generator', 
                            logger = logger,
                            info ='Imports information from GTFS data and generates public transport line services.',
                            )
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        
        
        scenario = mapmatching.get_scenario()
        rootfilepath = scenario.get_rootfilepath()
        
        if hasattr(mapmatching,'gtfsdirpath'):
            gtfsdirpath =  mapmatching.gtfsdirpath
        else:
            gtfsdirpath =  rootfilepath
                      
        self.gtfsdirpath = attrsman.add(cm.AttrConf(  'gtfsdirpath',kwargs.get('gtfsdirpath',gtfsdirpath),
                                        groupnames = ['options'], 
                                        perm='rw', 
                                        name = 'GTFS directory', 
                                        metatype = 'dirpath',
                                        info = 'GTFS directory.',
                                        )) 
        
        choices = OrderedDict()
        for y in np.arange(2015,2031): choices[str(y)]=y
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2018),
                            groupnames = ['options'], 
                            choices = choices,
                            perm='rw', 
                            name = 'Year', 
                            info = 'Selected year of public transport service.',
                            ))
                            
        choices = OrderedDict()
        for y in np.arange(1,13): choices[str(y)]=y
        self.month = attrsman.add(cm.AttrConf( 'month',kwargs.get('month',7),
                            groupnames = ['options'], 
                            choices = choices,
                            perm='rw', 
                            name = 'Month', 
                            info = 'Selected month of public transport service.',
                            ))
        choices = OrderedDict()
        for y in np.arange(1,32): choices[str(y)]=y
        self.day = attrsman.add(cm.AttrConf( 'day',kwargs.get('day',1),
                            groupnames = ['options'], 
                            choices = choices,
                            perm='rw', 
                            name = 'Day', 
                            info = 'Selected day of public transport service.',
                            ))
        
                                                                                                                    
        self.hour_begin = attrsman.add(cm.AttrConf( 'hour_begin',kwargs.get('hour_begin',6),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'From hour', 
                            unit = 'h',
                            info = 'Create only services after this hour.',
                            ))
        
        self.hour_end = attrsman.add(cm.AttrConf( 'hour_end',kwargs.get('hour_end',9),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'To hour', 
                            unit = 'h',
                            info = 'Create only services before this hour, excluding this hour.',
                            ))
                            
        
        self.time_dwell = attrsman.add(cm.AttrConf( 'time_dwell', kwargs.get('time_dwell',20),
                                    groupnames = ['options'], 
                                    perm='rw', 
                                    name = 'Dwell time',
                                    untit = 's',
                                    info = 'Default Dwell time in a stop while passengers are boarding/alighting.' 
                                    ))
            
        self.is_frequ_est = attrsman.add(cm.AttrConf( 'is_frequ_est', kwargs.get('is_frequ_est',True),
                                    groupnames = ['options'], 
                                    perm='rw', 
                                    name = 'Freqency. est',
                                    info = 'Estimate and approximate frequency of services if not provided.' 
                                    ))  
        
        self.hour_begin_frequ = attrsman.add(cm.AttrConf( 'hour_begin_frequ',kwargs.get('hour_begin_frequ',-1),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'From hour for const. frequ', 
                            unit = 'h',
                            info = 'Only used with frequency etsimation: Create only services after this hour. Value of -1 takes time from times from database.',
                            ))
        
        self.hour_end_frequ = attrsman.add(cm.AttrConf( 'hour_end_frequ',kwargs.get('hour_end_frequ',-1),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'To hour for const. frequ', 
                            unit = 'h',
                            info = 'Only used with frequency etsimation: Create only services before this hour, excluding this hour. Value of -1 takes time from times from database.',
                            ))
                            
        self.is_clear_ptlines = attrsman.add(cm.AttrConf( 'is_clear_ptlines', kwargs.get('is_clear_ptlines',True),
                                    groupnames = ['options'], 
                                    perm='rw', 
                                    name = 'Clear PT lines',
                                    info = 'Clear previously existing public transport line services.' 
                                    ))   
    
    def do(self):
        sep = ','
        aa = '\"'
        print('GtfsServiceGenerator.do is_frequ_est',self.is_frequ_est)
        
    
        # for later processes
        mapmatching = self.parent
        mapmatching.gtfsdirpath = self.gtfsdirpath
        
        
        if self.is_clear_ptlines:
            mapmatching.get_scenario().demand.ptlines.clear()
        
        # for debugging
        #map_service_to_route = mapmatching.map_service_to_route
        map_trip_to_route = mapmatching.map_trip_to_route
        
        ## check dates when services are available
        filepath = os.path.join(self.gtfsdirpath, 'calendar_dates.txt')
        f=open(filepath,'r', encoding="utf-8")
        # service_id    date    exception_type

        ind_service_id = 0
        ind_date = 1
        ind_exception_type = 2

        #service_to_date = {}
        ids_service_valid = []
        i = 0
        f.readline()
        for line in f.readlines():
            cols = line.split(sep)
            date = cols[ind_date].strip().strip(aa)
            year,month,day = (date[0:4],date[4:6],date[6:8])
            year = re.sub('\W+','', year)
            month = re.sub('\W+','', month)
            day = re.sub('\W+','', day)
            
            id_service = cols[ind_service_id].strip().strip(aa)
            id_service = re.sub('^[a-zA-Z_-]*$','', id_service)
            #print '  id_service',id_service,'year,month,day',year,month,day,(int(year) == self.year) ,(int(month) == self.month),(int(day) == self.day)

            
            if (int(year) == self.year) & (int(month) == self.month) &(int(day) == self.day):
                print('valid id_service',id_service,'year,month,day',year,month,day,(int(year) == self.year) ,(int(month) == self.month),(int(day) == self.day))
                ids_service_valid.append(id_service)
            else:
                #print 'not valid'
                pass
                
        f.close()
        
        print('  result: len(ids_service_valid)',len(ids_service_valid))
        print('    ids_service_valid',ids_service_valid)
        if len(ids_service_valid)>0:
            if self.is_frequ_est:
                return self.schedule_frequ_est(ids_service_valid)
            else:
                return self.schedule_simple(ids_service_valid)
        else:
            print('WARNING: no services found on specified date.')
            return True
        
    def schedule_frequ_est(self, ids_service_valid):
        """
        Scheduling with frequency estimation.
        """
        mapmatching = self.parent
        
        logger = self.get_logger()
        scenario = mapmatching.get_scenario()
        net = scenario.net
        vtypes = scenario.demand.vtypes
        #edges = net.edges
        #lanes = net.lanes
        #ptstops = net.ptstops
        ptlines = scenario.demand.ptlines
        
        distancesmap = mapmatching.get_distancesmap()
        #accesslevelsmap = mapmatching.get_accesslevelsmap()
        fstarmap = mapmatching.get_fstarmap(is_return_arrays = True, is_ignor_connections = False)
        
     
        gpstrips = mapmatching.trips
        trip_stops = mapmatching.trip_stops
        trip_times = mapmatching.trip_times
        #gtfsstop_to_simstop = mapmatching.gtfsstop_to_simstop
        map_trip_to_shape = mapmatching.map_trip_to_shape
        map_trip_to_route = mapmatching.map_trip_to_route
        
        get_id_gpstrip = gpstrips.ids_sumo.get_id_from_index
        stopnames = net.ptstops.stopnames
        ids_edges_shape = gpstrips.routes.get_value().ids_edges
        
        if not hasattr(mapmatching,'trip_stops'):
            print('WARNING: no stops found, please run PT stop generation first')
            return False
        ##
        ## Generate timetables for ids_trip
        ids_trip = list(mapmatching.trip_stops.keys())
        
        time_inter_begin = self.hour_begin*3600.0
        time_inter_end = self.hour_end*3600.0
        time_inter = time_inter_end-time_inter_begin
        id_service_counter = 0
        # maps stop sequences to a unique service ID
        # which is different from the GTFS service ID
        stopsequence_to_services = {}
        for id_service, ids_trip in mapmatching.map_service_to_trips.items():
            print('  freq. check id_service',id_service,'ids_trip',ids_trip,'valid',id_service in ids_service_valid)
            if id_service in ids_service_valid:
                for id_trip in ids_trip:
                    
                    hs,ms,ss = trip_times[id_trip][0].split(':')
                    print('  examin valid id_service',id_service,'id_trip',id_trip,'t',hs,ms,ss)
                    hs = int(hs)
                    if (hs >= self.hour_begin)&(hs<self.hour_end):
                        time_start = hs*3600+int(ms)*60+int(ss)
                        
                        # attention, not all stops may have been successfully created
                        # ids_simstop is the sequence of valid (matched) stops
                        # and is used as a key to identify trips with identical
                        # stop sequences 
                        ids_simstop = []
                        for id_stop in trip_stops[id_trip]:
                            if stopnames.has_index(id_stop):
                                ids_simstop.append(stopnames.get_id_from_index(id_stop))
                        
                        # try to group services with identical stop sequences 
                        # by assigning service frequencies...save unique ID and init times
                    
                        #Attention: there are different service ids 
                        #with identical stop sequences!!!!
                        ids_simstop = tuple(ids_simstop)
                        
                            
                        if ids_simstop not in stopsequence_to_services:
                            stopsequence_to_services[ids_simstop] = []
                        
                        # check if service with same start time exists already 
                        is_found = False
                        for time_start_check, _id_service, _id_trip in stopsequence_to_services[ids_simstop]:
                            is_found = time_start==time_start_check
                            if is_found:
                                print('    WARNING: already existing id_trip',id_trip,'with time_start',time_start)
                                break
                            
                        if not  is_found:
                            print('    add id_trip',id_trip,'id_route',map_trip_to_route[id_trip],'time_start %s:%s'%(hs,ms))#,'to ids_simstop',ids_simstop
                            stopsequence_to_services[ids_simstop].append((time_start, id_service, id_trip))  
                    

    
        id_estservice = 0
        ids_route_count = {}
        for ids_simstop, services in stopsequence_to_services.items():
            
            if len(ids_simstop) >= 2: # at least 2 stops
                # sort trips with identical stop sequences by start time
                # this is only to extract first run time
                # actually no longer needed
                services.sort()
                time_first = services[0][0]
                time_end = services[-1][0]
                
                
                
                # pick example trip to get route
                id_trip = services[0][2]
                #id_service = services[0][1]
                
                if id_trip in map_trip_to_shape:
                
                    # here it is assumed that all services with the same
                    # sequence of stops produce the same route ID 
                    # which corrisponds to the line name 
                    id_route = map_trip_to_route[id_trip]
                    
                    
                    n_services = len(services)
                    if time_end-time_first<1:
                        # configure single time service
                        freq = time_inter+1
                        time_end = time_first+1
                    else:
                        # calculate average service time
                        #freq = int(float(time_end-time_first)/n_services)
                        freq = int(time_inter/n_services)
                    
                    # check for custom defined service times
                    if self.hour_begin_frequ >= 0:
                        time_first = 3600.0*self.hour_begin_frequ
                    if self.hour_end_frequ >= 0:
                        time_end = 3600.0*self.hour_end_frequ
                        
                    if id_route not in ids_route_count:
                        ids_route_count[id_route] = 0
                    else:
                        ids_route_count[id_route] += 1
                    
                    linename = '%s_%s'%(id_route,ids_route_count[id_route])
              
                    #linename = '%s_%s'%(map_service_to_route[id_estservice],id_estservice,)
                    id_gpstrip = get_id_gpstrip(map_trip_to_shape[id_trip])
                
                    ids_edge = ids_edges_shape[gpstrips.ids_route_matched[id_gpstrip]]
                    
                    # corrrect pt route matching problems
                    id_vtype= gpstrips.ids_vtype[id_gpstrip]
                    id_mode = vtypes.ids_mode[id_vtype]
                    
                    ids_edge = self.correct_route(list(ids_simstop), ids_edge, net, fstarmap[id_mode], distancesmap[id_mode])
                    
                    id_line = ptlines.make(linename = linename, 
                                            time_begin = time_first,
                                            time_end = time_end,# 
                                            period = freq,
                                            time_dwell= self.time_dwell,
                                            ids_stop= list(ids_simstop),
                                            ids_edge= ids_edge,
                                            id_vtype= id_vtype,
                                            )
                    print('\n',70*'-')
                    print('  scheduled',linename,'time_start %d'%time_start,'time_end %d'%time_end,'frequ %d'%freq,'id_stop',trip_stops[id_trip][0])
                    print('      id_gpstrip',id_gpstrip,'id_gpsroute',gpstrips.ids_route_matched[id_gpstrip])
                    #print '        ids_simstop',ids_simstop
                    #print '        ids_edge',ids_edge
                    t = time_first                        
                    for time_start, id_service, id_trip in services:
                        print('           check id_trip',id_trip,'time_start %d'%time_start,'t %.d'%t,'delay',time_start-t)
                        t = t + freq
                    id_estservice += 1     
            
                    
                
            
        return True
                            
    def correct_route(self, ids_stop, ids_edge, net, fstar, distances):
        """
        Can happen that edge of first and last stop is not part of the route
        due to unprecise mapmatching. Here this problem is fixed.
        """
        #print 'correct_route ids_stop'#,ids_stop
        ids_edge_stop = net.lanes.ids_edge[net.ptstops.ids_lane[ids_stop]]
        #print '  redges',ids_edge
        #print '  sedges',ids_edge_stop
        #for id_stop in ids_stop:
        if ids_edge_stop[0] not in  ids_edge:
            #print '  try to fix beginning of bus route'
            dist, ids_edges_fix  = routing.get_mincostroute_edge2edge(\
                                                ids_edge_stop[0], 
                                                ids_edge[0], 
                                                weights = distances, 
                                              fstar = fstar)
            if dist > 0:
                return ids_edges_fix[:-1] + ids_edge
            else:
                #print '  failed to fix beginning of bus route'
                return  ids_edge
            
        if ids_edge_stop[-1] not in  ids_edge:
            #print '  try to fix end of bus route'
            dist, ids_edges_fix  = routing.get_mincostroute_edge2edge(\
                                                ids_edge[-1],
                                                ids_edge_stop[-1], 
                                                weights = distances, 
                                                fstar = fstar)
            if dist > 0:
                return  ids_edge + ids_edges_fix[1:]
            else:
                #print '  failed to fix end of bus route'
                return  ids_edge
                 
        return ids_edge
                                        
    def schedule_simple(self, ids_service_valid):
        """
        Plain scheduling of services.
        """
        mapmatching = self.parent
        logger = self.get_logger()
        scenario = mapmatching.get_scenario()
        net = scenario.net
        vtypes = scenario.demand.vtypes
        
        distancesmap = mapmatching.get_distancesmap()
        #accesslevelsmap = mapmatching.get_accesslevelsmap()
        fstarmap = mapmatching.get_fstarmap(is_return_arrays = True, is_ignor_connections = False)
        
        
        #edges = net.edges
        #lanes = net.lanes
        #ptstops = net.ptstops
        ptlines = scenario.demand.ptlines
        
        gpstrips = mapmatching.trips
        trip_stops = mapmatching.trip_stops
        trip_times = mapmatching.trip_times
        #gtfsstop_to_simstop = mapmatching.gtfsstop_to_simstop
        map_trip_to_shape = mapmatching.map_trip_to_shape
        map_trip_to_route = mapmatching.map_trip_to_route
        #map_service_to_route = mapmatching.map_service_to_route
        get_id_gpstrip = gpstrips.ids_sumo.get_id_from_index
        stopnames = net.ptstops.stopnames
        
        # matched routes
        ids_edges_shape = gpstrips.routes.get_value().ids_edges
        
        if not hasattr(mapmatching,'trip_stops'):
            print('WARNING: no stops found, please run PT stop generation first')
            return False
        ##
        ## Generate timetables for ids_trip
        ids_trip = list(mapmatching.trip_stops.keys())
        
        id_service_counter = 0
        # maps stop sequences to a unique service ID
        # which is different from the GTFS service ID
        #stopsequence_to_services = {}
        
        # this list records tuples of (id_route, time_start) in order to
        # used to avoid that multiple buses do the same route at the same time!
        routes_used = []
        for id_service, ids_trip in mapmatching.map_service_to_trips.items():
            print('-'*70)
            print('id_service',id_service)
            
            schedules=[]# contains all trip data of a service
            if id_service in ids_service_valid:
                for id_trip in ids_trip:
                    if id_trip in map_trip_to_shape:
                        id_route = map_trip_to_route[id_trip]
                        print('  linename=%s_%s'%(id_route, id_trip),',time',trip_times[id_trip][0],'\t id_stop',trip_stops[id_trip][0],id_trip)
                        #print '    len(trip_times[id_trip])',len(trip_times[id_trip]),'len(trip_stops[id_trip])',len(trip_stops[id_trip])
                        
                        if (len(trip_times[id_trip])>1):
                            hs,ms,ss = trip_times[id_trip][0].split(':')
                            hs = int(hs)
                            print('    hs,ms,ss',hs,ms,ss,'time_start',int(hs)*3600+int(ms)*60+int(ss))
                            if (hs >= self.hour_begin)&(hs<self.hour_end):
                                time_start = int(hs)*3600+int(ms)*60+int(ss)
                                if (id_route,time_start) not in routes_used:
                                    times_stops = []
                                    ids_simstop = []
                                    for id_stop, timestr in zip(trip_stops[id_trip], trip_times[id_trip]):
                                        # attention, not all stops may have been successfully created
                                        if stopnames.has_index(id_stop):
                                            ids_simstop.append(stopnames.get_id_from_index(id_stop))
                                            hs,ms,ss = timestr.split(':')
                                            times_stops.append(int(hs)*3600+int(ms)*60+int(ss))
                                    routes_used.append((id_route,time_start))
                                    schedules.append((time_start,id_route,id_trip,ids_simstop,times_stops))
                        
                ############
                schedules.sort()
                
                
                
                
                for time_start,id_route, id_trip,ids_simstop,times_stop  in schedules:
                    if len(ids_simstop) >= 2: # at least 2 stops
                        #linename = '%s_%s'%(map_service_to_route[id_service], id_trip)
                        linename = '%s_%s'%(id_route,id_trip)
                        id_gpstrip = get_id_gpstrip(map_trip_to_shape[id_trip])
                    
                        ids_edge = ids_edges_shape[gpstrips.ids_route_matched[id_gpstrip]]
                        
                        # corrrect pt route matching problems
                        id_vtype = gpstrips.ids_vtype[id_gpstrip]
                        id_mode = vtypes.ids_mode[id_vtype]
                        
                        ids_edge = self.correct_route(list(ids_simstop), ids_edge, net, fstarmap[id_mode], distancesmap[id_mode])
                        
                            
                        id_line = ptlines.make(linename = linename, 
                                            time_begin = times_stop[0]-self.time_dwell,
                                            time_end = times_stop[0],# one off service
                                            period = 10**8,# infinite
                                            time_dwell= self.time_dwell,
                                            ids_stop= ids_simstop,
                                            ids_edge= ids_edge,
                                            times_stop = times_stop,
                                            id_vtype= gpstrips.ids_vtype[id_gpstrip],
                                            )
                                
                        print('  scheduled',linename,"start%d"%time_start,'id_stop',trip_stops[id_trip][0],'id_gpstrip',id_gpstrip,'id_gpsroute',gpstrips.ids_route_matched[id_gpstrip])
                        #print '        ids_simstop',ids_simstop
                        #print '        ids_edge',ids_edge
            
        
            
        return True
                            
class GpsTrips(Trips):
    def __init__(self, ident, mapmatching,  **kwargs):
        #print 'Trips.__init__'
        self._init_objman(  ident = ident, 
                            parent = mapmatching, 
                            name = 'Trips', 
                            info = 'Table with GPS trips, matched routes and alternative routes.',
                            xmltag = ('trips','trip','ids_sumo'),
                            version = 0.0,
                            **kwargs)
        
        
        self._init_attributes()
        self._init_constants()
        
    def _init_attributes(self):
        scenario = self.parent.get_scenario()
        self.add_col(SumoIdsConf('GPS Trip', xmltag = 'id', info = 'GPS trip data.'))
        
        self.add_col(am.ArrayConf('are_selected', default = True,
                                    dtype = np.bool_,
                                    groupnames = ['parameters',], 
                                    name = 'selected',
                                    symbol = 'Sel.',
                                    info = 'Selected for being processed (example mapmatching, export, etc).',
                                    ))   
        
           
                                    
        
                                    
        self.add_col(am.ArrayConf('timestamps', default = 0,
                                    dtype = np.int32,
                                    perm = 'r',
                                    groupnames = ['parameters','gps'], 
                                    name = 'timestamp',
                                    unit = 's',
                                    metatype = 'datetime', 
                                    info = 'Timestamp when trip started in seconds after 01 January 1970.',
                                    )) 
                                            
        self.timestamps.metatype = 'datetime'
        self.timestamps.set_perm('r')
        
        
        self.add_col(am.ArrayConf('durations_gps', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'GPS duration',
                                    unit = 's',
                                    info = 'Time duration measure with GPS points.',
                                    )) 
                                                                
        self.add_col(am.ArrayConf('distances_gps', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'GPS distance',
                                    unit = 'm',
                                    info = 'Distance measure with GPS points.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('positive_elevations', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'Positive Elevation',
                                    symbol = 'Pos. Elev.',
                                    unit = 'm',
                                    info = 'Total positive elevation of trip.',
                                    )) 
        self.add_col(am.ArrayConf('negative_elevations', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'Negative Elevation',
                                    symbol = 'Neg. Elev.',
                                    unit = 'm',
                                    info = 'Total negative elevation of trip.',
                                    )) 

        self.add_col(am.ArrayConf('speeds_average', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'Av. speed',
                                    unit = 'm/s',
                                    info = 'Average speed based on GPS info.',
                                    ))
        self.add_col(am.ArrayConf('speeds_max', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['parameters','gps'], 
                                    name = 'Max. speed',
                                    unit = 'm/s',
                                    info = 'Maximum speed based on GPS info.',
                                    ))
                                    
        
        
         
        
        #Trips._init_attributes(self)
        self.add_col(am.IdsArrayConf( 'ids_vtype', self.get_obj_vtypes(), 
                                        groupnames = ['state'], 
                                        name = 'Type', 
                                        info = 'Vehicle type.',
                                        xmltag = 'type',
                                        ))
        
        
        
        
        self.add_col(am.ArrayConf('ids_purpose', default = TRIPPUROPSES['unknown'],
                                    dtype = np.int32,
                                    groupnames = ['parameters','gps'], 
                                    choices = TRIPPUROPSES,
                                    name = 'Purpose',
                                    info = 'Trip purpose ID',
                                    )) 
                                    
        

                                    
        self.add_col(am.ArrayConf('ids_device', default = DEVICES['unknown'],
                                    dtype = np.int32,
                                    groupnames = ['parameters','gps'], 
                                    choices = DEVICES,
                                    name = 'Devices',
                                    info = 'Device ID',
                                    )) 
        
        
        #???????????                                
        self.add( cm.ObjConf( Routes('routes', self, self.parent.get_scenario().net)))
        
        # make routes                                
        routes = self.add( cm.ObjConf( Routes('routes', self, scenario.net)))
        
        # make additional route attributes for matching public transport routes
        routes.add_col(am.IdlistsArrayConf('ids_ptlinks', scenario.demand.ptlines.get_ptlinks(), 
                                    groupnames = ['parameters'], 
                                    name = 'IDs PT link', 
                                    info = 'List of public transport link IDs.',
                                    ))
                                    
        self.add_col(am.IdsArrayConf('ids_route_matched', self.get_routes(), 
                                    groupnames = ['results'], 
                                    name = 'ID matched route', 
                                    info = 'Route ID of mached route.',
                                    ))
                                                                                                
        self.add_col(am.IdsArrayConf('ids_route_shortest', self.get_routes(), 
                                    groupnames = ['results'], 
                                    name = 'ID shortest route', 
                                    info = 'Route ID of shortest route.',
                                    ))
                                                                                            
        self.add_col(am.IdsArrayConf('ids_route_fastest', self.get_routes(), 
                                    groupnames = ['results'], 
                                    name = 'ID fastest route', 
                                    info = 'Route ID of fastest route.',
                                    ))
                                    
        
        self.add_col(am.ArrayConf('lengths_gpsroute_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched GPS length',
                                    symbol = 'L match GPS',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace, measured by linear interpolation of GPS points. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                    
        self.add_col(am.ArrayConf('lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched length',
                                    symbol = 'L match',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace, measured by summing the length of edges of the matched route. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                    
        
        
        self.add_col(am.ArrayConf('durations_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched duration',
                                    symbol = 'T match',
                                    unit = 's',
                                    info = 'Duration of the matched part of the GPS trace. This is the difference in timestamps between last and first GPS point of the matched route. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                                                
        self.add_col(am.ArrayConf('lengths_route_shortest', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Shortest length',
                                    symbol = 'L short',
                                    unit = 'm',
                                    info = 'Length of the shortest route.  Shortest route is connecting the first matched edge and the final matched edge.',
                                    ))
                                    
        
        self.add_col(am.ArrayConf('durations_route_fastest', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Fastest duration',
                                    symbol = 'T fast',
                                    unit = 's',
                                    info = 'Durations of the fastest route.  Fastest route is connecting the first matched edge and the final matched edge.',
                                    ))
        
        self.add_col(am.ArrayConf('timelosses_route_fastest', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Fastest timeloss',
                                    symbol = 'dT fast',
                                    unit = 's',
                                    info = """Time loss of matched route with respect to fastest route.  Fastest route is connecting the first matched edge and the final matched edge.
                                    Note that in order to be comparable, this time loss is calculated for matched and fastest route with the same edge travel times.
                                    These edge travel times are an approximation and are not necessarily identical with the experienced edge travel times.
                                    """,
                                    ))
                                    
        self.add_col(am.ArrayConf('lengths_route_fastest', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Fastest length',
                                    symbol = 'L fast',
                                    unit = 'm',
                                    info = 'Length of the fastest route.  Fastest route is connecting the first matched edge and the final matched edge.',
                                    ))
                                                                
        self.add_col(am.ArrayConf('lengths_route_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched length mixed access',
                                    symbol = 'L match mix',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_route_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched length exclusive access',
                                    symbol = 'L match excl',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                                                                            
        self.add_col(am.ArrayConf('lengths_route_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched length in contrary direction',
                                    symbol = 'L match contr',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace in the contrary direction. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                    
        self.add_col(am.ArrayConf('lengthindexes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length index',
                                    unit = '%',
                                    info = 'Length index is the length of the matched route divided by length of line-interpolated GPS points.',
                                    )) 
        
        self.add_col(am.ArrayConf('errors_dist', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Distance error',
                                    unit = 'mm',
                                    info = 'The distance error is the average distance between the GPS points and the matched route.',
                                    )) 
                                                                
        self.add_col(am.ArrayConf('times_computation', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Computation time',
                                    unit = 'ms',
                                    info = 'Computation time of the match algorithm.',
                                    ))
        
        self.add_col(am.ArrayConf('are_match_connected', default = False,
                                    dtype = np.bool_,
                                    groupnames = ['results',], 
                                    name = 'Match connected',
                                    #symbol = 'Match conn.',
                                    info = 'The matched route connects first and last network edge where GPS points have been detected.',
                                    ))
                                          
        self.add_col(am.IdlistsArrayConf( 'ids_points', self.parent.points,
                                        groupnames = ['_private'], 
                                        name = 'Point IDs', 
                                        info = "GPS point IDs.",   
                                        )) 
         
        self.get_config('ids_points').add_groupnames(['_private',])
                                        
        self.add_col(am.IdlistsArrayConf( 'ids_points_edgeend', self.parent.points,
                                        groupnames = ['results','_private'], 
                                        name = 'Edge endpoint IDs', 
                                        info = "This is a list of GPS point IDs which represent the last point associated with each matched edge.",   
                                        )) 
        
        self.add_col(am.IdlistsArrayConf( 'edges_list_dyn', self.parent.points,
                                        groupnames = ['results','_private'], 
                                        name = 'Traveled edges with the dyna analysis', 
                                        info = "Traveled edges with the dyna analysis.",   
                                        )) 
                                        
        self.add_col(am.IdlistsArrayConf( 'connections_list_dyn', self.parent.points,
                                        groupnames = ['results','_private'], 
                                        name = 'Traveled connections with the dyna analysis', 
                                        info = "Traveled connections with the dyna analysis.",   
                                        )) 
                                        
        self.add_col(am.IdlistsArrayConf( 'nodes_list_dyn', self.parent.points,
                                        groupnames = ['results','_private'], 
                                        name = 'Traveled nodes with the dyna analysis', 
                                        info = "Traveled nodes with the dyna analysis",   
                                        )) 
                                        
        # update
        self.ids_points_edgeend.add_groupnames(['_private'])
        
    
    def set_persons(self):
        self.add_col(am.IdsArrayConf( 'ids_person', self.parent.persons, 
                                        groupnames = ['parameters'], 
                                        name = 'ID person', 
                                        info = 'Person ID.',
                                        ))
                                           
    def get_ids_point_matched(self, id_trip):
        """Returns a list of point ids that have been used for the matched rout"""
        
        
        
        idpee = self.ids_points_edgeend[id_trip]
        if len(idpee)<2:
            return []
        
        ids_point_all = list(self.ids_points[id_trip])#.tolist()
        #print 'get_ids_point_matched'
        #print '  ids_point_all',ids_point_all
        #print '  idpee',idpee
        #print '  ',ids_point_all.index(idpee[0]),':',ids_point_all.index(idpee[-1])+1,'len',len(ids_point_all)
        return ids_point_all[ids_point_all.index(idpee[0]): ids_point_all.index(idpee[-1])+1]
        
           
                             
    def get_obj_vtypes(self):
        return self.parent.get_scenario().demand.vtypes

    def get_routes(self):
        return self.routes.get_value()
    
    def clear_routes(self):
        self.get_routes().clear()
        for attrconf in self.get_group('results'):
            attrconf.reset()
    
    def select_all(self):
        self.are_selected.get_value()[:] = True 
        
    def unselect_all(self):
        self.are_selected.get_value()[:] = False 
    
    def invert_selection(self):
        self.are_selected.get_value()[:] = np.logical_not(self.are_selected.get_value()[:]) 
        
    
                 
    def set_matched_route(self, id_trip, route,
                                    length_matched = 0.0,
                                    length_route_mixed = 0.0,
                                    length_route_exclusive = 0.0,
                                    length_route_contrary = 0.0,
                                    duration_matched = 0.0,
                                    lengthindex = -1.0,
                                    error_dist = -1.0,
                                    comptime = 0.0,
                                    is_connected = False,
                                    ids_point_edgeend = [],
                                    color = COLOR_MATCHED_ROUTE.copy(),
                                    ):
                                        
                                        
        
        if len(route)>0:   
            id_route = self.ids_route_matched[id_trip]
            if id_route>=0:
                # already a matched route existant
                self.get_routes().ids_edges[id_route] = route
                self.get_routes().colors[id_route] = color
                #self.get_routes().set_row(  id_route,
                #                            ids_edges = route,
                #                            colors = COLOR_MATCHED_ROUTE,
                #                            )
                
                #self.get_routes().set_row(  id_route,
                #                            ids_trip = id_trip,
                #                            ids_edges = route,
                #                            #costs = duration_matched,
                #                            #probabilities = 1.0,
                #                        )  
            else:                  
                id_route = self.get_routes().add_row(   ids_trip = id_trip,
                                                    ids_edges = route,
                                                    #costs = duration_matched,
                                                    #probabilities = 1.0,
                                                    colors = color,
                                                    )
        else:
            id_route = -1
            
        #print 'set_matched_route id_trip', id_trip,'id_route', id_route
        #print '  ids_point_edgeend',ids_point_edgeend
        #self.ids_route_matched[id_trip] = id_route
        
        self.set_row(   id_trip,
                        ids_route_matched = id_route,
                        lengths_gpsroute_matched = length_matched/lengthindex,
                        durations_route_matched = duration_matched,
                        lengths_route_matched = length_matched,
                        lengths_route_matched_mixed = length_route_mixed,
                        lengths_route_matched_exclusive = length_route_exclusive,
                        lengths_route_matched_contrary = length_route_contrary,
                        lengthindexes = 100*lengthindex,
                        errors_dist = 1000* error_dist,
                        times_computation = 1000*comptime,
                        are_match_connected = is_connected
                        )
        # TODO:  do this extra! this is a bug!! 
        # if included in set_row, only first value in list is taken!!!
        self.ids_points_edgeend[id_trip] = ids_point_edgeend 
        
        #print '  ids_points_edgeend[id_trip]',self.ids_points_edgeend[id_trip]
        
        
        return id_route
        
    def set_matched_ptroute(self, id_trip, ptroute, route, **kwargs):
        id_route = self.set_matched_route(id_trip, route, **kwargs)
        self.get_routes().ids_ptlinks[id_route] = ptroute
        return id_route
    
    def make(self,  **kwargs):
        #if self.ids_sumo.has_index(id_sumo):
        #    id_trip = self.ids_sumo.get_id_from_index(id_sumo)
        #    #self.set_row(id_sumo, **kwargs)
        #    return id_trip
        #else:
            
        
        #purpose = cols[j_speed_av].strip(),
        #device = cols[j_device].strip(),
        device = kwargs.get('device',None)
        if device in DEVICES:
            id_device = DEVICES[device]
        else:
            id_device = DEVICES['unknown']
        
        purpose = kwargs.get('purpose',None)
        if purpose in TRIPPUROPSES:
            id_purpose = TRIPPUROPSES[purpose]
        else:
            id_purpose = TRIPPUROPSES['unknown']
        
        id_trip =  self.add_row(    ids_sumo = kwargs.get('id_sumo',None),
                                    ids_vtype = kwargs.get('id_vtype',None),
                                    timestamps = kwargs.get('timestamp',None),
                                    distances_gps = kwargs.get('distance_gps',None),
                                    durations_gps = kwargs.get('duration_gps',None),
                                    speeds_average= kwargs.get('speed_average',None),
                                    speeds_max= kwargs.get('speed_max',None),
                                    ids_purpose= id_purpose,
                                    ids_device= id_device,
                                    ids_points= kwargs.get('ids_point',None),
                                    )
        return id_trip
    
    
    def set_points(self, id_trip, ids_point):
        self.ids_points[id_trip] = ids_point
        
    def get_ids_selected(self):
        return self.select_ids(self.are_selected.get_value())

    
    def route_fastest_with_waits(self, time_modespecific = 3.0, c_modespecific = 0.9, 
                        is_ignor_connections = False, times_wait_nodes = None,
                        speeds_in_motion = None, dist_min_modespecific= 15.0,
                        color_route = COLOR_FASTEST_ROUTE.copy()):
        """
        Shortest fastest routing.
        """
        print('route_fastest_with_waits',time_modespecific,c_modespecific)
        # TODO: if too mant vtypes, better go through id_modes
        exectime_start = time.perf_counter()
        scenario = self.parent.get_scenario()
        net = scenario.net
        edges = net.edges
        vtypes = scenario.demand.vtypes 
        routes = self.get_routes()
        #ids_edges = []
        #ids_trip = []
        #costs = []
        
        distancesmap = self.parent.get_distancesmap()
        accesslevelsmap = self.parent.get_accesslevelsmap()
        
        # delete current 
        #ids_with_shortes = self.select_ids(np.logical_and(self.are_selected.get_value(), self.ids_route_shortest.get_value()>=0))        
        #routes.del_rows(self.ids_route_shortest[ids_with_shortes])
        #self.ids_route_shortest[ids_with_shortes] = -1
                
        
        
        fstar = edges.get_fstar(is_ignor_connections = is_ignor_connections)
        for id_vtype in self.get_vtypes():
            id_mode = vtypes.ids_mode[id_vtype]
            
            # no routing for pedestrians
            if id_mode != net.modes.get_id_mode('pedestrian'):
                dists_orig = distancesmap[id_mode].copy() 
                times = np.zeros(len(dists_orig), dtype = np.float32)
                weights = np.zeros(len(dists_orig), dtype = np.float32)
                
                # this will subtract some meters dependent on 
                # access-level of the edge
                accesslevels =  accesslevelsmap[id_mode]
                ids_edge = edges.get_ids()
                are_valid = dists_orig > dist_min_modespecific
                
                
                
                
                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value() == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value()>=0))
                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
                #print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
                #print '  weights',weights
                
                edgewaittimes = times_wait_nodes[edges.ids_tonode[ids_edge]]
                specifictimes = time_modespecific * are_valid[ids_edge] * accesslevels[ids_edge]
                specificfactors = 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge]==2)
                
                
                
                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
                
                for id_trip,id_route, speed_inmotion in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype],speeds_in_motion[ids_trip_vtype]):
                    route_matched = routes.ids_edges[id_route]
                    #print '  id_trip,speed_inmotion',id_trip,speed_inmotion 
                    times[ids_edge] =  dists_orig[ids_edge]/speed_inmotion + edgewaittimes 
                    weights[ids_edge] = (times[ids_edge]  -specifictimes)*specificfactors
                    #weights[ids_edge] -= 
                    #weights[ids_edge] *= 
                
                
                    # compute time wit speed in motion and add waiting time of tonode
                    cost, route = routing.get_mincostroute_edge2edge(   route_matched[0],
                                                                        route_matched[-1], 
                                                                        weights= weights,
                                                                        fstar = fstar)
                    if len(route)>0:
                        #ids_edges.append(route)
                        #ids_trip.append(id_trip)
                        #costs.append(cost)
                        
                        id_route =  self.ids_route_fastest[id_trip]
                        if id_route>=0:
                            # there is already a previous shortest route
                            routes.set_row(id_route,
                                           #ids_edges = list(route),
                                           costs = cost,
                                           colors = color_route,
                                            )
                            # TODO!!! this assigment does not work in set_row!!!!
                            # tales only first edge !! why??
                            routes.ids_edges[id_route] = route
                            #print '  old route',id_route,type(route)
                        else:
                            # create new route
                            id_route =  routes.add_row( ids_trip = id_trip,
                                                        ids_edges = route,
                                                        costs = cost,
                                                        colors = color_route,
                                                        )
                            self.ids_route_fastest[id_trip] = id_route
                            #print '  new route',type(route)
                            
                        
                        self.lengths_route_fastest[id_trip] = np.sum(dists_orig[route])
                        self.durations_route_fastest[id_trip] = np.sum(times[route])
                        self.timelosses_route_fastest[id_trip] = np.sum(times[route_matched])-self.durations_route_fastest[id_trip]
                        #print '   route', route   
                        #print '   times',times[route]
                        #print '  routes.ids_edges' ,routes.ids_edges[id_route]
        
        
        print('  exectime',time.perf_counter()-exectime_start) 
        
    def route_fastest(self, time_modespecific = 3.0, c_modespecific = 0.9, 
                        is_ignor_connections = False,
                        color_route = COLOR_FASTEST_ROUTE.copy()):
        """
        Shortest fastest routing.
        """
        print('route_fastest',time_modespecific,c_modespecific)
        # TODO: if too mant vtypes, better go through id_modes
        exectime_start = time.perf_counter()
        scenario = self.parent.get_scenario()
        net = scenario.net
        edges = net.edges
        vtypes = scenario.demand.vtypes 
        routes = self.get_routes()

        
        timesmap = self.parent.get_timesmap()
        #distancesmap = self.parent.get_distancesmap()
        accesslevelsmap = self.parent.get_accesslevelsmap()
        
        
        fstar = edges.get_fstar(is_ignor_connections = is_ignor_connections)
        for id_vtype in self.get_vtypes():
            id_mode = vtypes.ids_mode[id_vtype]
            
            # no routing for pedestrians
            if (id_mode != net.modes.get_id_mode('pedestrian'))&(id_mode in timesmap):
                #dists = distancesmap[id_mode]
                times_orig = timesmap[id_mode].copy() 
                
                # TODO: needs to be improved with default junction waits 
                weights = times_orig.copy() 
                
                # this will subtract some meters dependent on 
                
                accesslevels =  accesslevelsmap[id_mode]
                ids_edge = edges.get_ids()
                are_valid = weights > 3*time_modespecific# 
                
                
                weights[ids_edge] -= time_modespecific * are_valid[ids_edge] *(accesslevels[ids_edge]==2)
                weights[ids_edge] *= 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge]==2)
           
                
                
                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value() == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value()>=0))
                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
                #print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
                #print '  weights',weights
                
                
                
                
                
                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
                
                for id_trip,id_route in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype]):
                    route_matched = routes.ids_edges[id_route]
                   
                    cost, route = routing.get_mincostroute_edge2edge(   route_matched[0],
                                                                            route_matched[-1], 
                                                                            weights= weights,
                                                                            fstar = fstar,
                                                                            )
                    
                                                                            
                    if len(route)>0:
                        #ids_edges.append(route)
                        #ids_trip.append(id_trip)
                        #costs.append(cost)
                        
                        id_route =  self.ids_route_fastest[id_trip]
                        if id_route>=0:
                            # there is already a previous shortest route
                            routes.set_row(id_route,
                                           #ids_edges = list(route),
                                           costs = cost,
                                           colors = color_route,
                                            )
                            # TODO!!! this assigment does not work in set_row!!!!
                            # tales only first edge !! why??
                            routes.ids_edges[id_route] = route
                            #print '  old route',id_route,type(route)
                        else:
                            # create new route
                            id_route =  routes.add_row( ids_trip = id_trip,
                                                        ids_edges = route,
                                                        costs = cost,
                                                        colors = color_route,
                                                        )
                            self.ids_route_fastest[id_trip] = id_route
                            #print '  new route',type(route)
                            
                        
                        self.durations_route_fastest[id_trip] = np.sum(times_orig[route])
                        self.timelosses_route_fastest[id_trip] = np.sum(times_orig[route_matched])-self.durations_route_fastest[id_trip]
                        #print '  route', route   
                        #print '  routes.ids_edges' ,routes.ids_edges[id_route]
        
        
        print('  exectime',time.perf_counter()-exectime_start) 
    
    def route_generic(self, weightsmap = None, is_ignor_connections = False, 
                                color_route = None,):
        """
        Generic path routing.
        """
        print('route_generic')
        # TODO: if too mant vtypes, better go through id_modes
        exectime_start = time.perf_counter()
        scenario = self.parent.get_scenario()
        net = scenario.net
        edges = net.edges
        vtypes = scenario.demand.vtypes 
        routes = self.get_routes()
        #ids_edges = []
        #ids_trip = []
        #costs = []
        accesslevelsmap = self.parent.get_accesslevelsmap()
        fstar = edges.get_fstar(is_ignor_connections = is_ignor_connections)
        distancesmap = self.parent.get_distancesmap()
        

        
        for id_vtype in self.get_vtypes():
            id_mode = vtypes.ids_mode[id_vtype]
            
            # no routing for pedestrians
            if (id_mode != net.modes.get_id_mode('pedestrian'))&(id_mode in weightsmap):
                weights = weightsmap[id_mode]
                
                distances = distancesmap[id_mode]
                
                
                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value() == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value()>=0))
                
                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
                #print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
                #print '  weights',weights

                
                for id_trip,id_route in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype]):
                    route_matched = routes.ids_edges[id_route]
                   
                    
                    cost, route = routing.get_mincostroute_edge2edge(   route_matched[0],
                                                                        route_matched[-1], 
                                                                        weights= weights,
                                                                        fstar = fstar)
                    if len(route)>0:
                        #ids_edges.append(route)
                        #ids_trip.append(id_trip)
                        #costs.append(cost)
                        
                        # ??? 
                        id_route_shortest =  self.ids_route_shortest[id_trip]
                        if id_route>=0:
                            # there is already a previous shortest route
                            routes.set_row(id_route_shortest,
                                           #ids_edges = list(route),
                                           costs = cost,
                                           colors = color_route,
                                            )
                            # TODO!!! this assigment does not work in set_row!!!!
                            # tales only first edge !! why??
                            routes.ids_edges[id_route_shortest] = route
                            #print '  old route',id_route,type(route)
                        else:
                            # create new route
                            id_route =  routes.add_row( ids_trip = id_trip,
                                                        ids_edges = route,
                                                        costs = cost,
                                                        colors = color_route,
                                                        )
                            self.ids_route_shortest[id_trip] = id_route_shortest
                            #print '  new route',type(route)
                            
                        
                        self.lengths_route_shortest[id_trip] = np.sum(distances[route])
                        #print '  route', route   
                        #print '  routes.ids_edges' ,routes.ids_edges[id_route]
        
        
        print('  exectime',time.perf_counter()-exectime_start) 
            
    def route_shortest(self, dist_modespecific = 5.0, c_modespecific = 0.9, 
                        is_ignor_connections = False, dist_min_modespecific= 15.0,
                        color_route = None):
        """
        Shortest path routing.
        """
        print('route_shortest',dist_modespecific,c_modespecific)
        # TODO: if too mant vtypes, better go through id_modes
        exectime_start = time.perf_counter()
        scenario = self.parent.get_scenario()
        net = scenario.net
        edges = net.edges
        vtypes = scenario.demand.vtypes 
        routes = self.get_routes()
        #ids_edges = []
        #ids_trip = []
        #costs = []
        accesslevelsmap = self.parent.get_accesslevelsmap()
        fstar = edges.get_fstar(is_ignor_connections = is_ignor_connections)
        distancesmap = self.parent.get_distancesmap()
        

        
        for id_vtype in self.get_vtypes():
            id_mode = vtypes.ids_mode[id_vtype]
            
            # no routing for pedestrians
            if (id_mode != net.modes.get_id_mode('pedestrian'))&(id_mode in distancesmap):
                dists_orig = distancesmap[id_mode].copy() 
                weights = dists_orig.copy() 
                
                # this will subtract some meters dependent on 
                # access-level of the edge
                accesslevels =  accesslevelsmap[id_mode]
                ids_edge = edges.get_ids()
                are_valid = weights > dist_min_modespecific
                weights[ids_edge] -= dist_modespecific * are_valid[ids_edge] * (accesslevels[ids_edge]==2)
                weights[ids_edge] *= 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge]==2)
           
                
                
                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value() == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value()>=0))
                
                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
                #print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
                #print '  weights',weights
                
                
                
                
                
                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
                
                for id_trip,id_route in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype]):
                    route_matched = routes.ids_edges[id_route]
                   
                    
                    cost, route = routing.get_mincostroute_edge2edge(   route_matched[0],
                                                                        route_matched[-1], 
                                                                        weights= weights,
                                                                        fstar = fstar)
                    if len(route)>0:
                        #ids_edges.append(route)
                        #ids_trip.append(id_trip)
                        #costs.append(cost)
                        
                        id_route =  self.ids_route_shortest[id_trip]
                        if id_route>=0:
                            # there is already a previous shortest route
                            routes.set_row(id_route,
                                           #ids_edges = list(route),
                                           costs = cost,
                                           colors = color_route,
                                            )
                            # TODO!!! this assigment does not work in set_row!!!!
                            # tales only first edge !! why??
                            routes.ids_edges[id_route] = route
                            #print '  old route',id_route,type(route)
                        else:
                            # create new route
                            id_route =  routes.add_row( ids_trip = id_trip,
                                                        ids_edges = route,
                                                        costs = cost,
                                                        colors = color_route,
                                                        )
                            self.ids_route_shortest[id_trip] = id_route
                            #print '  new route',type(route)
                            
                        
                        self.lengths_route_shortest[id_trip] = np.sum(dists_orig[route])
                        #print '  route', route   
                        #print '  routes.ids_edges' ,routes.ids_edges[id_route]
        
        
        print('  exectime',time.perf_counter()-exectime_start) 
        
    def get_ids_edge_matched(self, id_trip):
        return self.get_routes().ids_edges[self.ids_route_matched[id_trip]]
        
    def get_speedprofile(self, id_trip):
        """
        NOT USED!!??
        """
         
        points = self.parent.points
        ids_point = self.ids_points[id_trip]
        ids_point_edgeend = self.ids_points_edgeend[id_trip]
        ids_edge = self.get_routes().ids_edges[self.ids_route_matched[id_trip]]
        id_edge_current = -1
        n_edges = len(ids_edge)
        
        positions_gps = []
        times_gps = []
        ids_edges_profile = []
        ids_nodes_profile = []
        
        for id_point, coord, timestamp in zip(ids_point, points.coords[ids_point], points.timestamps[ids_point] ):
            #get_pos_from_coord(id_edge_current, coord)
            
            
            if id_point in ids_point_edgeend:
                ind = ids_point_edgeend.index(id_point)
                if ind == n_edges-1:
                    id_edge_current = -1
                else:
                    id_edge_current = ids_edge[ind+1]
                
            
            
    def get_flows(self, is_shortest_path = False):
        """
        Determine the total number of vehicles for each edge.
        returns ids_edge and flows
        """
        ids_edges = self.get_routes.ids_edges
        counts = np.zeros(np.max(self.get_net().edges.get_ids())+1,int)
        
        ids_trip = self.get_ids_selected()
        if not is_shortest_path:
            ids_route = self.ids_route_matched[ids_trip]
        else:
            ids_route = self.ids_route_shortest[ids_trip]
        inds_valid = np.flatnonzero(ids_route>0)
        for id_trip, id_route in zip(ids_trip[inds_valid],ids_route[inds_valid]):
            counts[ids_edges[id_route][:]] += 1 
        
        ids_edge = np.flatnonzero(counts)
        
        return ids_edge, counts[ids_edge].copy()
    
    def get_ids_route_selected(self):
        # TODO: here we could append direct routes 
        print('get_ids_route_selected')
        ids_route_matched = self.ids_route_matched[self.get_ids_selected()]
        ids_route_shortest = self.ids_route_shortest[self.get_ids_selected()]
        ids_route_fastest = self.ids_route_fastest[self.get_ids_selected()]
        #print '  ids_route_matched.dtype',ids_route_matched.dtype
        #print '  ids_route_shortest.dtype',ids_route_shortest.dtype
        #print '  ids_route_matched[ids_route_matched >= 0] ',ids_route_matched[ids_route_matched >= 0] 
        return np.concatenate([ ids_route_matched[ids_route_matched >= 0] ,
                                ids_route_shortest[ids_route_shortest >= 0],
                                ids_route_fastest[ids_route_fastest >= 0]])
        #return  ids_route_matched[ids_route_matched >= 0] 
             
class GpsPoints(am.ArrayObjman):
    """
    Contains data of points of a single trace.
    """
    def __init__(self,ident, mapmatching , **kwargs):
        #print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
        self._init_objman(  ident = ident, 
                            parent = mapmatching, 
                            name = 'GPS Points', 
                            info = 'GPS points database.',
                            version = 0.0,
                            **kwargs)
                                
                            
        self._init_attributes()
        self._init_constants()
            
   
   
    
            
    def _init_attributes(self):
        scenario = self.get_scenario()
        
        # ident is the path id of the trace
        
        # the actual numpy arrays are stored in .cols 
        self.add_col(am.ArrayConf( 'longitudes',     default =0.0,
                                        dtype = np.float32,
                                        groupnames = ['parameters',], 
                                        perm='rw', 
                                        name = 'Longitude',
                                        symbol = 'Lon',
                                        unit = 'deg',
                                        info = 'Longitude  of point',
                                        ))
                                        
        self.add_col(am.ArrayConf( 'latitudes',   default =0.0,
                                        groupnames = ['parameters',], 
                                        dtype = np.float32,
                                        perm='rw', 
                                        name = 'Latitude',
                                        symbol = 'Lat',
                                        unit = 'deg',
                                        info = 'Latitude of point',
                                        ))
        
        self.add_col(am.ArrayConf( 'altitudes',   default = -100000.0,
                                        groupnames = ['parameters',], 
                                        dtype = np.float32,
                                        perm='rw', 
                                        name = 'Altitude',
                                        symbol = 'Alt',
                                        unit = 'm',
                                        info = 'Altitude of point',
                                        ))
        
        
        self.add_col(am.ArrayConf( 'radii',  10.0, 
                                    dtype=np.float32,
                                    groupnames = ['parameters',], 
                                    perm='rw', 
                                    name = 'Radius',
                                    unit = 'm',
                                    info = 'Point radius, representing the imprecision of the point, which depends on the recording device ane the environment.',
                                    ))
        
                                                                                                        
        self.add_col(am.ArrayConf( 'coords',    default =[0.0,0.0,0.0],
                                        groupnames = ['parameters',], 
                                        dtype = np.float32,
                                        perm='rw', 
                                        name = 'Coordinate',
                                        symbol = 'x,y,z',
                                        unit = 'm',
                                        info = 'Local 3D coordinate  of point',
                                        ))
                                        

                                        
        self.add_col(am.ArrayConf( 'timestamps',  default =0.0,
                                        dtype = np.float32,
                                        groupnames = ['parameters',], 
                                        perm='r', 
                                        name = 'timestamp',
                                        symbol = 't',
                                        metatype = 'datetime',
                                        unit = 's',
                                        digits_fraction = 2,
                                        info = 'Time stamp of point in seconds after 01 January 1970.',
                                        ))
        self.add_col(am.ArrayConf('are_selected', default = False,
                                    dtype = np.bool_,
                                    groupnames = ['parameters',], 
                                    name = 'selected',
                                    symbol = 'Sel.',
                                    info = 'Is selected if the point is between the selected zone.',
                                    )) 
                                                                        
        # test:
        self.timestamps.metatype = 'datetime'
        self.timestamps.set_perm('r')
        
    def set_trips(self, trips):
        self.add_col(am.IdsArrayConf( 'ids_trip', trips, 
                                        groupnames = ['state'], 
                                        name = 'Trip ID', 
                                        info = 'ID of trips to which this point belongs to.',
                                        ))                                
        # put in geometry filter
        #self.add_col(am.ArrayConf( 'are_inside_boundary',  default =False,
        #                                groupnames = ['parameters',], 
        #                                perm='r', 
        #                                name = 'in boundary',
        #                                info = 'True if this the data point is within the boundaries of the road network.',
        #                                ))
    
                                        
                                        
    def get_ids_selected(self):
        """
        Returns point ids of selected traces
        """
        #print 'GpsPoints.get_ids_selected'
        #print '  ??ids_points = ',self.select_ids(self.parent.trips.are_selected[self.ids_trip.get_value()] )
        # TODO: why is this working??? do we need trips.ids_points????
        return self.select_ids(self.parent.trips.are_selected[self.ids_trip.get_value()] )
        #return self.get_ids()#self.select_ids(self.get_ids()
        #return self.select_ids(
    
    def get_scenario(self):
        return self.parent.get_scenario()
        
    def get_coords(self):
        """
        Returns an array of x,y coordinates of all points.
        """  
        return self.coords.get_value()
    
    def get_times(self):
        """
        Returns an array of time stamps of all points.
        """  
        return self.timestamp.get_value()
    
    def get_interval(self):
        if len(self)==0:
            return [0.0,0.0]
        else:
            timestamps =self.timestamps.get_value()
            return [timestamps.min(),timestamps.max()]
    
    def get_duration(self):
        ts,te = self.get_interval()
        return te-ts
    
    def get_distance(self):
        v = self.get_coords()
        #return np.linalg.norm( self.cols.coords[0:-1] - self.cols.coords[1:] )
        return np.sum( np.sqrt((v[1:,0]-v[0:-1,0])**2+ (v[1:,1]-v[0:-1,1])**2 ) )    
                
    
    def get_boundary(self):
        if len(self)==0:
            return [ 0,0,0,0]
        else:
            x_min,y_min = self.get_coords().min(0)
            x_max,y_max = self.get_coords().max(0)
            
            return [x_min,y_min,x_max,y_max] 
    
    def project(self, proj = None, offset=None):
        if proj is None:
            proj, offset = self.parent.get_proj_and_offset()
        x,y = proj(self.longitudes.get_value(), self.latitudes.get_value())
        self.get_coords()[:] = np.transpose(np.concatenate(([x+offset[0]],[y+offset[1]],[self.altitudes.get_value()]),axis=0))
 
class GpsPersons(am.ArrayObjman):
    
    def __init__(self,ident, mapmatching , **kwargs):
        #print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
        self._init_objman(  ident = ident, 
                            parent = mapmatching, 
                            name = 'GPS Persons', 
                            info = 'GPS person database.',
                            version = 0.2,
                            **kwargs)
        
        self._init_attributes()
        
        
    def _init_attributes(self):
        trips = self.parent.trips
        
        # TODO: add/update vtypes here
        self.add_col(SumoIdsConf('User', xmltag = 'id'))
  
        
        
        
        
            
        self.add_col(am.ArrayConf('ids_gender', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    choices = GENDERS,
                                    name = 'Gender',
                                    info = 'Gender of person.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('years_birth', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Birth year',
                                    info = 'Year when person has been born.',
                                    ))  

        self.add_col(am.ArrayConf('home_zips', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Home zips',
                                    info = 'ZIP code of the home of the cyclists',
                                    )) 
        self.add_col(am.ArrayConf('school_zips', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'School zips',
                                    info = 'ZIP code of the school of the cyclists',
                                    ))
        self.add_col(am.ArrayConf('work_zips', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Work zips',
                                    info = 'ZIP code of the work place of the cyclists',
                                    ))
        self.add_col(am.ArrayConf('ids_occupation', default = OCCUPATIONS['unknown'],
                                    dtype = np.int32,
                                    choices = OCCUPATIONS,
                                    groupnames = ['parameters'], 
                                    name = 'occupation',
                                    info = 'Tupe of occupation',
                                    ))       
                                    
        self.add_col(am.ArrayConf('are_frequent_user', False,
                                    dtype = np.bool_,
                                    groupnames = ['parameters'], 
                                    name = 'frequent user',
                                    info = 'If true, this person is a frequent user of the recorded transport mode.',
                                    ))  
                                    
            

        self.add_col(am.IdlistsArrayConf( 'ids_trips', trips,
                                        #groupnames = ['_private'], 
                                        name = 'Trip IDs', 
                                        info = "IDs of trips made by this vehicle. This is a collection of recorded trips associated with this person.",   
                                        )) 
        
        self.get_config('ids_trips').add_groupnames(['_private',])
                                        
        self.add_col(am.ArrayConf('numbers_tot_trip_gps', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'tot. trips',
                                    symbol = 'N tot GPS',
                                    info = 'Total number of recorded GPS traces.',
                                    ))  
        
        self.add_col(am.ArrayConf('lengths_tot_route_gps', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'tot. GPS length',
                                    symbol = 'L tot GPS',
                                    unit = 'm',
                                    info = 'Total distances of recorded GPS traces.',
                                    ))  
        self.add_col(am.ArrayConf('times_tot_route_gps', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. GPS time',
                                    symbol = 'T tot GPS',
                                    unit = 's',
                                    info = 'Total trip times of recorded GPS traces.',
                                    ))  
                                                                
        self.add_col(am.ArrayConf('speeds_av_gps', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. GPS time',
                                    symbol = 'V avg GPS',
                                    unit = 'm/s',
                                    info = 'Average speed of recorded GPS traces.',
                                    ))  
        
        
        self.add_col(am.ArrayConf('numbers_tot_trip_mached', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'tot. trips',
                                    symbol = 'N tot match',
                                    info = 'Total number of recorded GPS traces.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('lengths_tot_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. matched length',
                                    symbol = 'L tot match',
                                    unit = 'm',
                                    info = 'Total length of the matched parts of the GPS traces, measured by summing the length of edges of the matched route. Note the only a fraction of the GPS trace ma be within the given network.',
                                    )) 
                                    
        
                                    
        
                                    
        
        
        #self.add_col(am.ArrayConf('times_tot_route_matched', default = -1.0,
        #                            dtype = np.float32,
        #                            groupnames = ['results'], 
        #                            name = 'Tot. matched duration',
        #                            symbol = 'T tot match',
        #                            unit = 's',
        #                            info = 'Total duration of all matched part of the GPS trace. This is the difference in timestamps between last and first GPS point of the matched route. Note the only a fraction of the GPS trace ma be within the given network.',
        #                            ))
                                                                
        self.add_col(am.ArrayConf('lengths_tot_route_shortest', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. shortest length',
                                    symbol = 'L tot short',
                                    unit = 'm',
                                    info = 'Total length of the shortest routes.  Shortest route is connecting the first matched edge and the final matched edge.',
                                    ))
                                    
     
        self.add_col(am.ArrayConf('lengths_tot_route_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. matched length mixed access',
                                    symbol = 'L tot match mix',
                                    unit = 'm',
                                    info = 'Total length of the matched part of the GPS traces. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_tot_route_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Matched length exclusive access',
                                    symbol = 'L tot match excl',
                                    unit = 'm',
                                    info = 'Length of the matched part of the GPS trace. Note the only a fraction of the GPS trace ma be within the given network.',
                                    ))
                                                                                            
                                    
        # upgrade
        if hasattr(self,'ids_genders'): self.delete('ids_genders') 
    
    def analyze(self):
        print('Persons.analyze')
        #ids_person = self.select_ids(self.) 
        trips = self.parent.trips
        points = self.parent.points
        npsum = np.sum
        
        ids_person = self.get_ids()
        n_pers = len(ids_person)
        
        numbers_tot_trip_gps = np.zeros(n_pers, dtype = np.int32)
        lengths_tot_route_gps = np.zeros(n_pers, dtype = np.float32)
        times_tot_route_gps = np.zeros(n_pers, dtype = np.float32)
        speeds_av_gps = np.zeros(n_pers, dtype = np.float32)
        numbers_tot_trip_mached =  np.zeros(n_pers, dtype = np.int32)
        lengths_tot_route_matched = np.zeros(n_pers, dtype = np.float32)
        times_tot_route_matched = np.zeros(n_pers, dtype = np.float32)
        lengths_tot_route_shortest = np.zeros(n_pers, dtype = np.float32)
        lengths_tot_route_matched_mixed = np.zeros(n_pers, dtype = np.float32)
        lengths_tot_route_matched_exclusive = np.zeros(n_pers, dtype = np.float32)
        are_frequent_user = np.zeros(n_pers, dtype = np.bool_)
        
        timebudgets = -1*np.ones(n_pers, dtype = np.float32)
        
        i=0
        for id_person, ids_trip_all in zip(ids_person, self.ids_trips[ids_person]):
            
            # get selected trips only
            ids_trip = np.array(ids_trip_all)[trips.are_selected[ids_trip_all] ]      
            
            print('   analyze person',id_person,'ids_trip',ids_trip)
            
            if ids_trip is not None:
                if len(ids_trip) > 0:
                    # insert back-link
                    trips.ids_person[ids_trip] = id_person
                    
                    
                    numbers_tot_trip_gps[i] = len(ids_trip)
                    #print '  id_person, ids_trip',id_person, ids_trip
                    if len(ids_trip)>0:
                        
                        length_tot = npsum(trips.distances_gps[ids_trip])
                        time_tot = npsum(trips.durations_gps[ids_trip])
                        
                        lengths_tot_route_gps[i] = length_tot
                        times_tot_route_gps[i] = time_tot
                        if time_tot>0:
                            speeds_av_gps[i] = length_tot/time_tot
                        
                        
                        ## do statistics on matched trips
                        #print '    ids_route_shortest',trips.ids_route_shortest[ids_trip]
                        #print '    inds',trips.ids_route_shortest[ids_trip]>=0
                        ids_trip_matched = ids_trip[ trips.ids_route_shortest[ids_trip]>=0 ]
                        if len(ids_trip_matched)>0:
                            numbers_tot_trip_mached[i] = len(ids_trip_matched)
                            lengths_tot_route_matched[i] = npsum(trips.lengths_route_matched[ids_trip_matched])
                            #times_tot_route_matched[i] = npsum(trips.lengths_route_matched[ids_trip])
                            lengths_tot_route_shortest[i] = npsum(trips.lengths_route_shortest[ids_trip_matched])
                            lengths_tot_route_matched_mixed[i] = npsum(trips.lengths_route_matched_mixed[ids_trip_matched])
                            lengths_tot_route_matched_exclusive[i] = npsum(trips.lengths_route_matched_exclusive[ids_trip_matched])
                        
                        ## do statistics time budget
                        #coord0 = None#points.coords[ids_points[ids_trip[0]]]
                        #date = None# points.timestamps[ids_points[ids_trip[0]]]
                        #for ids_point in trips.ids_points[ids_trip]:
                        #    coord0 = None#points.coords[ids_points[ids_trip[0]]]
                        #    date = None# points.timestamps[ids_points[ids_trip[0]]]
            i += 1    
        
        
        numbers_tot_trip_gps_real = numbers_tot_trip_gps[(numbers_tot_trip_gps>0)]
        n_trips_av = np.mean(numbers_tot_trip_gps_real)               
        i = 0               
        for id_person, ids_trip_all in zip(ids_person, self.ids_trips[ids_person]):
            
            # get selected trips only
            ids_trip = np.array(ids_trip_all)[trips.are_selected[ids_trip_all] ]      
            
            print('   analyze person',id_person,'ids_trip',ids_trip)
            
            if ids_trip is not None:
                n_trips = len(ids_trip)
                if n_trips > n_trips_av:
                    are_frequent_user[i] = True  
            i+=1
        self.set_rows(  ids_person,
                        numbers_tot_trip_gps = numbers_tot_trip_gps,
                        are_frequent_user = are_frequent_user,
                        lengths_tot_route_gps = lengths_tot_route_gps,
                        times_tot_route_gps = times_tot_route_gps,
                        speeds_av_gps = speeds_av_gps,
                        numbers_tot_trip_mached = numbers_tot_trip_mached,
                        lengths_tot_route_matched = lengths_tot_route_matched,
                        times_tot_route_matched = times_tot_route_matched,
                        lengths_tot_route_shortest = lengths_tot_route_shortest,
                        lengths_tot_route_matched_mixed = lengths_tot_route_matched_mixed,
                        lengths_tot_route_matched_exclusive = lengths_tot_route_matched_exclusive,
                        )
    
    def add_trip(self, id_pers_sumo, id_trip):
        """Add trip id_pers_sumo to person id_pers
        """
        if self.ids_sumo.has_index(id_pers_sumo):
            id_pers = self.ids_sumo.get_id_from_index(id_pers_sumo)
            self.ids_trips[id_pers].append(id_trip)
        
    def make(self, id_sumo, **kwargs):
        print('make id_pers_sumo',id_sumo)
        
        id_trip = kwargs.get('id_trip',-1)
        if self.ids_sumo.has_index(id_sumo):
            # person exisis
            id_pers = self.ids_sumo.get_id_from_index(id_sumo)
            
            if id_trip >= 0:
                self.ids_trips[id_pers].append(id_trip)
            
            # debug
            #print '  exists id_pers',id_pers,self.ids_sumo[id_pers]
            #trips = self.parent.trips
            #for id_trip in self.ids_trips[id_pers]:
            #    print '    id_trip',id_trip,trips.ids_sumo[id_trip]
            
            
            #self.set_row(id_pers, **kwargs)
            return id_pers
        
        else:
            #print 'make new person',kwargs
            # add new person
            #if id_trip >= 0:
            #    ids_trip = [id_trip]
            #else:
            #    ids_trip = []
            
            
            gender = kwargs.get('gender','').lower()
            if gender in ['m','male','Male']:
                #print '  m gender=*%s*'%gender
                id_gender = GENDERS['male']
            elif gender in ['f','female','Female']:
                #print '  f gender=*%s*'%gender
                id_gender = GENDERS['female']
            else:
                #print '  u gender=*%s*'%gender
                id_gender = GENDERS['unknown']
                
            occupation = kwargs.get('occupation','')
            occupation = occupation.lower()
            if occupation in ['None','','unknown']:
                id_occupation = OCCUPATIONS['unknown']
            elif occupation in OCCUPATIONS:
                id_occupation = OCCUPATIONS[occupation]
            else:
                id_occupation = OCCUPATIONS['other']
            
            id_pers =  self.add_row( ids_sumo = id_sumo,
                                    ids_gender= id_gender,
                                    years_birth= kwargs.get('year_birth',None),
                                    ids_occupation= id_occupation,
                                    are_frequent_user= kwargs.get('is_frequent_user',None),
                                    incomes= kwargs.get('income',None),
                                    ethnicities= kwargs.get('ethnicity',None),
                                    home_zips= kwargs.get('home_zip',None),
                                    school_zips= kwargs.get('school_zip',None),
                                    work_zips= kwargs.get('work_zip',None),
                                    cycling_frequencies= kwargs.get('cycling_frequency',None),
                                    cycling_histories= kwargs.get('cycling_history',None),
                                    cyclist_types= kwargs.get('cyclist_type',None),
                                    ages_strava= kwargs.get('age_strava',None),
                                    )
            if id_trip>=0:
                self.ids_trips[id_pers] = [id_trip]
            else:
                self.ids_trips[id_pers] = []
            
            # debug
            #print '  made id_pers',id_pers, self.ids_sumo[id_pers]
            #trips = self.parent.trips
            #for id_trip in self.ids_trips[id_pers]:
            #    print '    id_trip',id_trip,trips.ids_sumo[id_trip]
            
            return id_pers


class Mapmatching(DemandobjMixin, cm.BaseObjman):
        def __init__(   self, ident='mapmatching', demand = None,
                        name = 'Mapmatching', info ='Mapmatching functionality.',
                        **kwargs):

            self._init_objman(  ident= ident, parent=demand, 
                                name = name, info = info, **kwargs)
                                
            attrsman = self.set_attrsman(cm.Attrsman(self))
            
            
            self._init_attributes()
            self._init_constants()
        
        
        
        def get_scenario(self):
            return self.parent.parent
        
        def clear_all(self):
            self.trips.clear()
            self.points.clear()
            self.persons.clear()
            self._init_constants()
            
            
        
            
        def _init_attributes(self):
            #print 'Mapmatching._init_attributes'
            attrsman = self.get_attrsman()
            
            self.points = attrsman.add( cm.ObjConf(GpsPoints('points',self)))
            self.trips = attrsman.add( cm.ObjConf(GpsTrips('trips',self)))
            self.points.set_trips(self.trips)
            self.persons = attrsman.add( cm.ObjConf(GpsPersons('persons',self)))
            self.trips.set_persons()
        
        def _init_constants(self):
            self._proj = None
            self._segvertices_xy = None
            self._distancesmap = None
            self._timesmap = None
            self._accesslevelsmap = None
            self._fstarmap = None
            
            attrsman = self.get_attrsman()
            attrsman.do_not_save_attrs(['_segvertices_xy','_proj','_distancesmap','_timesmap','_accesslevelsmap','_fstarmap'])
        
        
        def clear_routes(self):
            self.trips.clear_routes()
        
        def clear_gtfs(self):
            self.trip_stops = {}
            #self.gtfsstop_to_simstop = gtfsstop_to_simstop
            self.trip_times = {}
            self.map_service_to_trips = {}
            self.map_trip_to_shape = {}
            self.map_trip_to_route = {}
            self.ids_service_done = []
                    
                
        def delete_unselected_trips(self):
            trips = self.trips
            points = self.points
            persons = self.persons
            
            
            if len(persons) == 0:
                # no persons, just trips
                ids_del = trips.select_ids(np.logical_not(trips.are_selected.get_value()))
                
                for ids_point in trips.ids_points[ids_del]:
                    if ids_point is not None:
                        points.del_rows(ids_point)
                trips.del_rows(ids_del) 
            else:
##                for ids_trip in persons.ids_trips[persons.get_ids()]:
##                    if ids_trip is not None:
##                        ids_del =  np.array(ids_trip, dtype = np.int32)[np.logical_not(trips.are_selected[ids_trip])]
##                        for id_del,ids_point in zip(ids_del,trips.ids_points[ids_del]):
##                            ids_trip.remove(id_del)
##                            if ids_point is not None:
##                                points.del_rows(ids_point)
##                        
##                        trips.del_rows(ids_del) 
                ids_del = trips.get_ids()[(trips.are_selected[trips.get_ids()] == False)]
                del_pers = []
                trips_pers = []
                for ids_trip, id_person in zip(persons.ids_trips[persons.get_ids()], persons.get_ids()):
                    for id_trip in ids_trip:
                        if trips.are_selected[id_trip] == True:
                           trips_pers.append(id_trip)
                    persons.ids_trips[id_person] = trips_pers
                    if trips_pers == []:
                        del_pers.append(id_person)
                    trips_pers = []

                ids_points = points.get_ids()
                del_points = np.zeros(np.max(ids_points)+1, dtype = np.int32) 
                for id_del in ids_del:
                    ids_point_del = trips.ids_points[id_del]
                    del_points[ids_point_del] = ids_point_del
                points.del_rows(del_points[(del_points >0)])
                                                 
                trips.del_rows(ids_del)
                persons.del_rows(del_pers)


            
        def get_proj_and_offset(self):
            if self._proj is None:
                net = self.get_scenario().net
                proj_params = str(net.get_projparams())
                #try:
                self._proj = pyproj.Proj(proj_params)
                #except:
                #    proj_params ="+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
                #    self._proj = pyproj.Proj(self.proj_params)  
                
                self._offset = net.get_offset()
                
            return self._proj, self._offset   
        
        def get_segvertices_xy(self):
            if self._segvertices_xy is None:
                self._segvertices_xy = self.get_scenario().net.edges.get_segvertices_xy()
            
            return self._segvertices_xy
        
        def get_timesmap(self, is_check_lanes = False):
            """
            Returns a dictionary where key is id_mode and
            value is a distance-lookup table, mapping id_edge to edge distance
            """
            #print 'get_timesmap',self._distancesmap is None
            
            
            if self._timesmap is None:
                
                vtypes = self.get_scenario().demand.vtypes
                edges = self.get_scenario().net.edges
                ids_vtype = self.trips.ids_vtype[self.trips.get_ids_selected()]
                ids_mode = vtypes.ids_mode[ids_vtype]
                #print '    ids_mode',ids_mode
            
                self._timesmap = {}
                for id_mode in set(ids_mode):
                    #ids_vtype_mode = vtypes.select_by_mode(id_mode)
                    
                    self._timesmap[id_mode] = edges.get_times(  id_mode = id_mode,
                                                                is_check_lanes = is_check_lanes,
                                                                )
                    
                    
            #print '  len(self._distancesmap)',len(self._distancesmap)
            return  self._timesmap 
        
        def get_distancesmap(self, is_check_lanes = False, ids_mode = None):
            """
            Returns a dictionary where key is id_mode and
            value is a distance-lookup table, mapping id_edge to edge distance
            """
            print('get_distancesmap',self._distancesmap is None)
            
            
            if self._distancesmap is None:
                edges = self.get_scenario().net.edges
                if ids_mode is None:
                    vtypes = self.get_scenario().demand.vtypes
                    ids_vtype = self.trips.ids_vtype[self.trips.get_ids()]
                    ids_mode = vtypes.ids_mode[ids_vtype]
                print('    ids_mode',set(ids_mode),len(ids_mode))
            
                self._distancesmap = {}
                for id_mode in set(ids_mode):
                    #ids_vtype_mode = vtypes.select_by_mode(id_mode)
                    
                    self._distancesmap[id_mode] = edges.get_distances( id_mode = id_mode,
                                                                 is_check_lanes = is_check_lanes,
                                                           )
                    
                    
            #print '  len(self._distancesmap)',len(self._distancesmap)
            return  self._distancesmap    
                                                     
        def get_fstarmap(self, ids_mode = None, is_return_arrays = True, is_ignor_connections = False):
            """
            Returns a dictionary where key is id_mode and
            value is the corrisponding fstar.
            """
            print('get_fstarmap',self._fstarmap is None)
            
            
            if self._fstarmap is None:
                edges = self.get_scenario().net.edges
                if ids_mode is None:
                    vtypes = self.get_scenario().demand.vtypes
                    ids_vtype = self.trips.ids_vtype[self.trips.get_ids()]
                    ids_mode = vtypes.ids_mode[ids_vtype]
                print('    ids_mode',set(ids_mode),len(ids_mode))
            
                self._fstarmap = {}
                for id_mode in set(ids_mode):
                    #ids_vtype_mode = vtypes.select_by_mode(id_mode)
                    
                    self._fstarmap[id_mode] = edges.get_fstar(  id_mode = id_mode,
                                                                is_ignor_connections = is_ignor_connections,
                                                                is_return_arrays = is_return_arrays,
                                                            )
                    
                    
            #print '  len(self._distancesmap)',len(self._distancesmap)
            return  self._fstarmap    
                                          
        def get_accesslevelsmap(self, ids_mode = None):
            
            """
            Returns a dictionary where key is id_mode and
            value is a distance-lookup table, mapping id_edge to edge distance
            """
            
            if self._accesslevelsmap is None:
                edges = self.get_scenario().net.edges
                if ids_mode is None:
                    vtypes = self.get_scenario().demand.vtypes
                    ids_vtype = self.trips.ids_vtype[self.trips.get_ids()]
                    ids_mode = vtypes.ids_mode[ids_vtype]
                print('    ids_mode',set(ids_mode),len(ids_mode))
                
            
            
                self._accesslevelsmap = {}
                for id_mode in set(ids_mode):
                    #ids_vtype_mode = vtypes.select_by_mode(id_mode)
                    self._accesslevelsmap[id_mode] = edges.get_accesslevels(id_mode)
            return  self._accesslevelsmap 
                


class Matchresults(cm.BaseObjman):
        def __init__(self, ident, mapmatching, 
                        name = 'Mapmatching results', 
                        info ='Results of mapmatching analysis.', 
                        **kwargs):
             
            
            
            # make results a child of process or of wxgui
            # use these objects to access matched trips
                           
            self._init_objman(ident, parent=mapmatching, name = name, 
                                info = info, **kwargs)
            attrsman = self.set_attrsman(cm.Attrsman(self))
            
            self._init_attributes()
        
        
        def _init_attributes(self):
            attrsman = self.get_attrsman()
            mapmatching = self.parent
            #self.routesresults = attrsman.add(cm.ObjConf( Routesresults('routesresults',
            #                                                        self, mapmatching.trips.routes),
            #                                            groupnames = ['Route results'],
            #                                            ))
            
            # add trip results from all demand objects
            #print 'Matchresults._init_attributes'
        
        def config(self, resultobj, **kwargs):
            # attention: need to check whether already set
            # because setattr is set explicitely after add
            if not hasattr(self, resultobj.get_ident()):
                if 'groupnames' in kwargs:
                    kwargs['groupnames'].append('Results')
                else:
                    kwargs['groupnames'] = ['Results']
                attrsman = self.get_attrsman()
                attrsman.add(cm.ObjConf(  resultobj, **kwargs)) 
                setattr(self,resultobj.get_ident(), resultobj)
                   
        def get_scenario(self):
            return self.parent.get_scenario()
        
        def clear_all(self):
            self.clear()
        
        
        
        def save(self, filepath = None, is_not_save_parent=True):
            if filepath  is None:
                self.get_scenario().get_rootfilepath()+'.mmatch.obj'
            # parent will not be saved because in no_save set
            cm.save_obj(self, filepath, is_not_save_parent = is_not_save_parent)
     

class Nodesresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Node results', 
                             info = 'Table with data from matched traces on different node types.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        
        
        self.add_col(am.IdsArrayConf( 'ids_node', parent.get_scenario().net.nodes, 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'Node ID', 
                                            info = 'ID of network node.',
                                            ))
                                                                                               
        # rdundant
        #self.add_col(am.ArrayConf('types', default = 0.0,
        #                            dtype = np.int32,
        #                            choices =  parent.get_scenario().net.nodes.types.choices.copy() ,
        #                            groupnames = ['results'], 
        #                            name = 'Node type',
        #                            is_index = True,
        #                            info = 'Node type.',
        #                            ))
                                    
                                                                       
        
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        

                                    
        self.add_col(am.ArrayConf('numbers_tot_matched', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number matched',
                                    info = 'Total number of matched routes crossing this intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_matched_for_speed_analysis', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number matched for speed analysis',
                                    info = 'Total number of valid routes for the speed analysis crossing this intersection.',
                                    ))  

        self.add_col(am.ArrayConf('numbers_wait_time', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number waiting time',
                                    info = 'Total number of matched routes with waiting time detected at this node.',
                                    ))
                                    
        self.add_col(am.ArrayConf('times_wait', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time',
                                    unit = 's',
                                    info = 'Average wait times per vehicle at this node, considering all passed vehicle (even if the waiting time in this node was 0), basing on the traces valid for the speed analysis.',
                                    )) 
                                    
        self.add_col(am.ArrayConf( 'types', 0,
                                    choices = {\
                                        "priority":0,
                                        "traffic_light":1,
                                        "right_before_left":2,
                                        "unregulated":3,
                                        "priority_stop":4,
                                        "traffic_light_unregulated":5,
                                        "allway_stop":6,
                                        "rail_signal":7,
                                        "zipper":8,
                                        "traffic_light_right_on_red":9,
                                        "rail_crossing":10,
                                        "dead_end":11,
                                        },
                                    dtype = np.int32,
                                    perm='rw',
                                    name = 'Type',
                                    info = 'Node type.',
                                    xmltag = 'type',
                                    ))

        # this is actually a property defined in the TLS logic
        self.add_col(am.ArrayConf( 'types_tl', 0,
                                dtype = np.int32,
                                choices = {\
                                    "none":0,
                                    "static":1,
                                    "actuated":2,
                                    },
                                perm='rw',
                                name = 'TL type',
                                info = 'Traffic light type.',
                                xmltag = 'tlType',
                                ))

        self.add_col(am.ArrayConf( 'turnradii',  1.5,
                            dtype=np.float32,
                            groupnames = ['state'],
                            perm='rw',
                            name = 'Turn rad',
                            unit = 'm',
                            info = 'optional turning radius (for all corners) for that node.',
                            xmltag = 'radius',
                            ))
                            
        self.add_col(am.ArrayConf('n_connections', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N conn at node',
                                    name = 'number connections at node',
                                    info = 'Number of connections at node. This indicate the compexity of the intersection',
                                    )) 

        self.add_col(am.ArrayConf('n_left_turns', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N left turns at node',
                                    name = 'number left turns at node',
                                    info = 'Number of left turns at node',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_right_turns', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N right turn at node',
                                    name = 'number right turn at node',
                                    info = 'Number of right turn at node.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_crossings', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N crossings at node',
                                    name = 'number crossings at node',
                                    info = 'Number of crossings at node.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_u_turns', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N u-turns at node',
                                    name = 'number u-turns at node',
                                    info = 'Number of u-turns at node. ',
                                    )) 
                                    
        self.add_col(am.ListArrayConf('list_waiting_times', 
                                    groupnames = ['results'],
                                    perm='rw', 
                                    name = 'Waiting times',
                                    unit = 's',
                                    info = 'List of waiting times registered in this intersection.',
                                    ))

    
    def get_nodes_significant(self, ids_node_raw = None, 
                                n_lane_in_min =2, n_lane_out_min = 2):
        """
        Returns an array with significant nodes where wait times
        can be expected.
        """
        nodes = self.ids_node.get_linktab()
        net = nodes.parent
        lanes = net.lanes
        edges = net.edges
        if ids_node_raw is None:
            ids_node_raw = nodes.get_ids()
            
        ids_node = []
        id_mode_ped = net.modes.get_id_mode('pedestrian')
        id_ped_allow = [id_mode_ped]
        for id_node,type, ids_incoming, ids_outgoing, id_tls  in  zip(\
                                ids_node_raw,
                                nodes.types[ids_node_raw], 
                                nodes.ids_incoming[ids_node_raw],
                                nodes.ids_outgoing[ids_node_raw],
                                nodes.ids_tls[ids_node_raw]):
            
            if (ids_incoming is not None)&(ids_outgoing is not None):
                n_in =  len(ids_incoming)
                n_out = len(ids_outgoing)
                for ids_lane in edges.ids_lanes[ids_incoming]:
                    if len(ids_lane) == 1:
                        if lanes.ids_modes_allow[ids_lane[0]] == id_ped_allow:
                            n_in -=1
                for ids_lane in edges.ids_lanes[ids_outgoing]:
                    if len(ids_lane) == 1:
                        if lanes.ids_modes_allow[ids_lane[0]] == id_ped_allow:
                            n_out -=1
                if (n_in >=n_lane_in_min)&(n_out >=n_lane_out_min):
                    ids_node.append(id_node)
        return ids_node
                    
    def get_times_wait_est(self):
        """
        Estimate wait times at all nodes.
        Returns an array times_wait_est such that 
        the average wait tie at node id_node is
        times_wait_est[id_node]
        """
        print('get_times_wait_est')
        nodes = self.ids_node.get_linktab()
        ids_node = nodes.get_ids()
        ids_node_signif = self.get_nodes_significant()
        #nodetypes = nodes.types[ids_node_signif]
        map_nodetype_to_times_wait = self.get_map_nodetype_to_times_wait()
        
        print('  map_nodetype_to_times_wait',map_nodetype_to_times_wait)
        
        times_wait = np.zeros(max(ids_node)+1, dtype = np.int32)
        print('  ',np.min(nodes.types[ids_node_signif]),np.max(nodes.types[ids_node_signif]))
        
        times_wait[ids_node_signif] = map_nodetype_to_times_wait[nodes.types[ids_node_signif]]
        
        #for nodetype, time_wait  in map_nodetype_to_times_wait.items():
        #    times_wait[ids_node_signif[nodetypes == nodetype]] = time_wait
        return  times_wait   
        
    def get_map_nodetype_to_times_wait(self):
        print('get_map_nodetype_to_times_wait')
        nodes = self.ids_node.get_linktab()
        ids_res = self.get_ids()
        nodetypes =  nodes.types[self.ids_node[ids_res]]
        nodetypeset = list(nodes.types.choices.values())
        #map_type_to_typename = get_inversemap(nodes.types.choices)
        #map_type_to_times_wait = {}
        map_type_to_times_wait = np.zeros(max(nodetypeset)+1,dtype = np.float32)
        for thistype in nodetypeset:
            inds_res = np.flatnonzero(nodetypes == thistype)
            if len(inds_res)>0:
                map_type_to_times_wait[thistype] = np.mean(self.times_wait[ids_res[inds_res]])
        
        return map_type_to_times_wait                          
                
class Edgesresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Edges results', 
                             info = 'Table with results from edges that are part of matched routes or alternative routes.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        
        
        #self.add(cm.AttrConf(  'datapathkey',datapathkey,
        #                        groupnames = ['_private'], 
        #                        name = 'data pathkey',
        #                        info = "key of data path",
        #                        ))
        
         
       
                                                                       
        self.add_col(am.IdsArrayConf( 'ids_edge', parent.get_scenario().net.edges, 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'Edge ID', 
                                            info = 'ID of network edge.',
                                            ))
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
                            
        
        self.add_col(am.ArrayConf('speed_average', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Avg. speed',
                                    unit = 'm/s',
                                    info = 'Average speed on this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('speed_average_in_motion', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Avg. speed in mot.',
                                    unit = 'm/s',
                                    info = 'Average speed in motion this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('average_slopes', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Avg. slope.',
                                    unit = 'm/s',
                                    info = 'Average edge slope.',
                                    )) 
                                                                
        self.add_col(am.ArrayConf('durations_tot_matched', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. duration',
                                    unit = 's',
                                    info = 'Total time of matched routes spent on this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_tot_matched', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number matched',
                                    info = 'Total number of matched routes crossing this edge.',
                                    )) 
                                    
        
        self.add_col(am.ArrayConf('numbers_tot_shortest', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number shortest',
                                    info = 'Total number of shortest routes crossing this edge.',
                                    )) 
        
        self.add_col(am.ArrayConf('numbers_tot_fastest', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number fastest',
                                    info = 'Total number of fastest routes crossing this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_matched_for_speed_analysis', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number matched for speed analysis',
                                    info = 'Total number of valid routes for the speed analysis crossing this edge.',
                                    )) 
                                                                        
        self.add_col(am.ArrayConf('differences_dist_tot_shortest', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. short. length difference',
                                    unit = 'm',
                                    info = 'Total deviation caused by this edge. The deviation is the sum of length differences between matched route length and the corrisponding shortest route only on the section containing this edge.',
                                    )) 
        
        
        self.add_col(am.ArrayConf('differences_dist_rel_shortest', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Rel. deviation',
                                    unit = 'm',
                                    info = 'Relative deviation caused by this edge. The deviation is the sum of length differences between matched route length and the corrisponding shortest route only on the section containing this edge, devided by the number of users being deviated.',
                                    ))
                                                                
        self.add_col(am.ArrayConf('differences_dist_tot_fastest', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. fast. length difference',
                                    unit = 'm',
                                    info = 'Sum of the total length differences between matched route length and the corrisponding fastest route using this edge.',
                                    )) 
                                                                
        self.add_col(am.ArrayConf('probabilities_tot_matched', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'trip probab.',
                                    info = 'The probability that the edge has been used by the totality of all trips.',
                                    )) 
        
        
        self.add_col(am.ArrayConf('flows_est', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Estim. Flows',
                                    unit = '1/h',
                                    info = 'Estimated vehicle flows as a result of all trips.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'av. wait time',
                                    unit = 's',
                                    info = 'Average wait times per vehicle passed on this edge, basing on the traces available for the speed analysis, including stops at junctions or TLS.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_wait_edge', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number waiting at edge',
                                    info = 'Average wait times per vehicle at this edge, considering all passed vehicle (even if the waiting time in this edge was 0), basing on the traces valid for the speed analysis.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_junc', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'av. wait time at junction',
                                    unit = 's',
                                    info = 'Average wait times per vehicle at the junction where the edge is directed, considering all passed vehicle (even if the waiting time in this node was 0), basing on the traces valid for the speed analysis.',
                                    )) 
        
        self.add_col(am.ArrayConf('numbers_wait_junc', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number waiting at junction',
                                    info = 'Number of trips where waiting has been detected at junction at the end of this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_tls', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'av. wait time at TLS',
                                    unit = 's',
                                    info = 'Average wait times per vehicle at a traffic light systems at the end of this edge.',
                                    )) 
        self.add_col(am.ArrayConf('numbers_tls_wait_edge', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number waiting at tls',
                                    info = 'Number of trips where waiting has been detected at tls at the end of this edge.',
                                    ))    
                                                                   
        self.add_col(am.ListArrayConf('edge_speeds',
                                    groupnames = ['results'],
                                    perm='rw', 
                                    name = 'Edge speeds',
                                    unit = 'm/s',
                                    info = 'List of average speeds of matched trips in this edge, from the beginning, up to the Junction range.',
                                    ))
                                    
        self.add_col(am.ListArrayConf('edge_speeds_in_motion', 
                                    groupnames = ['results'],
                                    perm='rw', 
                                    name = 'Edge speeds in motion',
                                    unit = 'm/s',
                                    info = 'List of average speeds in motion of matched trips in this edge, from the beginning, up to the Junction range.',
                                    ))
##        self.add_col(am.ListArrayConf( 'speedana_point_times', 
##                                    groupnames = ['results','_private'], 
##                                    perm='rw', 
##                                    name = 'Point times',
##                                    unit = 's',
##                                    info = 'List of time points associated with each matched GPS point of this route. First point in routs has zero seconds.',
##                                    ))
    def init_for_routes(self, routes):
        """
        Initializes a row for each edge ID in routes
        """
        ids_edge_init = set()
        for ids_edge in routes.ids_edges.get_value():
            if ids_edge is not None:
                ids_edge_init.update(ids_edge)
        ids_edge_init = list(ids_edge_init)        
        # return result ids and respective edge ids
        ids_edgeresmap = np.zeros(np.max(ids_edge_init)+1,dtype = np.int32)
        ids_edgeresmap[ids_edge_init] = self.add_rows(n=len(ids_edge_init), ids_edge = ids_edge_init)
        print('init_for_routes: created',len(ids_edge_init),'entries for edgeresults  max(ids_edge)', np.max(ids_edge_init))

        return ids_edgeresmap
    
    def init_for_edges(self, edges):
        """
        Initializes a row for connection edge ID in a list of edges
        """
        ids_edge_init = set()
        ids_edge_init = list(edges)        
        # return result ids and respective edge ids
        ids_edgeresmap = np.zeros(np.max(ids_edge_init)+1,dtype = np.int32)
        ids_edgeresmap[ids_edge_init] = self.add_rows(n=len(ids_edge_init), ids_edge = ids_edge_init)
        print('init_for_routes: created',len(ids_edge_init),'entries for edgeresults  max(ids_edge)', np.max(ids_edge_init))
        return ids_edgeresmap
    
    def get_edgeresmap(self):
        """
        Returns a vector where the index is the edge ID 
        and the value is the edge result ID
        """
        ids_edgeres = self.get_ids()
        ids_edgeresmap = np.zeros(np.max(self.ids_edge[ids_edgeres])+1,dtype = np.int32)
        ids_edgeresmap[self.ids_edge[ids_edgeres]] = ids_edgeres
        return ids_edgeresmap
         
class Connectionsresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Connection results', 
                             info = 'Table with results from connections that are part of matched routes or alternative routes.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        
        
        #self.add(cm.AttrConf(  'datapathkey',datapathkey,
        #                        groupnames = ['_private'], 
        #                        name = 'data pathkey',
        #                        info = "key of data path",
        #                        ))
        
         
       
        if parent != None:                                                               
            self.add_col(am.IdsArrayConf( 'ids_connection', parent.get_scenario().net.edges, 
                                                groupnames = ['state'], 
                                                is_index = True,
                                                name = 'connection ID', 
                                                info = 'ID of network connection.',
                                                ))
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
                            
        
##        self.add_col(am.ArrayConf('speeds_average', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Avg. speed',
##                                    unit = 'm/s',
##                                    info = 'Average speed on this edge.',
##                                    )) 
                                    
##        self.add_col(am.ArrayConf('speeds_inmotion', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Avg. speed in mot.',
##                                    unit = 'm/s',
##                                    info = 'Average speed in motion this edge.',
##                                    )) 
                                                                
##        self.add_col(am.ArrayConf('durations_tot_matched', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Tot. duration',
##                                    unit = 's',
##                                    info = 'Total time of matched routes spent on this edge.',
##                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_tot_matched', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N tot matched',
                                    name = 'number matched',
                                    info = 'Total number of matched routes crossing this edge.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_matched_for_speed_analysis', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N tot matched dyn',
                                    name = 'number matched for speed analysis',
                                    info = 'Total number of valid routes for the speed analysis crossing this connection.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('numbers_wait', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    symbol = 'N tot wait time',
                                    name = 'number waiting',
                                    info = 'Number of trips where waiting has been detected at connection.',
                                    ))
                                    
##        self.add_col(am.ArrayConf('numbers_tot_shortest', default = 0,
##                                    dtype = np.int32,
##                                    groupnames = ['results'], 
##                                    name = 'number shortest',
##                                    info = 'Total number of shortest routes crossing this edge.',
##                                    )) 
##        
##        self.add_col(am.ArrayConf('numbers_tot_fastest', default = 0,
##                                    dtype = np.int32,
##                                    groupnames = ['results'], 
##                                    name = 'number fastest',
##                                    info = 'Total number of fastest routes crossing this edge.',
##                                    )) 
##                                    
##        self.add_col(am.ArrayConf('differences_dist_tot_shortest', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Tot. short. length difference',
##                                    unit = 'm',
##                                    info = 'Total deviation caused by this edge. The deviation is the sum of length differences between matched route length and the corrisponding shortest route only on the section containing this edge.',
##                                    )) 
        
        
##        self.add_col(am.ArrayConf('differences_dist_rel_shortest', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Rel. deviation',
##                                    unit = 'm',
##                                    info = 'Relative deviation caused by this edge. The deviation is the sum of length differences between matched route length and the corrisponding shortest route only on the section containing this edge, devided by the number of users being deviated.',
##                                    ))
                                                                
##        self.add_col(am.ArrayConf('differences_dist_tot_fastest', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Tot. fast. length difference',
##                                    unit = 'm',
##                                    info = 'Sum of the total length differences between matched route length and the corrisponding fastest route using this edge.',
##                                    )) 
                                                                
##        self.add_col(am.ArrayConf('probabilities_tot_matched', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'trip probab.',
##                                    info = 'The probability that the edge has been used by the totality of all trips.',
##                                    )) 
##        
        
##        self.add_col(am.ArrayConf('flows_est', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'Estim. Flows',
##                                    unit = '1/h',
##                                    info = 'Estimated vehicle flows as a result of all trips.',
##                                    )) 
                                    
##        self.add_col(am.ArrayConf('times_wait', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'wait time',
##                                    unit = 's',
##                                    info = 'Average  wait times per vehicle passed on this edge, including stops at junctions or TLS.',
##                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    symbol = 'Wait time',
                                    name = 'wait time ',
                                    unit = 's',
                                    info = 'Average wait times per vehicle at connection, considering all passed vehicle (even if the waiting time in this connection was 0), basing on the traces valid for the speed analysis.',
                                    )) 
        
 
                                    
##        self.add_col(am.ArrayConf('times_wait_tls', default = 0.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'wait time at TLS',
##                                    unit = 's',
##                                    info = 'Average wait times per vehicle at a traffic light systems.',
##                                    )) 
                                    
        self.add_col(am.ArrayConf('are_tls', default = 0,
                                    dtype = np.bool_,
                                    groupnames = ['results'], 
                                    symbol = 'TLS',
                                    name = 'number waiting at tls',
                                    info = 'Number of trips where waiting has been detected at tls.',
                                    )) 
                                    
##        self.add_col(am.ArrayConf('numbers_wait_tls', default = 0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'number waiting at tls',
##                                    info = 'Number of trips where waiting has been detected at tls.',
##                                    )) 
        
        self.add_col(am.ArrayConf('type_turn', default = '',
                                    dtype = np.object_,
                                    groupnames = ['results'], 
                                    symbol = 'Turn type',
                                    name = 'turn typology',
                                    info = 'Typology of turn',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('numbers_connections_at_node', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    symbol = 'N conn at node',
                                    name = 'number connections at node',
                                    info = 'Number of connections within the node in which the connection is included. This indicates the complexity of the intersection in which the connection is included.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('lengths', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    symbol = 'L connection',
                                    name = 'Length of the connection',
                                    unnit = '[m]',
                                    info = 'Direct length between the start and the end of the intersection.',
                                    )) 
                                    
        self.add_col(am.ListArrayConf('list_waiting_times', 
                                    groupnames = ['results'],
                                    perm='rw', 
                                    name = 'Waiting times',
                                    unit = 's',
                                    info = 'List of waiting times registered in this connection.',
                                    ))
                                    
    def init_for_connections(self, connections):
        """
        Initializes a row for connection edge ID in routes
        """
        ids_connection_init = set()
        ids_connection_init = list(connections)        
        # return result ids and respective edge ids
        ids_connectionresmap = np.zeros(np.max(ids_connection_init)+1,dtype = np.int32)
        ids_connectionresmap[ids_connection_init] = self.add_rows(n=len(ids_connection_init), ids_connection = ids_connection_init)
        print('init_for_routes: created',len(ids_connection_init),'entries for connectionresults  max(ids_connection)', np.max(ids_connection_init))
        return ids_connectionresmap
    
    def get_connectionresmap(self):
        """
        Returns a vector where the index is the connection ID 
        and the value is the edge result ID
        """
        ids_connectionres = self.get_ids()
        ids_connectionresmap = np.zeros(np.max(self.ids_connection[ids_connectionres])+1,dtype = np.int32)
        ids_connectionresmap[self.ids_connection[ids_connectionres]] = ids_connectionres
        return ids_connectionresmap
                                      
class Routesresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Route results', 
                             info = 'Table with results from analysis of each matched route.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        

                                                    
        self.add_col(am.IdsArrayConf( 'ids_route', parent.parent.trips.get_routes(), 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'ID route', 
                                            info = 'ID of route.',
                                            ))
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
        self.add_col(am.ArrayConf('distances', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'route distance',
                                    unit = 'm',
                                    info = 'Sum of the traveled edge lenghts.',
                                    ))
                                    
        self.add_col(am.ArrayConf('lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'distance within center',
                                    unit = 'm',
                                    info = 'Sum of the traveled edge lenghts of edges that have either the starting or ending point inside the center.',
                                    ))
                                    
        self.add_col(am.ArrayConf('distances_real', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Effective distance',
                                    unit = 'm',
                                    info = 'Distance between the first and last GPS points projected in the route Polilyne (that is the seguence of edge and connector traveled).',
                                    ))
                                    
        self.add_col(am.ArrayConf('durations', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'duration',
                                    unit = 's',
                                    info = 'Time duration of the route.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('durations_real', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Effective duration',
                                    unit = 's',
                                    info = 'Time duration of the route, after the GPS point filter for the speed analysis, that often involved the first and lasts points.',
                                    ))
                                    
        self.add_col(am.ArrayConf('average_speeds', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed',
                                    unit = 'm/s',
                                    info = 'Average speed of the trips.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('average_speeds_real', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Corrected average speed',
                                    unit = 'm/s',
                                    info = 'Corrected average speed with real distance and duration.',
                                    ))   
                                         
        self.add_col(am.ArrayConf('timestamps', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'timestamps',
                                    unit = 's',
                                    info = 'Timestamp of the beginning of the journey',
                                    ))                                    
              
        self.add_col(am.ArrayConf('lengths_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length mixed access',
                                    symbol = 'L mix',
                                    unit = 'm',
                                    info = 'Length of roads with mixed access.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length exclusive access',
                                    symbol = 'L excl',
                                    unit = 'm',
                                    info = 'Length of roads with exclusive access.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length contrary direction',
                                    symbol = 'L contr',
                                    unit = 'm',
                                    info = 'Length of roads in the contrary direction.',
                                    ))
                                    
        self.add_col(am.ArrayConf('lengths_low_priority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length low priority',
                                    symbol = 'L lowprio',
                                    unit = 'm',
                                    info = 'Length of low priority roads. These are roads with either speed limit to 30 or below, or classified as residential, or exclusive bike or pedestrian ways. This correspond to SUMO priorities 1-6.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_overlap_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length overlap',
                                    symbol = 'L overl',
                                    unit = 'm',
                                    info = 'Length of overlap with matched route.',
                                    ))
        
        self.add_col(am.ArrayConf('numbers_nodes', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of nodes',
                                    symbol = 'N nodes',
                                    info = 'Total number of nodes.',
                                    ))
                                    
        self.add_col(am.ArrayConf('av_n_connections_per_intersection', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'N av connections per intersection',
                                    symbol = 'N av conn. per inters. ',
                                    info = 'Average number of intersections within the crossed intersections.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_left_turns', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of left turns',
                                    symbol = 'N left turns',
                                    info = 'Total number of left turns at intersections.',
                                    ))
        self.add_col(am.ArrayConf('numbers_right_turns', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of right turns',
                                    symbol = 'N right turns',
                                    info = 'Total number of right turns at intersections.',
                                    ))
        self.add_col(am.ArrayConf('numbers_crossings', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of crossings',
                                    symbol = 'N crossings',
                                    info = 'Total number of crossings at intersections.',
                                    ))
        self.add_col(am.ArrayConf('numbers_u_turns', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of U-turns',
                                    symbol = 'N U-turns',
                                    info = 'Total number of U-turns at intersections.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_nodes_tls', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of TL nodes',
                                    symbol = 'N TL',
                                    info = 'Total number of traffic light controlled nodes.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_tls_left_turns', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of tls  left turns',
                                    symbol = 'N tls left turns',
                                    info = 'Total number of tls left turns at intersections.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_tls_right_turns', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of tls  right turns',
                                    symbol = 'N tls  right turns',
                                    info = 'Total number of tls right turns at intersections.',
                                    ))
                                    
        self.add_col(am.ArrayConf('numbers_tls_crossings', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of tls  crossings',
                                    symbol = 'N tls  crossings',
                                    info = 'Total number of tls crossings at intersections.',
                                    ))
        
        
        self.add_col(am.ArrayConf('numbers_prioritychange', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'N prio',
                                    info = 'Total number of change in road priority.',
                                    ))

                                    
        self.add_col(am.ArrayConf('times_inmotion', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Time in motion',
                                    symbol = 'Time motion',
                                    unit = 's',
                                    info = 'Times while moving during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait',
                                    symbol = 'N times wait',
                                    unit = 's',
                                    info = 'Number of wait times or times of low speed during the matched trip.',
                                    ))         
                                    
        self.add_col(am.ArrayConf('times_wait', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time',
                                    symbol = 'Wait time',
                                    unit = 's',
                                    info = 'Wait times or times of low speed during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_edges', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at edges',
                                    symbol = 'N times wait edges',
                                    unit = 's',
                                    info = 'Number of wait times or times of low speed at edges during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_edges', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at edges',
                                    symbol = 'wait time edges',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at edges during the matched trip.',
                                    ))                          
                                    
        self.add_col(am.ArrayConf('n_times_wait_tls', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at TLS',
                                    symbol = 'N times wait tls',
                                    unit = 's',
                                    info = 'Number of wait times or times of low speed at traffic lights during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_tls', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at TLS',
                                    symbol = 'wait time tls',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at traffic lights during the matched trip.',
                                    )) 
        
        self.add_col(am.ArrayConf('n_times_wait_junction', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at junktions',
                                    symbol = 'N times wait junc',
                                    unit = 's',
                                    info = 'Number of junktions where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_junction', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at junktions',
                                    symbol = 'wait time junct',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all junctions during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_left_turn', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at left turns',
                                    symbol = 'N times wait left turns',
                                    unit = 's',
                                    info = 'Number of left turns where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_left_turn', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at left turns',
                                    symbol = 'wait times left turns',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all left turns during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_right_turn', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at right turns',
                                    symbol = 'N times wait right turns',
                                    unit = 's',
                                    info = 'Number of right turns where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_right_turn', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at right turns',
                                    symbol = 'wait times right turns',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all right turns during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_crossing', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at crossings',
                                    symbol = 'N times wait crossings',
                                    unit = 's',
                                    info = 'Number crossings where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_crossing', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at crossings',
                                    symbol = 'wait times crossings',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all crossings during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_left_turn_tls', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at tls left turns',
                                    symbol = 'N times wait tls left turns',
                                    unit = 's',
                                    info = 'Number tls left turns where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_left_turn_tls', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at tls left turns',
                                    symbol = 'wait times tls left turns',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all tls left turns during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_right_turn_tls', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at tls right turns',
                                    symbol = 'N times wait tls right turns',
                                    unit = 's',
                                    info = 'Number of tls right turns where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_right_turn_tls', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at tls right turns',
                                    symbol = 'wait times tls right turns',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all tls right turns during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('n_times_wait_crossing_tls', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of wait time at tls crossings',
                                    symbol = 'N times wait tls crossings',
                                    unit = 's',
                                    info = 'Number of tls crossings where a waiting time have been individuated during the matched trip.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('times_wait_crossing_tls', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'wait time at tls crossings',
                                    symbol = 'wait times tls crossings',
                                    unit = 's',
                                    info = 'Wait times or times of low speed at all tls crossings during the matched trip.',
                                    ))             
                                    

        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_left_turn', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at left turns',
                                    symbol = 'Expected wait time at left turns',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at left turns.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_right_turn', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at right turns',
                                    symbol = 'Expected wait time tot match Dyn at right turns',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at right turns.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_crossing', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at crossings',
                                    symbol = 'Expected wait time tot match Dyn at crossings',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at crossings.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_left_turn_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at tls left turns',
                                    symbol = 'Expected wait time tot match Dyn at tls left turns',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at tls left turns.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_right_turn_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at tls right turns',
                                    symbol = 'Expected wait time tot match Dyn at tls right turns',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at tls right turns.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_crossing_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at tls crossings',
                                    symbol = 'Expected wait time tot match Dyn at tls crossings',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users, at tls crossings.',
                                    ))
   
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at Nodes',
                                    symbol = 'Expected wait time tot match Dyn at Nodes',
                                    unit = 's',
                                    info = 'Expected total waiting time at nodes, based on average waiting time registered from all other users for same matched nodes.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at tls Nodes',
                                    symbol = 'Expected wait time tot match Dyn at tls Nodes',
                                    unit = 's',
                                    info = 'Expected total waiting time at edges, based on average waiting time registered from all other users for same matched tls Nodes.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at Edges',
                                    symbol = 'Expected wait time tot match Dyn at Edges',
                                    unit = 's',
                                    info = 'Expected total waiting time at edges, based on average waiting time registered from all other users for same matched edges.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched1', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at Edges/Connections',
                                    symbol = 'Expected wait time  at Edges/Connections',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users for same matched edges and connections.',
                                    ))
                                    
        self.add_col(am.ArrayConf('expected_waiting_times_tot_route_matched2', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. expected matched waiting times at Edges/intersections',
                                    symbol = 'Expected wait time at Edges/intersections',
                                    unit = 's',
                                    info = 'Expected total waiting time, based on average waiting time registered from all other users for same matched edges and intersections.',
                                    )) 
                                     
                                    
        self.add_col(am.ListArrayConf( 'ids_valid_point_speedana', 
                                    groupnames = ['results','_private'], 
                                    perm='rw', 
                                    name = 'Valid points for speedana',
                                    info = 'List of the valid trip points ids for the speed analysis.',
                                    ))      
        
        self.add_col(am.ListArrayConf( 'speedana_point_speeds', 
                                    groupnames = ['results','_private'], 
                                    perm='rw', 
                                    name = 'Point speeds',
                                    unit = 'm/s',
                                    info = 'List of velocities associated with each matched GPS  of this route.',
                                    ))
                                    
        self.add_col(am.ListArrayConf( 'speedana_point_pos', 
                                    groupnames = ['results','_private'], 
                                    perm='rw', 
                                    name = 'Point positions',
                                    unit = 'm',
                                    info = 'List of point positions on edge associated with each matched GPS point of this route.',
                                    ))
        
        self.add_col(am.ListArrayConf( 'speedana_point_times', 
                                    groupnames = ['results','_private'], 
                                    perm='rw', 
                                    name = 'Point times',
                                    unit = 's',
                                    info = 'List of time points associated with each matched GPS point of this route. First point in routs has zero seconds.',
                                    ))
                                    
        self.add_col(am.ListArrayConf( 'edge_cumulative_dists', 
                                    groupnames = ['results','_private'], 
                                    perm='rw', 
                                    name = 'Point times',
                                    unit = 'm',
                                    info = 'Cumulative progressive dists of the stretched route',
                                    ))
                                    
        self.add_col(am.ListArrayConf( 'ids_pointedges', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Point edge IDs', 
                                        info = "This is a list edge IDs corrisponding to each matched GPS point of this route.",   
                                        ))
                                        
        self.add_col(am.ListArrayConf( 'ids_arc', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Ids route arcs', 
                                        info = "This is a list edge or connector IDs corrisponding to each matched GPS point of this route.",   
                                        )) 
                                          
        self.add_col(am.ListArrayConf( 'cumulative_dists', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Cumulative edge dists', 
                                        info = "This is a list of the cumulative edge + connection dists of the stretched matched trip.",   
                                        ))
                                        
                                    
        
        self.add_col(am.ListArrayConf( 'is_connection', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Is connection', 
                                        info = "This is a list of 1 and 0, where 1 means that the relative id_arc is a connection and 0 means that the relative id_arc is an edge.",   
                                        )) 
                                        
        self.add_col(am.ListArrayConf( 'ids_arc_point', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Ids route arcs', 
                                        info = "This is a list of edge + connection ids associated with the trip points.",   
                                        ))                                     
        
        self.add_col(am.ListArrayConf( 'is_connection_point', self.parent.get_scenario().net.edges,
                                        groupnames = ['results','_private'], 
                                        name = 'Is connection', 
                                        info = "This is a list of 1 and 0, where 1 means that the relative id_arc_point is a connection and 0 means that the relative id_arc_point is an edge.",   
                                        )) 
                      
                                             
    def get_speeds_inmotion(self, ids_routeres):
        return self.distances[ids_routeres]/self.times_inmotion[ids_routeres]
    
    def get_speeds_inmotion_est(self):
        """
        Estimate speeds in motion.
        Returns an array speeds_inmotion_est such that 
        the average speed in motion of trip id_trip is
        speeds_inmotion_est[id_trip]
        """
        routes = self.ids_route.get_linktab()
        
        ids_routeres = self.get_ids()
        ids_trip = routes.ids_trip[self.ids_route[ids_routeres]]
        
        if len(ids_trip)>0:
            speeds_inmotion_est = np.zeros(max(ids_trip)+1, dtype = np.float32)
        else:
            return []
        
        #ids_trip_matched = self.ids_trip[ids_tripres]
        speeds_inmotion_est[ids_trip] = self.get_speeds_inmotion(ids_routeres)
        return speeds_inmotion_est
                                                                                                                                                                                                            

class Shortestrouter(Process):
    def __init__(self, ident, mapmatching,  logger = None, **kwargs):
        print('Shortestrouter.__init__')
        
        # TODO: let this be independent, link to it or child??
        
           
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Shortest Router', 
                            logger = logger,
                            info ='Shortest path router.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        

                            
        self.c_modespecific = attrsman.add(cm.AttrConf( 'c_modespecific',kwargs.get('c_modespecific',1.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode constant', 
                            info = 'The Mode constant is multiplied with the the distance of each edge if vehicle has exclusive access.',
                            ))
                            
        self.dist_modespecific = attrsman.add(cm.AttrConf( 'dist_modespecific',kwargs.get('dist_modespecific',5.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode dist', 
                            unit = 'm',
                            info = 'This mode specific distance is multiplied with the access-level and subtracted from the edge distance in case the edge is exclusive for the mode of the matched trip.',
                            ))
         
         
        
        self.dist_min_modespecific = attrsman.add(cm.AttrConf( 'dist_min_modespecific',kwargs.get('dist_min_modespecific',15.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode dist min', 
                            unit = 'm',
                            info = 'Minimum edge distance for which mode specific modifications take place. This is to revent that a sum of very short edges with exclusive access causes the route to receive too much cost reduction.',
                            ))
                                                
        self.is_ignor_connections = attrsman.add(cm.AttrConf( 'is_ignor_connections',kwargs.get('is_ignor_connections',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Ignore connections', 
                            info = 'Ignore connections means that the vehicle can always make turns into all possible edges, whether they are allowed or not.',
                            ))
        self.color_route = attrsman.add(cm.AttrConf(  'color_route', kwargs.get('color_route',COLOR_SHORTEST_ROUTE.copy() ),
                                        groupnames = ['options'],
                                        perm='wr', 
                                        metatype = 'color',
                                        name = 'Route color', 
                                        info = 'Color of matched routes used in various visualization tools.',
                                        ))
                                        
    def do(self):
        print('Shortestrouter.do')
        # links
        mapmatching = self.parent
        trips = mapmatching.trips
        trips.route_shortest(   dist_modespecific = self.dist_modespecific, 
                                dist_min_modespecific = self.dist_min_modespecific,
                                c_modespecific = self.c_modespecific, 
                                is_ignor_connections = self.is_ignor_connections,
                                color_route = self.color_route,
                                )
        return True   
                                         
class Fastestrouter(Process):
    def __init__(self, ident, mapmatching,  logger = None, matchresults = None, **kwargs):
        print('Fastestrouter.__init__')
        
        # TODO: let this be independent, link to it or child??
        
           
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Fastest Router', 
                            logger = logger,
                            info ='Fastest path router.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.matchresults = matchresults
        

                            
        self.c_modespecific = attrsman.add(cm.AttrConf( 'c_modespecific',kwargs.get('c_modespecific',0.9),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode constant', 
                            info = 'The Mode constant is multiplied with the the distance of each edge if vehicle has exclusive access.',
                            ))
                            
        self.time_modespecific = attrsman.add(cm.AttrConf( 'time_modespecific',kwargs.get('time_modespecific',0.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode time', 
                            unit = 's',
                            info = 'This mode specific time is multiplied with the access-level and subtracted from the edge distance in case the edge is exclusive for the mode of the matched trip.',
                            ))
                            
        self.dist_min_modespecific = attrsman.add(cm.AttrConf( 'dist_min_modespecific',kwargs.get('dist_min_modespecific',15.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Mode dist min', 
                            unit = 'm',
                            info = 'Minimum edge distance for which mode specific modifications take place. This is to revent that a sum of very short edges with exclusive access causes the route to receive too much cost reduction.',
                            ))
                            
        self.is_ignor_connections = attrsman.add(cm.AttrConf( 'is_ignor_connections',kwargs.get('is_ignor_connections',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Ignore connections', 
                            info = 'Ignore connections means that the vehicle can always make turns into all possible edges, whether they are allowed or not.',
                            ))
        self.color_route = attrsman.add(cm.AttrConf(  'color_route', kwargs.get('color_route',COLOR_FASTEST_ROUTE.copy()),
                                        groupnames = ['options'],
                                        perm='wr', 
                                        metatype = 'color',
                                        name = 'Route color', 
                                        info = 'Color of fastest routes used in various visualization tools.',
                                        ))
        
        if (self.matchresults is not None):
            if hasattr(self.matchresults,'nodesresults'):
                self.is_use_nodeswaittimes_est = attrsman.add(cm.AttrConf( 'is_use_nodeswaittimes_est',kwargs.get('is_use_nodeswaittimes_est',True),
                                    groupnames = ['options'], 
                                    perm='rw', 
                                    name = 'Use estimated waittime at nodes', 
                                    info = 'If True, the average waittime at different node types are used.',
                                    #is_enabled = lambda self: self.nodesresults is not None,
                                    ))
            else:
                self.is_use_nodeswaittimes_est = False
        else:
            self.is_use_nodeswaittimes_est = False
                                                             
    def do(self):
        mapmatching = self.parent
        trips = mapmatching.trips
        print('Shortestrouter.do is_use_nodeswaittimes_est',self.is_use_nodeswaittimes_est,(self.matchresults is not None))
        # links
        
        # map_nodetype_to_times_wait = get_map_nodetype_to_times_wait
        if self.is_use_nodeswaittimes_est & (self.matchresults is not None):
            trips.route_fastest_with_waits(   time_modespecific = self.time_modespecific, 
                                    c_modespecific = self.c_modespecific, 
                                    is_ignor_connections = self.is_ignor_connections,
                                    times_wait_nodes = self.matchresults.nodesresults.get_times_wait_est(),
                                    speeds_in_motion = self.matchresults.routesresults_matched.get_speeds_inmotion_est(),
                                    dist_min_modespecific = self.dist_min_modespecific,
                                    color_route = self.color_route,
                                    )
        else:
            trips.route_fastest(   time_modespecific = self.time_modespecific, 
                                    c_modespecific = self.c_modespecific, 
                                    is_ignor_connections = self.is_ignor_connections,
                                    color_route = self.color_route,
                                    )
        return True                  
 
class AlternativeRoutesresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Alternative route results', 
                             info = 'Table with results from analysis of each matched route.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        

        self.add_col(am.IdsArrayConf( 'ids_trip', parent.parent.trips, 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'ID trip', 
                                            info = 'ID of GPS trip.',
                                            ))                                            
        
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
        self.add_col(am.ArrayConf('counter', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'Counter',
                                    symbol = 'Count',
                                    info = 'Counter of samples.',
                                    ))
                                    
        self.add_col(am.ArrayConf('ids_alt', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'Alternative ID',
                                    symbol = 'Alt. ID',
                                    info = 'ID of alternative route.',
                                    ))
                                    
        
        self.add_col(am.ArrayConf('ids_set', default = 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'Choice set ID',
                                    symbol = 'Set ID',
                                    info = 'Length of the route.',
                                    ))
        
        self.add_col(am.ArrayConf('choices', default = False,
                                    dtype = np.bool_,
                                    groupnames = ['results'], 
                                    name = 'Choice',
                                    info = 'True if this is alternative has been chosen.',
                                    ))
                                                                                                    
        self.add_col(am.ArrayConf('distances', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'distance',
                                    unit = 'm',
                                    info = 'Length of the route.',
                                    ))
                                    
        
        self.add_col(am.ArrayConf('lengths_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length mixed access',
                                    symbol = 'L mix',
                                    unit = 'm',
                                    info = 'Length of roads with mixed access.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length exclusive access',
                                    symbol = 'L excl',
                                    unit = 'm',
                                    info = 'Length of roads with exclusive access.',
                                    ))
        
        self.add_col(am.ArrayConf('lengths_low_priority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Length low priority',
                                    symbol = 'L lowprio',
                                    unit = 'm',
                                    info = 'Length of low priority roads. These are roads with either speed limit to 30 or below, or classified as residential, or exclusive bike or pedestrian ways. This correspond to SUMO priorities 1-6.',
                                    ))
                                      
        self.add_col(am.ArrayConf('numbers_nodes', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'number of nodes',
                                    symbol = 'N nodes',
                                    info = 'Total number of nodes.',
                                    ))
                                                       
        self.add_col(am.ArrayConf('ids_gender', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results','person'], 
                                    choices = GENDERS,
                                    name = 'Gender',
                                    info = 'Gender of person.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('years_birth', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['results','person'], 
                                    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 = ['results','person'], 
                                    name = 'occupation',
                                    info = 'Tupe of occupation',
                                    ))       
                                    
        self.add_col(am.ArrayConf('are_frequent_user', False,
                                    dtype = np.bool_,
                                    groupnames = ['results','person'], 
                                    name = 'frequent user',
                                    info = 'If true, this person is a frequent user of the recorded transport mode.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('speeds_av_gps', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results','person'], 
                                    name = 'Tot. GPS time',
                                    symbol = 'V avg GPS',
                                    unit = 'm/s',
                                    info = 'Average speed of recorded GPS traces.',
                                    ))
                                    
        self.add_col(am.IdlistsArrayConf( 'ids_edges', self.parent.get_scenario().net.edges,
                                        name = 'Edge IDs',
                                        groupnames = ['results','_private'], 
                                        info = 'List of edge IDs constituting this route. Needed to visualize alternative routes on plots',
                                        ))
                                                                                     
class AlternativeRoutesanalyzer(Process):
    def __init__(self, ident, mapmatching, results = None,  logger = None, **kwargs):
        print('AlternativeRoutesanalyzer.__init__')
        
        # TODO: let this be independent, link to it or child??
        
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
            
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Alternative Routes Analyzer', 
                            logger = logger,
                            info ="""Secetions all routes in overlapping and 
                            non-overlapping sequences and finds alternative routes to those sequences,
                            either by analyzing other cyclists or by generating close alternative routes. 
                            Determines attributes of matched route and alternative routes.
                            For this analyses the shortest path routing is required.
                            """
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.n_edge_min = attrsman.add(cm.AttrConf( 'n_edge_min',kwargs.get('n_edge_min',3),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. edge number', 
                            info = 'Minimum number of route edges to be considered.',
                            ))
                            
        self.dist_alt_min = attrsman.add(cm.AttrConf( 'dist_alt_min',kwargs.get('dist_alt_min',100.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. alt. distance', 
                            unit = 'm',
                            info = 'Minimum distance of routes in set of alternative routes.',
                            ))
        
        
        self.dist_diff_max = attrsman.add(cm.AttrConf( 'dist_diff_max',kwargs.get('dist_diff_max',3000.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. difference distance', 
                            unit = 'm',
                            info = 'Maximum absolute difference in distance between the shortest and the longes route within a set of alternative routes cannot be larger than this distance.',
                            ))
        
        self.fact_diff_max = attrsman.add(cm.AttrConf( 'fact_diff_max',kwargs.get('fact_diff_max',2.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. difference factor', 
                            unit = 'm',
                            info = 'Maximum difference in distance between the longes and the shortest route within a set of alternative routes cannot be larger than this factor.',
                            ))
                                                
        self.priority_max_low = attrsman.add(cm.AttrConf( 'priority_max_low',kwargs.get('priority_max_low',6),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. low priority', 
                            info = 'Maximum priority of an edge, such that it is still considered low priority.',
                            ))
        
        self.is_save_routes = attrsman.add(cm.AttrConf( 'is_save_routes',kwargs.get('is_save_routes',True),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Save routes', 
                            info = 'When actives saves all alternative routes in results database.',
                            ))
                                                
        #self.timeloss_intersection = attrsman.add(cm.AttrConf( 'timeloss_intersection',kwargs.get('timeloss_intersecion',5.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Timeloss intersection', 
        #                    info = 'Estimated timeloss at intersections due to waiting or reduction of speed. This value is used to estimate travel time of route alternatives.',
        #                    ))
        #
        #self.timeloss_tl = attrsman.add(cm.AttrConf( 'timeloss_tl',kwargs.get('timeloss_tl',15.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Timeloss TL', 
        #                    info = 'Additional estimated timeloss at traffic light intersections due to waiting at the red light. This value is used to estimate travel time of route alternatives.',
        #                    ))
                                                
        results = self.get_results()
        results.config(AlternativeRoutesresults('altroutesresults',results),name = 'Results of alternative routes')
        
        
        
                            
    def get_results(self):
        return   self._results 
    
    def do(self):
        print('AlternativeRoutesanalyzer.do')
        # results
        results = self.get_results()
        altroutesresults = results.altroutesresults
        self.altroutesresults = results.altroutesresults
        self.altroutesresults.clear()
        #routesresults_fastest = results.routesresults_fastest
        #routesresults_matched = results.routesresults_matched
        
        
        
        
        # links
        mapmatching = self.parent
        trips = mapmatching.trips
        
        routes = trips.get_routes()
        scenario = mapmatching.get_scenario()
        edges = scenario.net.edges
        fstar = edges.get_fstar(is_return_arrays = True, is_ignor_connections = True)
        
        
        ###
        lanes = scenario.net.lanes
        nodes = scenario.net.nodes
        id_mode_ped = scenario.net.modes.get_id_mode('pedestrian')
        id_mode_ped = scenario.net.modes.get_id_mode('bicycle')
        #get_pos_from_coord = edges.get_pos_from_coord
        
        vtypes = scenario.demand.vtypes
        #points = mapmatching.points
        #timestamps = points.timestamps
        
        priority_max_low = self.priority_max_low
        #timeloss_intersection = self.timeloss_intersection
        #timeloss_tl = self.timeloss_tl
        
        distancesmap = mapmatching.get_distancesmap(is_check_lanes = False)
        
        
        
        #ids_tonode = edges.ids_tonode
        #nodetypes = scenario.net.nodes.types
        #tlstype = nodetypes.choices['traffic_light']
        #ids_tls = nodes.ids_tls
        
        # databases for add_alternative function
        self._distancesmap = distancesmap
        self._accesslevelsmap = mapmatching.get_accesslevelsmap()
        self._priorities = edges.priorities
        self._edges = edges
        self._persons = mapmatching.persons
        self._nodes = nodes
        self._trips = trips
        #
        
        ids_trip_sel = trips.get_ids_selected()
        
        ids_route_sel = trips.ids_route_matched[ids_trip_sel]
        inds_valid = np.flatnonzero(ids_route_sel>0)
        
        ids_route = ids_route_sel[inds_valid]
        ids_trip = ids_trip_sel[inds_valid]
        ids_route_shortest = trips.ids_route_shortest[ids_trip]
        ids_route_fastest = trips.ids_route_fastest[ids_trip]
        
        ids_vtype = trips.ids_vtype[ids_trip]
        ids_mode = vtypes.ids_mode[ids_vtype]
        
        
        #ind = 0
        if len(ids_trip)==0:
            print('WARNING: no trips selected.')
            return True
        
        print('  analyzing %d trips'%len(ids_trip))
        
        
        #ids_res = routesresults_matched.add_rows(n=len(ids_trip),)
        
        
        routesets_nonoverlap = []
        routesets_overlap = []
        routesets_nonoverlap_dists = []
        routesets_overlap_dists = []
        ids_trip_routesets_nonoverlap = []
        ids_trip_routesets_overlap = []
        ids_mode_routesets_nonoverlap = []
        ids_mode_routesets_overlap = []
        for id_trip, ids_edge_matched, ids_edge_shortest, ids_edge_fastest, id_mode\
                in zip( ids_trip, 
                        routes.ids_edges[ids_route], 
                        routes.ids_edges[ids_route_shortest], 
                        routes.ids_edges[ids_route_fastest],
                        ids_mode, 
                        ):
            print(79*'*')
            print('  id_trip',id_trip)
            
            # edge weights for routing, which is mode specific
            # because non accessible edges are marked with weight = -1 
            weights = distancesmap[id_mode]
            
            #print '    d_matched',np.sum(weights[ids_edge_matched]),'ids_edge_matched',ids_edge_matched
            #print '    dd_shortest',np.sum(weights[ids_edge_shortest]),'ids_edge_shortest',ids_edge_shortest
            
            
            #ids_edge_match_nonoverlap = np.array(list(set(ids_edge_matched).difference(ids_edge_shortest)),dtype = np.int32)
            #ids_edge_match_nonoverl = list(set(ids_edge_matched).difference(ids_edge_shortest))
            #ids_edge_match_nonoverl_next = []
            
            #for id_edge in ids_edge_match_nonoverl:
                #ind = ids_edge_shortest.index(id_edge)
                #if 
            #for id_edge_matched, is_edge_shortest in zip(ids_edge_matched, ids_edge_shortest):
            #    if id_edge_matched not in ids_edge_shortest
            n_edge_matched = len(ids_edge_matched)
            n_edge_shortest= len(ids_edge_shortest)
            if (n_edge_matched>=self.n_edge_min)\
               &(n_edge_shortest>self.n_edge_min)\
               &(n_edge_matched == len(set(ids_edge_matched))): # no loops?
                ind_matched = 0
                ind_shortest = 0
                
                route_ol = [] 
                while ind_matched < n_edge_matched:
                    id_edge_matched = ids_edge_matched[ind_matched]
                    id_edge_shortest = ids_edge_shortest[ind_shortest]
                    #print '    ind_matched',ind_matched,'id_edge_matched',id_edge_matched,'id_edge_shortest',id_edge_shortest
                    
                    if id_edge_matched != id_edge_shortest:
                        
                        dist = np.sum(weights[route_ol])
                        print('     overlapped route len',len(route_ol),'d',dist)
                        if (len(route_ol) >= self.n_edge_min) & (dist > self.dist_alt_min):
                            #print '       store and finish route_ol',route_ol
                            routesets_overlap.append([route_ol,])
                            routesets_overlap_dists.append([dist,])
                            ids_trip_routesets_overlap.append(id_trip)
                            ids_mode_routesets_overlap.append(id_mode)
                            #print '   routesets_overlap pos',routesets_overlap
                        route_ol = []
                        
                        # initialize non-overlap section with last common edge
                        route_matched_nol = [ids_edge_matched[ind_matched-1]]
                        route_shortest_nol = [ids_edge_shortest[ind_shortest-1]]
                        
                        # determine  non-overlap section on matched route
                        while id_edge_matched not in ids_edge_shortest:
                            #print '      append to matched',id_edge_matched,ind_matched
                            route_matched_nol.append(id_edge_matched)
                            ind_matched +=1
                            id_edge_matched = ids_edge_matched[ind_matched]
                        #print '      append to matched-|',id_edge_matched,ind_matched
                        route_matched_nol.append(id_edge_matched)# last edge ovelaps
                        
                        # determine  non-overlap section on shortest route    
                        while id_edge_shortest not in ids_edge_matched:
                            #print '      append to shortest',id_edge_shortest,ind_shortest
                            route_shortest_nol.append(id_edge_shortest)
                            ind_shortest +=1
                            id_edge_shortest = ids_edge_shortest[ind_shortest] 
                        #print '      append to shortest-|',id_edge_shortest,ind_shortest
                        route_shortest_nol.append(id_edge_shortest)# last edge ovelaps
                        
                        # collect data of matched and shortest route section
                        dist_matched = np.sum(weights[route_matched_nol])
                        dist_shortest = np.sum(weights[route_shortest_nol])
                        print('     nonoverlapped route len',len(route_shortest_nol),'dm',dist_matched,'ds',dist_shortest,'dd',dist_matched-dist_shortest)
                        if (len(route_matched_nol)>=self.n_edge_min)\
                           &(len(route_shortest_nol)>=self.n_edge_min)\
                           &(dist_matched > self.dist_alt_min)\
                           &(dist_shortest > self.dist_alt_min)\
                           &(dist_matched-dist_shortest < self.dist_diff_max)\
                           &(dist_matched/dist_shortest < self.fact_diff_max):
                            #print
                            #print '       store and finish route_matched_nol',route_matched_nol
                            #print '       store and finish route_shortest_nol',route_shortest_nol
                            routesets_nonoverlap.append([route_matched_nol, route_shortest_nol])
                            routesets_nonoverlap_dists.append([dist_matched, dist_shortest])
                            ids_trip_routesets_nonoverlap.append(id_trip)
                            ids_mode_routesets_nonoverlap.append(id_mode)
                            #print '   routesets_nonoverlap pos',routesets_nonoverlap
                    else:
                         # common edge
                         route_ol.append(id_edge_matched)
                            
                    ind_matched +=1
                    ind_shortest += 1
        #print '\n  routesets_nonoverlap',routesets_nonoverlap
        #print '\n  routesets_overlap',routesets_overlap
        
        ## Do edge statistics for non-overlapping
        if hasattr(results, 'edgesresults'):
            #  edgesresults must exist
            edgesresults = results.edgesresults
            
            # map ids_edge to ids_edge_res
            ids_edgeresmap = edgesresults.get_edgeresmap()
            
            differences_dist_tot_shortest = edgesresults.differences_dist_tot_shortest
            ids_edgeres = edgesresults.get_ids()
            counter = np.zeros(max(ids_edgeres)+1,dtype = np.int32)
            #nodesresults = results.nodesresults
            for  routeset_nonoverlap, routeset_nonoverlap_dists in zip(routesets_nonoverlap, routesets_nonoverlap_dists):
                #print '  routeset_nonoverlap',routeset_nonoverlap
                #print '  routeset_nonoverlap_dists',routeset_nonoverlap_dists
                if len(routeset_nonoverlap)>1:
                    
                    ids_edge_matched, ids_edge_shortest = routeset_nonoverlap[:2]
                    #print '  routeset_overlap_dists',routeset_nonoverlap_dists[:2],len(routeset_nonoverlap_dists[:2])
                    #dist_matched = routeset_overlap_dists[0]
                    #dist_shortest = routeset_overlap_dists[1]
                    dist_matched, dist_shortest = routeset_nonoverlap_dists[:2]
                    ids_edgeres_shortest = ids_edgeresmap[ids_edge_shortest]
                    differences_dist_tot_shortest[ids_edgeres_shortest] += dist_matched-dist_shortest
                    counter[ids_edgeres_shortest] += 1
           
            edgesresults.differences_dist_rel_shortest[ids_edgeres] =  differences_dist_tot_shortest[ids_edgeres]/counter[ids_edgeres]
        
        
        
        print('  create shortest routes around overlapping routes',len(routesets_overlap))
        for routeset_overlap, routeset_overlap_dists  in zip(routesets_overlap, routesets_overlap_dists):
            # routesets_overlap has initially the onlu selected route
            ids_edge = routeset_overlap[0]
            dist_matched = routeset_overlap_dists[0]
            #print '    dist_matched',dist_matched,'ids_edge',ids_edge
            
            # save precious edge weights and delete weights of this route
            weights_unchanged = deepcopy(weights[ids_edge])
            
            weights[ids_edge[1:-1]] = -1
            
            # create alternative by routing with shortest path from 
            # first to last edge
            costs, routes = routing.get_mincostroute_edge2edges(ids_edge[0], [ids_edge[-1]], 
                                                        weights = weights, fstar = fstar)
            
            
            
            # restore old weights
            weights[ids_edge]= weights_unchanged
            
            if len(routes)==1:
                dist = np.sum(weights[routes[0]])
                
                if (np.abs(dist-dist_matched) < self.dist_diff_max)\
                   & (dist/dist_matched < self.fact_diff_max)\
                   & (len(routes[0]) >= self.n_edge_min):
                    #print '   dist_created',dist
                    routeset_overlap.append(routes[0])
                    routeset_overlap_dists.append(dist)
            
        
        if 0:
            print(79*'=')  
            n_overl = 0  
            for routeset_nonoverlap, routeset_nonoverlap_dists  in zip(routesets_nonoverlap,routesets_nonoverlap_dists):
                print()
                dist_min = np.min(routeset_nonoverlap_dists)
                if len(routeset_nonoverlap)>0:
                    n_overl += 1
                    for ids_edge, dist in zip(routeset_nonoverlap, routeset_nonoverlap_dists):
                        print('    nonoverl d=%dfm, delta=%dm'%(dist,dist-dist_min),dist)#,'ids_edge',ids_edge
                
            print('  len(routesets_nonoverlap)',len(routesets_nonoverlap),len(ids_trip_routesets_overlap),len(routesets_overlap_dists))
            
            
        if 0:
            print(79*'=')  
            n_overl = 0  
            for routeset_overlap, routeset_overlap_dists  in zip(routesets_overlap,routesets_overlap_dists):
                print()
                dist_min = np.min(routeset_overlap_dists)
                if len(routeset_overlap)>0:
                    n_overl += 1
                    for ids_edge, dist in zip(routeset_overlap, routeset_overlap_dists):
                        print('    overl d=%dfm, delta=%dm'%(dist,dist-dist_min),'ids_edge',ids_edge)
                
            
            print('  len(routesets_overlap)',n_overl,len(ids_trip_routesets_nonoverlap),len(routesets_nonoverlap_dists))
        
        self._samplecount = 0
        self._id_alt_last = 10**8
        self._id_set = 0
        #print 79*'*'  
        #print '  ids_trip_routesets_nonoverlap',ids_trip_routesets_nonoverlap
        #print '  routesets_nonoverlap',routesets_nonoverlap
        #print '  routesets_nonoverlap_dists',routesets_nonoverlap_dists
        
        print(79*'*') 
        print('  create alternative routes database')
        
        # do all non-overlap routes
        for id_trip, routeset_nonoverlap, routeset_nonoverlap_dists, id_mode  in zip(ids_trip_routesets_nonoverlap, routesets_nonoverlap,routesets_nonoverlap_dists,ids_mode_routesets_nonoverlap):
            print(79*'-')
            print('  id_trip NOL',id_trip,len(routeset_overlap))
            #print '    routeset_nonoverlap',routeset_nonoverlap
            #print '    dists',routeset_nonoverlap_dists
            if len(routeset_nonoverlap) >= 2:
                # add chosen, matched route
                self.add_alternative(id_trip,np.array(routeset_nonoverlap[0], dtype=np.int32),1, True, routeset_nonoverlap_dists[0], id_mode)
                
                # add shortest path, not chosen route
                self.add_alternative(id_trip,np.array(routeset_nonoverlap[1], dtype=np.int32),2, False, routeset_nonoverlap_dists[1], id_mode)
        
        # do all overlap and generated routes
        for id_trip, routeset_overlap, routeset_overlap_dists, id_mode  in zip(ids_trip_routesets_overlap, routesets_overlap,routesets_overlap_dists, ids_mode_routesets_overlap):
            print(79*'-')
            print('  id_trip OL',id_trip,len(routeset_overlap))
            if len(routeset_overlap) >= 2:
                # add chosen, matched and overlapping route
                self.add_alternative(id_trip,np.array(routeset_overlap[0], dtype=np.int32),1, True, routeset_overlap_dists[0], id_mode)
                
                # add the generated, not chosen route  alternative
                self.add_alternative(id_trip,np.array(routeset_overlap[1], dtype=np.int32),2, False, routeset_overlap_dists[1], id_mode)    
        return True
    
    def add_alternative(self,id_trip, ids_edge_all, id_alt, is_selected, dist, id_mode):
        
        self._samplecount += 1
        if self._id_alt_last > id_alt:
            self._id_set += 1
        
        ids_edge = ids_edge_all[1:-1]
        #self._distancesmap = distancesmap
        #self._accesslevelsmap = mapmatching.get_accesslevelsmap()
        #self._priorities = edges.priorities
        edgelengths = self._distancesmap[id_mode]
        accesses = self._accesslevelsmap[id_mode]
        priorities = self._edges.priorities
        persons = self._persons
        
        trips = self._trips
        id_person = trips.ids_person[id_trip]
        #print '  accesses[ids_edge]',accesses[ids_edge]
        #print '  accesses[ids_edge]==1',accesses[ids_edge]==1
        #print '  ids_edge[accesses[ids_edge]==1]',ids_edge[accesses[ids_edge]==1]
        #print '  edgelengths[ids_edge[accesses[ids_edge]==1]]',edgelengths[ids_edge[accesses[ids_edge]==1]]
        
        print('add_alternative',id_trip,id_alt,is_selected, 'dist',dist,np.sum(edgelengths[ids_edge]),'excl',np.sum(edgelengths[ids_edge[accesses[ids_edge]==2]]))
        id_res = self.altroutesresults.add_row(\
                                  ids_trip = id_trip,
                                  counter = self._samplecount,
                                  ids_alt =  id_alt,
                                  ids_set = self._id_set,
                                  choices = is_selected,
                                  distances = np.sum(edgelengths[ids_edge]),
                                  lengths_mixed = np.sum(edgelengths[ids_edge[accesses[ids_edge]==1]]),
                                  lengths_exclusive = np.sum(edgelengths[ids_edge[accesses[ids_edge]==2]]),
                                  lengths_low_priority = np.sum(edgelengths[ids_edge[priorities[ids_edge]<self.priority_max_low]]),
                                  numbers_nodes = len(ids_edge),
                                  ids_gender = persons.ids_gender[id_person],
                                  years_birth = persons.years_birth[id_person],
                                  speeds_av_gps = persons.speeds_av_gps[id_person],
                                  )
        if self.is_save_routes:
            self.altroutesresults.ids_edges[id_res] = list(ids_edge_all)
        self._id_alt_last = id_alt
        
        return id_res
                    
class CyclistsDatabase(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Cyclists database results', 
                             info = 'Table with elaborated persons results',
                             **kwargs):
    
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        
##        self.add_col(SumoIdsConf('User', xmltag = 'id'))
        
        self.add_col(am.ArrayConf('ids_person', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Person',
                                    info = 'Person ID.',
                                    ))                                          
        
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
                                                
    
        self.add_col(am.ArrayConf('ids_gender', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    choices = GENDERS,
                                    name = 'Gender',
                                    info = 'Gender of person.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('age', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Age',
                                    info = 'Age of the user.',
                                    ))                                        
        
##            self.add_col(am.ArrayConf('ids_occupation', default = OCCUPATIONS['unknown'],
##                                        dtype = np.int32,
##                                        choices = OCCUPATIONS,
##                                        groupnames = ['parameters'], 
##                                        name = 'occupation',
##                                        info = 'Tupe of occupation',
##                                        ))       
                                    
        self.add_col(am.ArrayConf('are_frequent_user', -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Frequent user',
                                    info = 'If true, this person is a frequent user of the recorded transport mode.',
                                    )) 
                                    
##            self.add_col(am.ArrayConf('numbers_tot_trip_gps', 0,
##                                    dtype = np.int32,
##                                    groupnames = ['results'], 
##                                    name = 'tot. trips',
##                                    symbol = 'N tot GPS',
##                                    info = 'Total number of recorded GPS traces from the person.',
##                                    ))         
                                    
        self.add_col(am.ArrayConf('numbers_tot_trip_mached', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'tot. trips matched',
                                    symbol = 'N tot match',
                                    info = 'Total number of correctly matched GPS traces from the person.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_trips', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn',
                                    symbol = 'N Dyn',
                                    info = 'Number of traces available for the dynamic analysis.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_tls', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn tls nodes',
                                    symbol = 'N Dyn tls',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one tls node.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_left', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn left turn',
                                    symbol = 'N Dyn left',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_right', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn right turn',
                                    symbol = 'N Dyn right',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_crossing', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn crossing',
                                    symbol = 'N Dyn crossing',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_left_tls', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn left turn tls',
                                    symbol = 'N Dyn left tls',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one left turn at tls.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_right_tls', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn right turn tls',
                                    symbol = 'N Dyn right tls',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one right turn at tls.',
                                    ))
                                    
        self.add_col(am.ArrayConf('n_dyn_crossing_tls', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'N Dyn crossing tls',
                                    symbol = 'N Dyn crossing tls',
                                    info = 'Number of traces available for the dynamic analysis that crossed at least one crossing at tls.',
                                    ))
                                    
#-------------------------------------------------------------------------------AV NORMAL AVERAGE REFERRED TO THE CARRIED OUT TRIPS
                                                                        
        
        self.add_col(am.ArrayConf('AV_lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'matched length',
                                    symbol = 'AV L match',
                                    unit = 'm',
                                    info = 'length of matched route.',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('AV_lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'AV L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'AV V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               

        self.add_col(am.ArrayConf('AV_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'AV N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'AV Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in contrary direction path',
                                    symbol = 'AV Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in a contrary-direction bike path',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'AV Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'AV Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'AV Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'AV Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'AV Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'AV Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'AV Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'AV Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'AV Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'AV Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'AV N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    

        self.add_col(am.ArrayConf('AV_shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'AV Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'AV N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'AV Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Contrary-direction links',
                                    symbol = 'AV Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary direction roads in the matched length minus share of contrary direction roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'AV Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'AV Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'AV Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'AV Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'AV Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'AV Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'AV Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'AV Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'AV Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'AV Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('AV_shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'AV N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------Dynamic analysis


        self.add_col(am.ArrayConf('AV_speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'AV V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('AV_waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'AV Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'AV Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('AV_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'AV Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'AV Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'AV Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'AV Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'AV Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'AV Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'AV Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('AV_edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'AV Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'AV Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'AV Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'AV Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'AV Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'AV Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'AV Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'AV Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'AV Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'AV Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('AV_real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'AV Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('AV_real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'AV Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    )) 
#-------------------------------------------------------------------------------WL AVERAGE WEIGHTED WITH LENGTH
                                                                        
        
##        self.add_col(am.ArrayConf('WL_lengths_route_matched', default = -1.0,
##                                    dtype = np.float32,
##                                    groupnames = ['results'], 
##                                    name = 'matched length',
##                                    symbol = 'WL L match',
##                                    unit = 'm',
##                                    info = 'length of matched route.',
##                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('WL_lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'WL L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'WL V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               

        self.add_col(am.ArrayConf('WL_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'WL N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'WL Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in contrary direction path',
                                    symbol = 'WL Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in a contrary-direction bike path',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'WL Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'WL Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'WL Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'WL Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'WL Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'WL Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'WL Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'WL Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'WL Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'WL Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'WL N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    

        self.add_col(am.ArrayConf('WL_shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'WL Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'WL N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'WL Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Contrary-direction links',
                                    symbol = 'WL Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary direction roads in the matched length minus share of contrary direction roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'WL Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'WL Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'WL Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'WL Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'WL Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'WL Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'WL Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'WL Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'WL Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'WL Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('WL_shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'WL N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------Dynamic analysis


        self.add_col(am.ArrayConf('WL_speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'WL V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('WL_waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'WL Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'WL Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('WL_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'WL Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'WL Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'WL Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'WL Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'WL Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'WL Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'WL Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('WL_edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'WL Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'WL Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'WL Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'WL Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'WL Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'WL Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'WL Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'WL Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'WL Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'WL Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('WL_real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'WL Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('WL_real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'WL Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    ))
#-------------------------------------------------------------------------------MD MEDIAN
                                                                        
        
        self.add_col(am.ArrayConf('MD_lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'matched length',
                                    symbol = 'MD L match',
                                    unit = 'm',
                                    info = 'length of matched route.',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('MD_lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'MD L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'MD V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               


        self.add_col(am.ArrayConf('MD_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'MD N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'MD Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in contrary direction path',
                                    symbol = 'MD Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in a contrary-direction bike path',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'MD Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'MD Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'MD Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'MD Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'MD Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'MD Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'MD Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'MD Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'MD Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'MD Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'MD N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    

        self.add_col(am.ArrayConf('MD_shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'MD Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'MD N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'MD Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Contrary-direction links',
                                    symbol = 'MD Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary direction roads in the matched length minus share of contrary direction roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'MD Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'MD Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'MD Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'MD Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'MD Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'MD Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'MD Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'MD Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'MD Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'MD Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('MD_shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'MD N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------Dynamic analysis


        self.add_col(am.ArrayConf('MD_speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'MD V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('MD_waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'MD Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'MD Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('MD_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'MD Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'MD Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'MD Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'MD Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'MD Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'MD Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'MD Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('MD_edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'MD Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'MD Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'MD Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'MD Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'MD Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'MD Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'MD Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'MD Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'MD Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'MD Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('MD_real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'MD Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MD_real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'MD Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    ))
#-------------------------------------------------------------------------------SD STANDARD DEVIATION
                                                                        
        
        self.add_col(am.ArrayConf('SD_lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'matched length',
                                    symbol = 'SD L match',
                                    unit = 'm',
                                    info = 'length of matched route.',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('SD_lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'SD L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'SD V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               

        self.add_col(am.ArrayConf('SD_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'SD N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'SD Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in contrary direction path',
                                    symbol = 'SD Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in a contrary-direction bike path',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'SD Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'SD Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'SD Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'SD Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'SD Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'SD Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'SD Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'SD Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'SD Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'SD Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'SD N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    

        self.add_col(am.ArrayConf('SD_shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'SD Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'SD N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'SD Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Contrary-direction links',
                                    symbol = 'SD Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary direction roads in the matched length minus share of contrary direction roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'SD Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'SD Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'SD Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'SD Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'SD Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'SD Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'SD Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'SD Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'SD Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'SD Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('SD_shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'SD N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------Dynamic analysis


        self.add_col(am.ArrayConf('SD_speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'SD V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('SD_waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'SD Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'SD Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('SD_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'SD Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'SD Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'SD Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'SD Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'SD Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'SD Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'SD Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('SD_edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'SD Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'SD Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'SD Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'SD Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'SD Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'SD Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'SD Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'SD Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'SD Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'SD Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('SD_real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'SD Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('SD_real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'SD Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    ))
#-------------------------------------------------------------------------------MA MEAN ABSOLUTE DIFFERENCE
                                                                        
        
        self.add_col(am.ArrayConf('MA_lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'matched length',
                                    symbol = 'MA L match',
                                    unit = 'm',
                                    info = 'length of matched route.',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('MA_lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'MA L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'MA V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               

        self.add_col(am.ArrayConf('MA_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'MA N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('MA_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'MA Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in contrary direction path',
                                    symbol = 'MA Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in a contrary-direction bike path',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'MA Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'MA Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'MA Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'MA Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'MA Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'MA Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'MA Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'MA Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'MA Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'MA Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'MA N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    

        self.add_col(am.ArrayConf('MA_shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'MA Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'MA N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'MA Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Contrary-direction links',
                                    symbol = 'MA Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary direction roads in the matched length minus share of contrary direction roads in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'MA Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'MA Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'MA Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'MA Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'MA Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'MA Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'MA Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'MA Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'MA Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'MA Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('MA_shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'MA N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------Dynamic analysis


        self.add_col(am.ArrayConf('MA_speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'MA V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('MA_waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'MA Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'MA Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('MA_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'MA Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'MA Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'MA Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'MA Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'MA Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'MA Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'MA Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('MA_edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'MA Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'MA Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'MA Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'MA Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'MA Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'MA Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'MA Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'MA Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'MA Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'MA Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('MA_real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'MA Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('MA_real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'MA Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    ))
                                    
class CyclistsDatabaseAnalyzer(Process):
    def __init__(self, ident, mapmatching, results = None, logger = None, **kwargs):
##        self._init_objman(  ident='trips_database_analyzer', parent=parent, 
##                            name = 'Trips database analyzer',
##                            version = 0.1,
##                            **kwargs)
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
            
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Cyclists Database Analyzer', 
                            logger = logger,
                            info =""" Elaborated analysis of the GPS persons
                            """
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2017),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Year dataset', 
                            info = 'Year of the dataset, fo evaluating the age of the users.',
                            ))
        
                     
        results = self.get_results()  
        results.config(CyclistsDatabase('cyclistsdatabase',results),name = 'Results of cyclists')    
        
        
    def get_results(self):
        return   self._results 
                                    
    def do(self):
            
            

        results = self.get_results()
        persons = self.parent.persons
##            results.config(Personsresults('personsresults',results),name = 'Results of persons')
        tripsdatabase = results.tripsdatabase
        cyclistsdatabase = results.cyclistsdatabase
        self.cyclistsdatabase = results.cyclistsdatabase
        self.cyclistsdatabase.clear()
        ids_tripsdatabase = tripsdatabase.get_ids()
        ids_person = np.unique(tripsdatabase.ids_person[ids_tripsdatabase])
        ids_person = ids_person[(ids_person>0)]
##        users = persons.ids_sumo[ids_person]
##            personsresults = results.personsresults
##            ids_person = personsresults.get_ids()
##            ids_route = np.zeros(len(ids_person))
        ids_gender = persons.ids_gender[ids_person]
        age = self.year-persons.years_birth[ids_person]
        are_frequent_user = persons.are_frequent_user[ids_person] 
        numbers_tot_trip_mached = persons.numbers_tot_trip_mached[ids_person] 

        n_dynaanalysis = np.zeros(len(ids_person))
        n_dyn_tls = np.zeros(len(ids_person))
        n_dyn_left = np.zeros(len(ids_person))
        n_dyn_right = np.zeros(len(ids_person))
        n_dyn_crossing = np.zeros(len(ids_person))
        n_dyn_left_tls = np.zeros(len(ids_person))
        n_dyn_right_tls = np.zeros(len(ids_person))
        n_dyn_crossing_tls = np.zeros(len(ids_person))
        AV_lengths_route_matched = np.zeros(len(ids_person))
        AV_lengths_within_center = np.zeros(len(ids_person))
        AV_speeds_av_matched = np.zeros(len(ids_person))
        AV_numbers_prioritychange = np.zeros(len(ids_person))
        AV_matched_exclusive = np.zeros(len(ids_person))
        AV_matched_contrary = np.zeros(len(ids_person))
        AV_matched_mixed = np.zeros(len(ids_person))
        AV_matched_lowpriority = np.zeros(len(ids_person))
        AV_matched_nodes = np.zeros(len(ids_person))
        AV_matched_left_turns = np.zeros(len(ids_person))
        AV_matched_right_turns = np.zeros(len(ids_person))
        AV_matched_crossings = np.zeros(len(ids_person))
        AV_matched_tls_nodes = np.zeros(len(ids_person))
        AV_matched_tls_left_turns = np.zeros(len(ids_person))
        AV_matched_tls_right_turns = np.zeros(len(ids_person))
        AV_matched_tls_crossings = np.zeros(len(ids_person))
        AV_av_n_connections_per_node = np.zeros(len(ids_person))
        AV_shortest_length = np.zeros(len(ids_person))
        AV_shortest_vs_matched_numbers_prioritychange = np.zeros(len(ids_person))
        AV_shortest_vs_matched_exclusive = np.zeros(len(ids_person))
        AV_shortest_vs_matched_contrary = np.zeros(len(ids_person))
        AV_shortest_vs_matched_mixed = np.zeros(len(ids_person))
        AV_shortest_vs_matched_lowpriority = np.zeros(len(ids_person))
        AV_shortest_vs_matched_nodes = np.zeros(len(ids_person))
        AV_shortest_vs_matched_left_turns = np.zeros(len(ids_person))
        AV_shortest_vs_matched_right_turns = np.zeros(len(ids_person))
        AV_shortest_vs_matched_crossings = np.zeros(len(ids_person))
        AV_shortest_vs_matched_tls_nodes = np.zeros(len(ids_person))
        AV_shortest_vs_matched_tls_left_turns = np.zeros(len(ids_person))
        AV_shortest_vs_matched_tls_right_turns = np.zeros(len(ids_person))
        AV_shortest_vs_matched_tls_crossings = np.zeros(len(ids_person))
        AV_shortest_vs_matched_av_n_connections_per_node = np.zeros(len(ids_person))
        #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
        AV_speeds_inmotion_av = np.zeros(len(ids_person))
        AV_waiting_times_share = np.zeros(len(ids_person))
        AV_intersections_waiting_times = np.zeros(len(ids_person))
        AV_left_turns_waiting_times = np.zeros(len(ids_person))
        AV_right_turns_waiting_times = np.zeros(len(ids_person))
        AV_crossings_waiting_times = np.zeros(len(ids_person))
        AV_tls_intersections_waiting_times = np.zeros(len(ids_person))
        AV_tls_left_turns_waiting_times = np.zeros(len(ids_person))
        AV_tls_right_turns_waiting_times = np.zeros(len(ids_person))
        AV_tls_crossings_waiting_times = np.zeros(len(ids_person))
        AV_edges_waiting_times_av = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_edges = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_nodes = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_crossings = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_right_turns_tls = np.zeros(len(ids_person))
        AV_real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_person))
        AV_real_vs_expected1_waiting_time = np.zeros(len(ids_person))
        AV_real_vs_expected2_waiting_time = np.zeros(len(ids_person))
        #-------------------------------------------------------------------------------WL AVERAGE WEIGHTED WITH LENGTH
        
##        WL_lengths_route_matched = np.zeros(len(ids_person))
        WL_lengths_within_center = np.zeros(len(ids_person))
        WL_speeds_av_matched = np.zeros(len(ids_person))
        WL_numbers_prioritychange = np.zeros(len(ids_person))
        WL_matched_exclusive = np.zeros(len(ids_person))
        WL_matched_contrary = np.zeros(len(ids_person))
        WL_matched_mixed =  np.zeros(len(ids_person))
        WL_matched_lowpriority = np.zeros(len(ids_person))
        WL_matched_nodes = np.zeros(len(ids_person))
        WL_matched_left_turns = np.zeros(len(ids_person))
        WL_matched_right_turns = np.zeros(len(ids_person))
        WL_matched_crossings = np.zeros(len(ids_person))
        WL_matched_tls_nodes = np.zeros(len(ids_person))
        WL_matched_tls_left_turns = np.zeros(len(ids_person))
        WL_matched_tls_right_turns = np.zeros(len(ids_person))
        WL_matched_tls_crossings = np.zeros(len(ids_person))
        WL_av_n_connections_per_node = np.zeros(len(ids_person))
        WL_shortest_length = np.zeros(len(ids_person))
        WL_shortest_vs_matched_numbers_prioritychange = np.zeros(len(ids_person))
        WL_shortest_vs_matched_exclusive = np.zeros(len(ids_person))
        WL_shortest_vs_matched_contrary = np.zeros(len(ids_person))
        WL_shortest_vs_matched_mixed = np.zeros(len(ids_person))
        WL_shortest_vs_matched_lowpriority = np.zeros(len(ids_person))
        WL_shortest_vs_matched_nodes = np.zeros(len(ids_person))
        WL_shortest_vs_matched_left_turns = np.zeros(len(ids_person))
        WL_shortest_vs_matched_right_turns = np.zeros(len(ids_person))
        WL_shortest_vs_matched_crossings = np.zeros(len(ids_person))
        WL_shortest_vs_matched_tls_nodes = np.zeros(len(ids_person))
        WL_shortest_vs_matched_tls_left_turns = np.zeros(len(ids_person))
        WL_shortest_vs_matched_tls_right_turns = np.zeros(len(ids_person))
        WL_shortest_vs_matched_tls_crossings = np.zeros(len(ids_person))
        WL_shortest_vs_matched_av_n_connections_per_node = np.zeros(len(ids_person))
        #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
        WL_speeds_inmotion_av = np.zeros(len(ids_person))
        WL_waiting_times_share = np.zeros(len(ids_person))
        WL_intersections_waiting_times = np.zeros(len(ids_person))
        WL_left_turns_waiting_times = np.zeros(len(ids_person))
        WL_right_turns_waiting_times  = np.zeros(len(ids_person))
        WL_crossings_waiting_times = np.zeros(len(ids_person))
        WL_tls_intersections_waiting_times = np.zeros(len(ids_person))
        WL_tls_left_turns_waiting_times = np.zeros(len(ids_person))
        WL_tls_right_turns_waiting_times = np.zeros(len(ids_person))
        WL_tls_crossings_waiting_times = np.zeros(len(ids_person))
        WL_edges_waiting_times_av = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_edges = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_nodes = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_crossings = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_right_turns_tls = np.zeros(len(ids_person))
        WL_real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_person))
        WL_real_vs_expected1_waiting_time = np.zeros(len(ids_person))
        WL_real_vs_expected2_waiting_time = np.zeros(len(ids_person))
        #-------------------------------------------------------------------------------MD MEDIAN
        MD_lengths_route_matched = np.zeros(len(ids_person))
        MD_lengths_within_center = np.zeros(len(ids_person))
        MD_speeds_av_matched = np.zeros(len(ids_person))
        MD_numbers_prioritychange = np.zeros(len(ids_person))
        MD_matched_exclusive = np.zeros(len(ids_person))
        MD_matched_contrary = np.zeros(len(ids_person))
        MD_matched_mixed = np.zeros(len(ids_person))
        MD_matched_lowpriority = np.zeros(len(ids_person))
        MD_matched_nodes = np.zeros(len(ids_person))
        MD_matched_left_turns = np.zeros(len(ids_person))
        MD_matched_right_turns = np.zeros(len(ids_person))
        MD_matched_crossings = np.zeros(len(ids_person))
        MD_matched_tls_nodes = np.zeros(len(ids_person))
        MD_matched_tls_left_turns = np.zeros(len(ids_person))
        MD_matched_tls_right_turns = np.zeros(len(ids_person))
        MD_matched_tls_crossings = np.zeros(len(ids_person))
        MD_av_n_connections_per_node = np.zeros(len(ids_person))
        MD_shortest_length = np.zeros(len(ids_person))
        MD_shortest_vs_matched_numbers_prioritychange = np.zeros(len(ids_person))
        MD_shortest_vs_matched_exclusive = np.zeros(len(ids_person))
        MD_shortest_vs_matched_contrary = np.zeros(len(ids_person))
        MD_shortest_vs_matched_mixed = np.zeros(len(ids_person))
        MD_shortest_vs_matched_lowpriority = np.zeros(len(ids_person))
        MD_shortest_vs_matched_nodes = np.zeros(len(ids_person))
        MD_shortest_vs_matched_left_turns = np.zeros(len(ids_person))
        MD_shortest_vs_matched_right_turns = np.zeros(len(ids_person))
        MD_shortest_vs_matched_crossings = np.zeros(len(ids_person))
        MD_shortest_vs_matched_tls_nodes = np.zeros(len(ids_person))
        MD_shortest_vs_matched_tls_left_turns = np.zeros(len(ids_person))
        MD_shortest_vs_matched_tls_right_turns = np.zeros(len(ids_person))
        MD_shortest_vs_matched_tls_crossings = np.zeros(len(ids_person))
        MD_shortest_vs_matched_av_n_connections_per_node = np.zeros(len(ids_person)) 
        #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
        MD_speeds_inmotion_av = np.zeros(len(ids_person))
        MD_waiting_times_share = np.zeros(len(ids_person))
        MD_intersections_waiting_times = np.zeros(len(ids_person))
        MD_left_turns_waiting_times = np.zeros(len(ids_person))
        MD_right_turns_waiting_times = np.zeros(len(ids_person))
        MD_crossings_waiting_times = np.zeros(len(ids_person))
        MD_tls_intersections_waiting_times = np.zeros(len(ids_person))
        MD_tls_left_turns_waiting_times = np.zeros(len(ids_person))
        MD_tls_right_turns_waiting_times = np.zeros(len(ids_person))
        MD_tls_crossings_waiting_times = np.zeros(len(ids_person))
        MD_edges_waiting_times_av = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_edges = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_nodes = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_crossings = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_right_turns_tls = np.zeros(len(ids_person))
        MD_real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_person))
        MD_real_vs_expected1_waiting_time = np.zeros(len(ids_person))
        MD_real_vs_expected2_waiting_time = np.zeros(len(ids_person))
        #-------------------------------------------------------------------------------SD STANDARD DEVIATION
        SD_lengths_route_matched = np.zeros(len(ids_person))
        SD_lengths_within_center = np.zeros(len(ids_person))
        SD_speeds_av_matched = np.zeros(len(ids_person))
        SD_numbers_prioritychange = np.zeros(len(ids_person))
        SD_matched_exclusive = np.zeros(len(ids_person))
        SD_matched_contrary = np.zeros(len(ids_person))
        SD_matched_mixed = np.zeros(len(ids_person))
        SD_matched_lowpriority = np.zeros(len(ids_person))
        SD_matched_nodes = np.zeros(len(ids_person))
        SD_matched_left_turns = np.zeros(len(ids_person))
        SD_matched_right_turns = np.zeros(len(ids_person))
        SD_matched_crossings = np.zeros(len(ids_person))
        SD_matched_tls_nodes = np.zeros(len(ids_person))
        SD_matched_tls_left_turns = np.zeros(len(ids_person))
        SD_matched_tls_right_turns = np.zeros(len(ids_person))
        SD_matched_tls_crossings = np.zeros(len(ids_person))
        SD_av_n_connections_per_node = np.zeros(len(ids_person))
        SD_shortest_length = np.zeros(len(ids_person))
        SD_shortest_vs_matched_numbers_prioritychange = np.zeros(len(ids_person))
        SD_shortest_vs_matched_exclusive = np.zeros(len(ids_person))
        SD_shortest_vs_matched_contrary = np.zeros(len(ids_person))
        SD_shortest_vs_matched_mixed = np.zeros(len(ids_person))
        SD_shortest_vs_matched_lowpriority = np.zeros(len(ids_person))
        SD_shortest_vs_matched_nodes = np.zeros(len(ids_person))
        SD_shortest_vs_matched_left_turns = np.zeros(len(ids_person))
        SD_shortest_vs_matched_right_turns = np.zeros(len(ids_person))
        SD_shortest_vs_matched_crossings = np.zeros(len(ids_person))
        SD_shortest_vs_matched_tls_nodes = np.zeros(len(ids_person))
        SD_shortest_vs_matched_tls_left_turns = np.zeros(len(ids_person))
        SD_shortest_vs_matched_tls_right_turns = np.zeros(len(ids_person))
        SD_shortest_vs_matched_tls_crossings = np.zeros(len(ids_person))
        SD_shortest_vs_matched_av_n_connections_per_node = np.zeros(len(ids_person))
        #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
        SD_speeds_inmotion_av = np.zeros(len(ids_person))
        SD_waiting_times_share = np.zeros(len(ids_person))
        SD_intersections_waiting_times = np.zeros(len(ids_person))
        SD_left_turns_waiting_times = np.zeros(len(ids_person))
        SD_right_turns_waiting_times = np.zeros(len(ids_person))
        SD_crossings_waiting_times = np.zeros(len(ids_person))
        SD_tls_intersections_waiting_times = np.zeros(len(ids_person))
        SD_tls_left_turns_waiting_times = np.zeros(len(ids_person))
        SD_tls_right_turns_waiting_times = np.zeros(len(ids_person))
        SD_tls_crossings_waiting_times = np.zeros(len(ids_person))
        SD_edges_waiting_times_av = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_edges = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_nodes = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_crossings = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_right_turns_tls = np.zeros(len(ids_person))
        SD_real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_person))
        SD_real_vs_expected1_waiting_time = np.zeros(len(ids_person))
        SD_real_vs_expected2_waiting_time = np.zeros(len(ids_person))
        #-------------------------------------------------------------------------------MA MEAN ABSOLUTE DIFFERENCE
        MA_lengths_route_matched = np.zeros(len(ids_person))
        MA_lengths_within_center = np.zeros(len(ids_person))
        MA_speeds_av_matched = np.zeros(len(ids_person))
        MA_numbers_prioritychange = np.zeros(len(ids_person))
        MA_matched_exclusive = np.zeros(len(ids_person))
        MA_matched_contrary = np.zeros(len(ids_person))
        MA_matched_mixed = np.zeros(len(ids_person))
        MA_matched_lowpriority = np.zeros(len(ids_person))
        MA_matched_nodes = np.zeros(len(ids_person))
        MA_matched_left_turns = np.zeros(len(ids_person))
        MA_matched_right_turns = np.zeros(len(ids_person))
        MA_matched_crossings = np.zeros(len(ids_person))
        MA_matched_tls_nodes = np.zeros(len(ids_person))
        MA_matched_tls_left_turns = np.zeros(len(ids_person))
        MA_matched_tls_right_turns = np.zeros(len(ids_person))
        MA_matched_tls_crossings = np.zeros(len(ids_person))
        MA_av_n_connections_per_node = np.zeros(len(ids_person))
        MA_shortest_length = np.zeros(len(ids_person))
        MA_shortest_vs_matched_numbers_prioritychange = np.zeros(len(ids_person))
        MA_shortest_vs_matched_exclusive = np.zeros(len(ids_person))
        MA_shortest_vs_matched_contrary = np.zeros(len(ids_person))
        MA_shortest_vs_matched_mixed = np.zeros(len(ids_person))
        MA_shortest_vs_matched_lowpriority = np.zeros(len(ids_person)) 
        MA_shortest_vs_matched_nodes = np.zeros(len(ids_person))
        MA_shortest_vs_matched_left_turns = np.zeros(len(ids_person))
        MA_shortest_vs_matched_right_turns = np.zeros(len(ids_person))
        MA_shortest_vs_matched_crossings = np.zeros(len(ids_person))
        MA_shortest_vs_matched_tls_nodes = np.zeros(len(ids_person))
        MA_shortest_vs_matched_tls_left_turns = np.zeros(len(ids_person))
        MA_shortest_vs_matched_tls_right_turns = np.zeros(len(ids_person))
        MA_shortest_vs_matched_tls_crossings = np.zeros(len(ids_person))
        MA_shortest_vs_matched_av_n_connections_per_node = np.zeros(len(ids_person))
        #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
        MA_speeds_inmotion_av = np.zeros(len(ids_person))
        MA_waiting_times_share = np.zeros(len(ids_person))
        MA_intersections_waiting_times = np.zeros(len(ids_person))
        MA_left_turns_waiting_times = np.zeros(len(ids_person))
        MA_right_turns_waiting_times = np.zeros(len(ids_person))
        MA_crossings_waiting_times = np.zeros(len(ids_person))
        MA_tls_intersections_waiting_times = np.zeros(len(ids_person))
        MA_tls_left_turns_waiting_times = np.zeros(len(ids_person))
        MA_tls_right_turns_waiting_times = np.zeros(len(ids_person))
        MA_tls_crossings_waiting_times = np.zeros(len(ids_person))
        MA_edges_waiting_times_av = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_edges = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_nodes = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_crossings = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_person))
        MA_real_vs_expected_waiting_time_right_turns_tls = np.zeros(len(ids_person)) 
        MA_real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_person))
        MA_real_vs_expected1_waiting_time = np.zeros(len(ids_person))
        MA_real_vs_expected2_waiting_time = np.zeros(len(ids_person))
        
        for id_person, i in zip(ids_person, list(range(len(ids_person)))):
            
            ids_tripsdatabase_pers = ids_tripsdatabase[(tripsdatabase.ids_person[ids_tripsdatabase] == id_person)]
            lengths = tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers]
            total_lengths = np.sum(lengths)
            
            ids_tripsdatabase_dyn = []
            ids_tripsdatabase_left_turn = [] 
            ids_tripsdatabase_right_turn = [] 
            ids_tripsdatabase_crossing = [] 
            ids_tripsdatabase_tls_node = []  
            ids_tripsdatabase_tls_left_turn = []  
            ids_tripsdatabase_tls_right_turn = []
            ids_tripsdatabase_tls_crossing = []
            for id_tripsdatabase_pers in ids_tripsdatabase_pers:
                if tripsdatabase.speeds_inmotion_av[id_tripsdatabase_pers]>0:
                    ids_tripsdatabase_dyn.append(id_tripsdatabase_pers)

            for  id_tripsdatabase_dyn in ids_tripsdatabase_dyn:
                n_dynaanalysis[i] += 1
                if tripsdatabase.matched_left_turns[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_left_turn.append(id_tripsdatabase_dyn)
                    n_dyn_left[i] += 1
                if tripsdatabase.matched_right_turns[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_right_turn.append(id_tripsdatabase_dyn)
                    n_dyn_right[i] += 1
                if tripsdatabase.matched_crossings[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_crossing.append(id_tripsdatabase_dyn)
                    n_dyn_crossing[i] += 1
                if tripsdatabase.matched_tls_nodes[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_tls_node.append(id_tripsdatabase_dyn) 
                    n_dyn_tls[i] += 1 
                if tripsdatabase.matched_tls_left_turns[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_tls_left_turn.append(id_tripsdatabase_dyn)
                    n_dyn_left_tls[i] += 1
                if tripsdatabase.matched_tls_right_turns[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_tls_right_turn.append(id_tripsdatabase_dyn) 
                    n_dyn_right_tls[i] += 1
                if tripsdatabase.matched_tls_crossings[id_tripsdatabase_dyn]>0:
                    ids_tripsdatabase_tls_crossing.append(id_tripsdatabase_dyn)  
                    n_dyn_crossing_tls[i] += 1

            
            #-------------------------------------------------------------------------------AV NORMAL AVERAGE REFERRED TO THE CARRIED OUT TRIPS
            AV_lengths_route_matched[i] = np.mean(tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers])
            AV_lengths_within_center[i] = np.mean(tripsdatabase.lengths_within_center[ids_tripsdatabase_pers])
            AV_speeds_av_matched[i] = np.mean(tripsdatabase.speeds_av_matched[ids_tripsdatabase_pers])
            AV_numbers_prioritychange[i] = np.mean(tripsdatabase.numbers_prioritychange[ids_tripsdatabase_pers])
            AV_matched_exclusive[i] = np.mean(tripsdatabase.matched_exclusive[ids_tripsdatabase_pers])
            AV_matched_contrary[i] = np.mean(tripsdatabase.matched_contrary[ids_tripsdatabase_pers])
            AV_matched_mixed[i] = np.mean(tripsdatabase.matched_mixed[ids_tripsdatabase_pers])
            AV_matched_lowpriority[i] = np.mean(tripsdatabase.matched_lowpriority[ids_tripsdatabase_pers])
            AV_matched_nodes[i] = np.mean(tripsdatabase.matched_nodes[ids_tripsdatabase_pers])
            AV_matched_left_turns[i] = np.mean(tripsdatabase.matched_left_turns[ids_tripsdatabase_pers])
            AV_matched_right_turns[i] = np.mean(tripsdatabase.matched_right_turns[ids_tripsdatabase_pers])
            AV_matched_crossings[i] = np.mean(tripsdatabase.matched_crossings[ids_tripsdatabase_pers])
            AV_matched_tls_nodes[i] = np.mean(tripsdatabase.matched_tls_nodes[ids_tripsdatabase_pers])
            AV_matched_tls_left_turns[i] = np.mean(tripsdatabase.matched_tls_left_turns[ids_tripsdatabase_pers])
            AV_matched_tls_right_turns[i] = np.mean(tripsdatabase.matched_tls_right_turns[ids_tripsdatabase_pers])
            AV_matched_tls_crossings[i] = np.mean(tripsdatabase.matched_tls_crossings[ids_tripsdatabase_pers])
            AV_av_n_connections_per_node[i] = np.mean(tripsdatabase.av_n_connections_per_node[ids_tripsdatabase_pers])
            AV_shortest_length[i] = np.mean(tripsdatabase.shortest_length[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_exclusive[i] = np.mean(tripsdatabase.shortest_vs_matched_exclusive[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_contrary[i] = np.mean(tripsdatabase.shortest_vs_matched_contrary[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_numbers_prioritychange[i] = np.mean(tripsdatabase.shortest_vs_matched_numbers_prioritychange[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_mixed[i] = np.mean(tripsdatabase.shortest_vs_matched_mixed[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_lowpriority[i] = np.mean(tripsdatabase.shortest_vs_matched_lowpriority[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_nodes[i] = np.mean(tripsdatabase.shortest_vs_matched_nodes[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_left_turns[i] = np.mean(tripsdatabase.shortest_vs_matched_left_turns[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_right_turns[i] = np.mean(tripsdatabase.shortest_vs_matched_right_turns[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_crossings[i] = np.mean(tripsdatabase.shortest_vs_matched_crossings[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_tls_nodes[i] = np.mean(tripsdatabase.shortest_vs_matched_tls_nodes[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_tls_left_turns[i] = np.mean(tripsdatabase.shortest_vs_matched_tls_left_turns[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_tls_right_turns[i] = np.mean(tripsdatabase.shortest_vs_matched_tls_right_turns[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_tls_crossings[i] = np.mean(tripsdatabase.shortest_vs_matched_tls_crossings[ids_tripsdatabase_pers])
            AV_shortest_vs_matched_av_n_connections_per_node[i] = np.mean(tripsdatabase.shortest_vs_matched_av_n_connections_per_node[ids_tripsdatabase_pers])
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            

            if ids_tripsdatabase_dyn != []:
                AV_speeds_inmotion_av[i] = np.mean(tripsdatabase.speeds_inmotion_av[ids_tripsdatabase_dyn])
                AV_waiting_times_share[i] = np.mean(tripsdatabase.waiting_times_share[ids_tripsdatabase_dyn])
                AV_intersections_waiting_times[i] = np.mean(tripsdatabase.intersections_waiting_times[ids_tripsdatabase_dyn])
                if ids_tripsdatabase_left_turn != []:
                    AV_left_turns_waiting_times[i] = np.mean(tripsdatabase.left_turns_waiting_times[ids_tripsdatabase_left_turn])
                    AV_real_vs_expected_waiting_time_left_turns[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_left_turns[ids_tripsdatabase_left_turn])
                else:
                    AV_left_turns_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_left_turns[i] = -1
                if ids_tripsdatabase_right_turn != []:
                    AV_right_turns_waiting_times[i] = np.mean(tripsdatabase.right_turns_waiting_times[ids_tripsdatabase_right_turn])
                    AV_real_vs_expected_waiting_time_right_turns[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_right_turns[ids_tripsdatabase_right_turn])
                else:
                    AV_right_turns_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_right_turns[i] = -1
                if ids_tripsdatabase_crossing != []:
                    AV_crossings_waiting_times[i] = np.mean(tripsdatabase.crossings_waiting_times[ids_tripsdatabase_crossing])
                    AV_real_vs_expected_waiting_time_crossings[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_crossings[ids_tripsdatabase_crossing])
                else:
                    AV_crossings_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_crossings[i] = -1
                if ids_tripsdatabase_tls_node != []:
                    AV_tls_intersections_waiting_times[i] = np.mean(tripsdatabase.tls_intersections_waiting_times[ids_tripsdatabase_tls_node])
                    AV_real_vs_expected_waiting_time_tls_nodes[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_tls_nodes[ids_tripsdatabase_tls_node])
                else:
                    AV_tls_intersections_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_tls_nodes[i] = -1
                if ids_tripsdatabase_tls_left_turn != []:
                    AV_tls_left_turns_waiting_times[i] = np.mean(tripsdatabase.tls_left_turns_waiting_times[ids_tripsdatabase_tls_left_turn])
                    AV_real_vs_expected_waiting_time_left_turns_tls[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_left_turns_tls[ids_tripsdatabase_tls_left_turn])
                else:
                    AV_tls_left_turns_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                if ids_tripsdatabase_tls_right_turn != []:
                    AV_tls_right_turns_waiting_times[i] = np.mean(tripsdatabase.tls_right_turns_waiting_times[ids_tripsdatabase_tls_right_turn])
                    AV_real_vs_expected_waiting_time_right_turns_tls[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_right_turns_tls[ids_tripsdatabase_tls_right_turn])
                else:
                    AV_tls_right_turns_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                if ids_tripsdatabase_tls_crossing != []:
                    AV_tls_crossings_waiting_times[i] = np.mean(tripsdatabase.tls_crossings_waiting_times[ids_tripsdatabase_tls_crossing])
                    AV_real_vs_expected_waiting_time_crossings_tls[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_crossings_tls[ids_tripsdatabase_tls_crossing])
                else:
                    AV_tls_crossings_waiting_times[i] = -1
                    AV_real_vs_expected_waiting_time_crossings_tls[i] = -1
                    
                AV_edges_waiting_times_av[i] = np.mean(tripsdatabase.edges_waiting_times_av[ids_tripsdatabase_dyn])
                AV_real_vs_expected_waiting_time_edges[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_edges[ids_tripsdatabase_dyn])
                AV_real_vs_expected_waiting_time_nodes[i] = np.mean(tripsdatabase.real_vs_expected_waiting_time_nodes[ids_tripsdatabase_dyn])
                AV_real_vs_expected1_waiting_time[i] = np.mean(tripsdatabase.real_vs_expected1_waiting_time[ids_tripsdatabase_dyn])
                AV_real_vs_expected2_waiting_time[i] = np.mean(tripsdatabase.real_vs_expected2_waiting_time[ids_tripsdatabase_dyn])
            else:
                AV_speeds_inmotion_av[i] = -1
                AV_waiting_times_share[i] = -1
                AV_intersections_waiting_times[i] = -1
                AV_left_turns_waiting_times[i] = -1
                AV_right_turns_waiting_times[i] = -1
                AV_crossings_waiting_times[i] = -1
                AV_tls_intersections_waiting_times[i] = -1
                AV_tls_left_turns_waiting_times[i] = -1
                AV_tls_right_turns_waiting_times[i] = -1
                AV_tls_crossings_waiting_times[i] = -1
                AV_edges_waiting_times_av[i] = -1
                AV_real_vs_expected_waiting_time_edges[i] = -1
                AV_real_vs_expected_waiting_time_nodes[i] = -1
                AV_real_vs_expected_waiting_time_tls_nodes[i] = -1
                AV_real_vs_expected_waiting_time_left_turns[i] = -1
                AV_real_vs_expected_waiting_time_right_turns[i] = -1
                AV_real_vs_expected_waiting_time_crossings[i] = -1
                AV_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                AV_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                AV_real_vs_expected_waiting_time_crossings_tls[i] = -1
                AV_real_vs_expected1_waiting_time[i] = -1
                AV_real_vs_expected2_waiting_time[i] = -1
            #-------------------------------------------------------------------------------WL AVERAGE WEIGHTED WITH LENGTH
            
##                WL_lengths_route_matched = np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers])
            WL_lengths_within_center[i] = np.sum(tripsdatabase.lengths_within_center[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_speeds_av_matched[i] = np.sum(tripsdatabase.speeds_av_matched[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_numbers_prioritychange[i] = np.sum(tripsdatabase.numbers_prioritychange[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_exclusive[i] = np.sum(tripsdatabase.matched_exclusive[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_contrary[i] = np.sum(tripsdatabase.matched_contrary[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_mixed[i] =  np.sum(tripsdatabase.matched_mixed[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_lowpriority[i] = np.sum(tripsdatabase.matched_lowpriority[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_nodes[i] = np.sum(tripsdatabase.matched_nodes[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_left_turns[i] = np.sum(tripsdatabase.matched_left_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_right_turns[i] = np.sum(tripsdatabase.matched_right_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_crossings[i] = np.sum(tripsdatabase.matched_crossings[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_tls_nodes[i] = np.sum(tripsdatabase.matched_tls_nodes[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_tls_left_turns[i] = np.sum(tripsdatabase.matched_tls_left_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_tls_right_turns[i] = np.sum(tripsdatabase.matched_tls_right_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_matched_tls_crossings[i] = np.sum(tripsdatabase.matched_tls_crossings[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_av_n_connections_per_node[i] = np.sum(tripsdatabase.av_n_connections_per_node[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_length[i] = np.sum(tripsdatabase.shortest_length[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_numbers_prioritychange[i] = np.sum(tripsdatabase.shortest_vs_matched_numbers_prioritychange[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_exclusive[i] = np.sum(tripsdatabase.shortest_vs_matched_exclusive[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_contrary[i] = np.sum(tripsdatabase.shortest_vs_matched_contrary[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_mixed[i] = np.sum(tripsdatabase.shortest_vs_matched_mixed[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_lowpriority[i] = np.sum(tripsdatabase.shortest_vs_matched_lowpriority[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_nodes[i] = np.sum(tripsdatabase.shortest_vs_matched_nodes[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_left_turns[i] = np.sum(tripsdatabase.shortest_vs_matched_left_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_right_turns[i] = np.sum(tripsdatabase.shortest_vs_matched_right_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_crossings[i] = np.sum(tripsdatabase.shortest_vs_matched_crossings[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_tls_nodes[i] = np.sum(tripsdatabase.shortest_vs_matched_tls_nodes[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_tls_left_turns[i] = np.sum(tripsdatabase.shortest_vs_matched_tls_left_turns[ids_tripsdatabase_pers]*lengths)/total_lengths 
            WL_shortest_vs_matched_tls_right_turns[i] = np.sum(tripsdatabase.shortest_vs_matched_tls_right_turns[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_tls_crossings[i] = np.sum(tripsdatabase.shortest_vs_matched_tls_crossings[ids_tripsdatabase_pers]*lengths)/total_lengths
            WL_shortest_vs_matched_av_n_connections_per_node[i] = np.sum(tripsdatabase.shortest_vs_matched_av_n_connections_per_node[ids_tripsdatabase_pers]*lengths)/total_lengths
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            

            if ids_tripsdatabase_dyn != []:
                WL_speeds_inmotion_av[i] = np.sum(tripsdatabase.speeds_inmotion_av[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_waiting_times_share[i] = np.sum(tripsdatabase.waiting_times_share[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_intersections_waiting_times[i] = np.sum(tripsdatabase.intersections_waiting_times[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                if ids_tripsdatabase_left_turn != []:
                    WL_left_turns_waiting_times[i] = np.sum(tripsdatabase.left_turns_waiting_times[ids_tripsdatabase_left_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_left_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_left_turn])
                    WL_real_vs_expected_waiting_time_left_turns[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_left_turns[ids_tripsdatabase_left_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_left_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_left_turn])
                else:
                    WL_left_turns_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_left_turns[i] = -1
                if ids_tripsdatabase_right_turn != []:
                    WL_right_turns_waiting_times[i] = np.sum(tripsdatabase.right_turns_waiting_times[ids_tripsdatabase_right_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_right_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_right_turn])
                    WL_real_vs_expected_waiting_time_right_turns[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_right_turns[ids_tripsdatabase_right_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_right_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_right_turn])
                else:
                    WL_right_turns_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_right_turns[i] = -1
                if ids_tripsdatabase_crossing != []:
                    WL_crossings_waiting_times[i] = np.sum(tripsdatabase.crossings_waiting_times[ids_tripsdatabase_crossing]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_crossing])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_crossing])
                    WL_real_vs_expected_waiting_time_crossings[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_crossings[ids_tripsdatabase_crossing]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_crossing])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_crossing])
                else:
                    WL_crossings_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_crossings[i] = -1
                if ids_tripsdatabase_tls_node != []:
                    WL_tls_intersections_waiting_times[i] = np.sum(tripsdatabase.tls_intersections_waiting_times[ids_tripsdatabase_tls_node]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_node])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_node])
                    WL_real_vs_expected_waiting_time_tls_nodes[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_tls_nodes[ids_tripsdatabase_tls_node]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_node])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_node])
                else:
                    WL_tls_intersections_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_tls_nodes[i] = -1
                if ids_tripsdatabase_tls_left_turn != []:
                    WL_tls_left_turns_waiting_times[i] = np.sum(tripsdatabase.tls_left_turns_waiting_times[ids_tripsdatabase_tls_left_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_left_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_left_turn])
                    WL_real_vs_expected_waiting_time_left_turns_tls[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_left_turns_tls[ids_tripsdatabase_tls_left_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_left_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_left_turn])
                else:
                    WL_tls_left_turns_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                if ids_tripsdatabase_tls_right_turn != []:
                    WL_tls_right_turns_waiting_times[i] = np.sum(tripsdatabase.tls_right_turns_waiting_times[ids_tripsdatabase_tls_right_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_right_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_right_turn])
                    WL_real_vs_expected_waiting_time_right_turns_tls[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_right_turns_tls[ids_tripsdatabase_tls_right_turn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_right_turn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_right_turn])
                else:
                    WL_tls_right_turns_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                if ids_tripsdatabase_tls_crossing != []:
                    WL_tls_crossings_waiting_times[i] = np.sum(tripsdatabase.tls_crossings_waiting_times[ids_tripsdatabase_tls_crossing]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_crossing])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_crossing])
                    WL_real_vs_expected_waiting_time_crossings_tls[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_crossings_tls[ids_tripsdatabase_tls_crossing]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_crossing])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_tls_crossing])
                else:
                    WL_tls_crossings_waiting_times[i] = -1
                    WL_real_vs_expected_waiting_time_crossings_tls[i] = -1
                    
                WL_edges_waiting_times_av[i] = np.sum(tripsdatabase.edges_waiting_times_av[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_real_vs_expected_waiting_time_edges[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_edges[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_real_vs_expected_waiting_time_nodes[i] = np.sum(tripsdatabase.real_vs_expected_waiting_time_nodes[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_real_vs_expected1_waiting_time[i] = np.sum(tripsdatabase.real_vs_expected1_waiting_time[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
                WL_real_vs_expected2_waiting_time[i] = np.sum(tripsdatabase.real_vs_expected2_waiting_time[ids_tripsdatabase_dyn]*tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])/np.sum(tripsdatabase.lengths_route_matched[ids_tripsdatabase_dyn])
            else:
                WL_speeds_inmotion_av[i] = -1
                WL_waiting_times_share[i] = -1
                WL_intersections_waiting_times[i] = -1
                WL_left_turns_waiting_times[i] = -1
                WL_right_turns_waiting_times[i] = -1
                WL_crossings_waiting_times[i] = -1
                WL_tls_intersections_waiting_times[i] = -1
                WL_tls_left_turns_waiting_times[i] = -1
                WL_tls_right_turns_waiting_times[i] = -1
                WL_tls_crossings_waiting_times[i] = -1
                WL_edges_waiting_times_av[i] = -1
                WL_real_vs_expected_waiting_time_edges[i] = -1
                WL_real_vs_expected_waiting_time_nodes[i] = -1
                WL_real_vs_expected_waiting_time_tls_nodes[i] = -1
                WL_real_vs_expected_waiting_time_left_turns[i] = -1
                WL_real_vs_expected_waiting_time_right_turns[i] = -1
                WL_real_vs_expected_waiting_time_crossings[i] = -1
                WL_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                WL_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                WL_real_vs_expected_waiting_time_crossings_tls[i] = -1
                WL_real_vs_expected1_waiting_time[i] = -1
                WL_real_vs_expected2_waiting_time[i] = -1
            #-------------------------------------------------------------------------------MD MEDIAN
            MD_lengths_route_matched[i] = np.median(tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers])
            MD_lengths_within_center[i] = np.median(tripsdatabase.lengths_within_center[ids_tripsdatabase_pers])
            MD_speeds_av_matched[i] = np.median(tripsdatabase.speeds_av_matched[ids_tripsdatabase_pers])
            MD_numbers_prioritychange[i] = np.median(tripsdatabase.numbers_prioritychange[ids_tripsdatabase_pers])
            MD_matched_exclusive[i] = np.median(tripsdatabase.matched_exclusive[ids_tripsdatabase_pers])
            MD_matched_contrary[i] = np.median(tripsdatabase.matched_contrary[ids_tripsdatabase_pers])
            MD_matched_mixed[i] = np.median(tripsdatabase.matched_mixed[ids_tripsdatabase_pers])
            MD_matched_lowpriority[i] = np.median(tripsdatabase.matched_lowpriority[ids_tripsdatabase_pers])
            MD_matched_nodes[i] = np.median(tripsdatabase.matched_nodes[ids_tripsdatabase_pers])
            MD_matched_left_turns[i] = np.median(tripsdatabase.matched_left_turns[ids_tripsdatabase_pers])
            MD_matched_right_turns[i] = np.median(tripsdatabase.matched_right_turns[ids_tripsdatabase_pers])
            MD_matched_crossings[i] = np.median(tripsdatabase.matched_crossings[ids_tripsdatabase_pers])
            MD_matched_tls_nodes[i] = np.median(tripsdatabase.matched_tls_nodes[ids_tripsdatabase_pers])
            MD_matched_tls_left_turns[i] = np.median(tripsdatabase.matched_tls_left_turns[ids_tripsdatabase_pers])
            MD_matched_tls_right_turns[i] = np.median(tripsdatabase.matched_tls_right_turns[ids_tripsdatabase_pers])
            MD_matched_tls_crossings[i] = np.median(tripsdatabase.matched_tls_crossings[ids_tripsdatabase_pers])
            MD_av_n_connections_per_node[i] = np.median(tripsdatabase.av_n_connections_per_node[ids_tripsdatabase_pers])
            MD_shortest_length[i] = np.median(tripsdatabase.shortest_length[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_numbers_prioritychange[i] = np.median(tripsdatabase.shortest_vs_matched_numbers_prioritychange[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_exclusive[i] = np.median(tripsdatabase.shortest_vs_matched_exclusive[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_contrary[i] = np.median(tripsdatabase.shortest_vs_matched_contrary[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_mixed[i] = np.median(tripsdatabase.shortest_vs_matched_mixed[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_lowpriority[i] = np.median(tripsdatabase.shortest_vs_matched_lowpriority[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_nodes[i] = np.median(tripsdatabase.shortest_vs_matched_nodes[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_left_turns[i] = np.median(tripsdatabase.shortest_vs_matched_left_turns[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_right_turns[i] = np.median(tripsdatabase.shortest_vs_matched_right_turns[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_crossings[i] = np.median(tripsdatabase.shortest_vs_matched_crossings[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_tls_nodes[i] = np.median(tripsdatabase.shortest_vs_matched_tls_nodes[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_tls_left_turns[i] = np.median(tripsdatabase.shortest_vs_matched_tls_left_turns[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_tls_right_turns[i] = np.median(tripsdatabase.shortest_vs_matched_tls_right_turns[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_tls_crossings[i] = np.median(tripsdatabase.shortest_vs_matched_tls_crossings[ids_tripsdatabase_pers])
            MD_shortest_vs_matched_av_n_connections_per_node[i] = np.median(tripsdatabase.shortest_vs_matched_av_n_connections_per_node[ids_tripsdatabase_pers])
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            

            if ids_tripsdatabase_dyn != []:
                MD_speeds_inmotion_av[i] = np.median(tripsdatabase.speeds_inmotion_av[ids_tripsdatabase_dyn])
                MD_waiting_times_share[i] = np.median(tripsdatabase.waiting_times_share[ids_tripsdatabase_dyn])
                MD_intersections_waiting_times[i] = np.median(tripsdatabase.intersections_waiting_times[ids_tripsdatabase_dyn])
                if ids_tripsdatabase_left_turn != []:
                    MD_left_turns_waiting_times[i] = np.median(tripsdatabase.left_turns_waiting_times[ids_tripsdatabase_left_turn])
                    MD_real_vs_expected_waiting_time_left_turns[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_left_turns[ids_tripsdatabase_left_turn])
                else:
                    MD_left_turns_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_left_turns[i] = -1
                if ids_tripsdatabase_right_turn != []:
                    MD_right_turns_waiting_times[i] = np.median(tripsdatabase.right_turns_waiting_times[ids_tripsdatabase_right_turn])
                    MD_real_vs_expected_waiting_time_right_turns[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_right_turns[ids_tripsdatabase_right_turn])
                else:
                    MD_right_turns_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_right_turns[i] = -1
                if ids_tripsdatabase_crossing != []:
                    MD_crossings_waiting_times[i] = np.median(tripsdatabase.crossings_waiting_times[ids_tripsdatabase_crossing])
                    MD_real_vs_expected_waiting_time_crossings[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_crossings[ids_tripsdatabase_crossing])
                else:
                    MD_crossings_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_crossings[i] = -1
                if ids_tripsdatabase_tls_node != []:
                    MD_tls_intersections_waiting_times[i] = np.median(tripsdatabase.tls_intersections_waiting_times[ids_tripsdatabase_tls_node])
                    MD_real_vs_expected_waiting_time_tls_nodes[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_tls_nodes[ids_tripsdatabase_tls_node])
                else:
                    MD_tls_intersections_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_tls_nodes[i] = -1
                if ids_tripsdatabase_tls_left_turn != []:
                    MD_tls_left_turns_waiting_times[i] = np.median(tripsdatabase.tls_left_turns_waiting_times[ids_tripsdatabase_tls_left_turn])
                    MD_real_vs_expected_waiting_time_left_turns_tls[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_left_turns_tls[ids_tripsdatabase_tls_left_turn])
                else:
                    MD_tls_left_turns_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                if ids_tripsdatabase_tls_right_turn != []:
                    MD_tls_right_turns_waiting_times[i] = np.median(tripsdatabase.tls_right_turns_waiting_times[ids_tripsdatabase_tls_right_turn])
                    MD_real_vs_expected_waiting_time_right_turns_tls[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_right_turns_tls[ids_tripsdatabase_tls_right_turn])
                else:
                    MD_tls_right_turns_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                if ids_tripsdatabase_tls_crossing != []:
                    MD_tls_crossings_waiting_times[i] = np.median(tripsdatabase.tls_crossings_waiting_times[ids_tripsdatabase_tls_crossing])
                    MD_real_vs_expected_waiting_time_crossings_tls[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_crossings_tls[ids_tripsdatabase_tls_crossing])
                else:
                    MD_tls_crossings_waiting_times[i] = -1
                    MD_real_vs_expected_waiting_time_crossings_tls[i] = -1
                    
                MD_edges_waiting_times_av[i] = np.median(tripsdatabase.edges_waiting_times_av[ids_tripsdatabase_dyn])
                MD_real_vs_expected_waiting_time_edges[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_edges[ids_tripsdatabase_dyn])
                MD_real_vs_expected_waiting_time_nodes[i] = np.median(tripsdatabase.real_vs_expected_waiting_time_nodes[ids_tripsdatabase_dyn])
                MD_real_vs_expected1_waiting_time[i] = np.median(tripsdatabase.real_vs_expected1_waiting_time[ids_tripsdatabase_dyn])
                MD_real_vs_expected2_waiting_time[i] = np.median(tripsdatabase.real_vs_expected2_waiting_time[ids_tripsdatabase_dyn])
            else:
                MD_speeds_inmotion_av[i] = -1
                MD_waiting_times_share[i] = -1
                MD_intersections_waiting_times[i] = -1
                MD_left_turns_waiting_times[i] = -1
                MD_right_turns_waiting_times[i] = -1
                MD_crossings_waiting_times[i] = -1
                MD_tls_intersections_waiting_times[i] = -1
                MD_tls_left_turns_waiting_times[i] = -1
                MD_tls_right_turns_waiting_times[i] = -1
                MD_tls_crossings_waiting_times[i] = -1
                MD_edges_waiting_times_av[i] = -1
                MD_real_vs_expected_waiting_time_edges[i] = -1
                MD_real_vs_expected_waiting_time_nodes[i] = -1
                MD_real_vs_expected_waiting_time_tls_nodes[i] = -1
                MD_real_vs_expected_waiting_time_left_turns[i] = -1
                MD_real_vs_expected_waiting_time_right_turns[i] = -1
                MD_real_vs_expected_waiting_time_crossings[i] = -1
                MD_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                MD_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                MD_real_vs_expected_waiting_time_crossings_tls[i] = -1
                MD_real_vs_expected1_waiting_time[i] = -1
                MD_real_vs_expected2_waiting_time[i] = -1
            #-------------------------------------------------------------------------------SD STANDARD DEVIATION
            SD_lengths_route_matched[i] = np.std(tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers])
            SD_lengths_within_center[i] = np.std(tripsdatabase.lengths_within_center[ids_tripsdatabase_pers])
            SD_speeds_av_matched[i] = np.std(tripsdatabase.speeds_av_matched[ids_tripsdatabase_pers])
            SD_numbers_prioritychange[i] = np.std(tripsdatabase.numbers_prioritychange[ids_tripsdatabase_pers])
            SD_matched_exclusive[i] = np.std(tripsdatabase.matched_exclusive[ids_tripsdatabase_pers])
            SD_matched_contrary[i] = np.std(tripsdatabase.matched_contrary[ids_tripsdatabase_pers])
            SD_matched_mixed[i] = np.std(tripsdatabase.matched_mixed[ids_tripsdatabase_pers])
            SD_matched_lowpriority[i] = np.std(tripsdatabase.matched_lowpriority[ids_tripsdatabase_pers])
            SD_matched_nodes[i] = np.std(tripsdatabase.matched_nodes[ids_tripsdatabase_pers])
            SD_matched_left_turns[i] = np.std(tripsdatabase.matched_left_turns[ids_tripsdatabase_pers])
            SD_matched_right_turns[i] = np.std(tripsdatabase.matched_right_turns[ids_tripsdatabase_pers])
            SD_matched_crossings[i] = np.std(tripsdatabase.matched_crossings[ids_tripsdatabase_pers])
            SD_matched_tls_nodes[i] = np.std(tripsdatabase.matched_tls_nodes[ids_tripsdatabase_pers])
            SD_matched_tls_left_turns[i] = np.std(tripsdatabase.matched_tls_left_turns[ids_tripsdatabase_pers])
            SD_matched_tls_right_turns[i] = np.std(tripsdatabase.matched_tls_right_turns[ids_tripsdatabase_pers])
            SD_matched_tls_crossings[i] = np.std(tripsdatabase.matched_tls_crossings[ids_tripsdatabase_pers])
            SD_av_n_connections_per_node[i] = np.std(tripsdatabase.av_n_connections_per_node[ids_tripsdatabase_pers])
            SD_shortest_length[i] = np.std(tripsdatabase.shortest_length[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_numbers_prioritychange[i] = np.std(tripsdatabase.shortest_vs_matched_numbers_prioritychange[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_exclusive[i] = np.std(tripsdatabase.shortest_vs_matched_exclusive[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_contrary[i] = np.std(tripsdatabase.shortest_vs_matched_contrary[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_mixed[i] = np.std(tripsdatabase.shortest_vs_matched_mixed[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_lowpriority[i] = np.std(tripsdatabase.shortest_vs_matched_lowpriority[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_nodes[i] = np.std(tripsdatabase.shortest_vs_matched_nodes[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_left_turns[i] = np.std(tripsdatabase.shortest_vs_matched_left_turns[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_right_turns[i] = np.std(tripsdatabase.shortest_vs_matched_right_turns[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_crossings[i] = np.std(tripsdatabase.shortest_vs_matched_crossings[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_tls_nodes[i] = np.std(tripsdatabase.shortest_vs_matched_tls_nodes[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_tls_left_turns[i] = np.std(tripsdatabase.shortest_vs_matched_tls_left_turns[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_tls_right_turns[i] = np.std(tripsdatabase.shortest_vs_matched_tls_right_turns[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_tls_crossings[i] = np.std(tripsdatabase.shortest_vs_matched_tls_crossings[ids_tripsdatabase_pers])
            SD_shortest_vs_matched_av_n_connections_per_node[i] = np.std(tripsdatabase.shortest_vs_matched_av_n_connections_per_node[ids_tripsdatabase_pers])
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            

            if ids_tripsdatabase_dyn != []:
                SD_speeds_inmotion_av[i] = np.std(tripsdatabase.speeds_inmotion_av[ids_tripsdatabase_dyn])
                SD_waiting_times_share[i] = np.std(tripsdatabase.waiting_times_share[ids_tripsdatabase_dyn])
                SD_intersections_waiting_times[i] = np.std(tripsdatabase.intersections_waiting_times[ids_tripsdatabase_dyn])
                if ids_tripsdatabase_left_turn != []:
                    SD_left_turns_waiting_times[i] = np.std(tripsdatabase.left_turns_waiting_times[ids_tripsdatabase_left_turn])
                    SD_real_vs_expected_waiting_time_left_turns[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_left_turns[ids_tripsdatabase_left_turn])
                else:
                    SD_left_turns_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_left_turns[i] = -1
                if ids_tripsdatabase_right_turn != []:
                    SD_right_turns_waiting_times[i] = np.std(tripsdatabase.right_turns_waiting_times[ids_tripsdatabase_right_turn])
                    SD_real_vs_expected_waiting_time_right_turns[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_right_turns[ids_tripsdatabase_right_turn])
                else:
                    SD_right_turns_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_right_turns[i] = -1
                if ids_tripsdatabase_crossing != []:
                    SD_crossings_waiting_times[i] = np.std(tripsdatabase.crossings_waiting_times[ids_tripsdatabase_crossing])
                    SD_real_vs_expected_waiting_time_crossings[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_crossings[ids_tripsdatabase_crossing])
                else:
                    SD_crossings_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_crossings[i] = -1
                if ids_tripsdatabase_tls_node != []:
                    SD_tls_intersections_waiting_times[i] = np.std(tripsdatabase.tls_intersections_waiting_times[ids_tripsdatabase_tls_node])
                    SD_real_vs_expected_waiting_time_tls_nodes[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_tls_nodes[ids_tripsdatabase_tls_node])
                else:
                    SD_tls_intersections_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_tls_nodes[i] = -1
                if ids_tripsdatabase_tls_left_turn != []:
                    SD_tls_left_turns_waiting_times[i] = np.std(tripsdatabase.tls_left_turns_waiting_times[ids_tripsdatabase_tls_left_turn])
                    SD_real_vs_expected_waiting_time_left_turns_tls[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_left_turns_tls[ids_tripsdatabase_tls_left_turn])
                else:
                    SD_tls_left_turns_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                if ids_tripsdatabase_tls_right_turn != []:
                    SD_tls_right_turns_waiting_times[i] = np.std(tripsdatabase.tls_right_turns_waiting_times[ids_tripsdatabase_tls_right_turn])
                    SD_real_vs_expected_waiting_time_right_turns_tls[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_right_turns_tls[ids_tripsdatabase_tls_right_turn])
                else:
                    SD_tls_right_turns_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                if ids_tripsdatabase_tls_crossing != []:
                    SD_tls_crossings_waiting_times[i] = np.std(tripsdatabase.tls_crossings_waiting_times[ids_tripsdatabase_tls_crossing])
                    SD_real_vs_expected_waiting_time_crossings_tls[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_crossings_tls[ids_tripsdatabase_tls_crossing])
                else:
                    SD_tls_crossings_waiting_times[i] = -1
                    SD_real_vs_expected_waiting_time_crossings_tls[i] = -1
                    
                SD_edges_waiting_times_av[i] = np.std(tripsdatabase.edges_waiting_times_av[ids_tripsdatabase_dyn])
                SD_real_vs_expected_waiting_time_edges[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_edges[ids_tripsdatabase_dyn])
                SD_real_vs_expected_waiting_time_nodes[i] = np.std(tripsdatabase.real_vs_expected_waiting_time_nodes[ids_tripsdatabase_dyn])
                SD_real_vs_expected1_waiting_time[i] = np.std(tripsdatabase.real_vs_expected1_waiting_time[ids_tripsdatabase_dyn])
                SD_real_vs_expected2_waiting_time[i] = np.std(tripsdatabase.real_vs_expected2_waiting_time[ids_tripsdatabase_dyn])
            else:
                SD_speeds_inmotion_av[i] = -1
                SD_waiting_times_share[i] = -1
                SD_intersections_waiting_times[i] = -1
                SD_left_turns_waiting_times[i] = -1
                SD_right_turns_waiting_times[i] = -1
                SD_crossings_waiting_times[i] = -1
                SD_tls_intersections_waiting_times[i] = -1
                SD_tls_left_turns_waiting_times[i] = -1
                SD_tls_right_turns_waiting_times[i] = -1
                SD_tls_crossings_waiting_times[i] = -1
                SD_edges_waiting_times_av[i] = -1
                SD_real_vs_expected_waiting_time_edges[i] = -1
                SD_real_vs_expected_waiting_time_nodes[i] = -1
                SD_real_vs_expected_waiting_time_tls_nodes[i] = -1
                SD_real_vs_expected_waiting_time_left_turns[i] = -1
                SD_real_vs_expected_waiting_time_right_turns[i] = -1
                SD_real_vs_expected_waiting_time_crossings[i] = -1
                SD_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                SD_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                SD_real_vs_expected_waiting_time_crossings_tls[i] = -1
                SD_real_vs_expected1_waiting_time[i] = -1
                SD_real_vs_expected2_waiting_time[i] = -1
            #-------------------------------------------------------------------------------MA MEAN ABSOLUTE DIFFERENCE
            MA_lengths_route_matched[i] = self.get_average_absolute_difference(tripsdatabase.lengths_route_matched[ids_tripsdatabase_pers])
            MA_lengths_within_center[i] = self.get_average_absolute_difference(tripsdatabase.lengths_within_center[ids_tripsdatabase_pers])
            MA_speeds_av_matched[i] = self.get_average_absolute_difference(tripsdatabase.speeds_av_matched[ids_tripsdatabase_pers])
            MA_numbers_prioritychange[i] = self.get_average_absolute_difference(tripsdatabase.numbers_prioritychange[ids_tripsdatabase_pers])
            MA_matched_exclusive[i] = self.get_average_absolute_difference(tripsdatabase.matched_exclusive[ids_tripsdatabase_pers])
            MA_matched_contrary[i] = self.get_average_absolute_difference(tripsdatabase.matched_contrary[ids_tripsdatabase_pers])
            MA_matched_mixed[i] = self.get_average_absolute_difference(tripsdatabase.matched_mixed[ids_tripsdatabase_pers])
            MA_matched_lowpriority[i] = self.get_average_absolute_difference(tripsdatabase.matched_lowpriority[ids_tripsdatabase_pers])
            MA_matched_nodes[i] = self.get_average_absolute_difference(tripsdatabase.matched_nodes[ids_tripsdatabase_pers])
            MA_matched_left_turns[i] = self.get_average_absolute_difference(tripsdatabase.matched_left_turns[ids_tripsdatabase_pers])
            MA_matched_right_turns[i] = self.get_average_absolute_difference(tripsdatabase.matched_right_turns[ids_tripsdatabase_pers])
            MA_matched_crossings[i] = self.get_average_absolute_difference(tripsdatabase.matched_crossings[ids_tripsdatabase_pers])
            MA_matched_tls_nodes[i] = self.get_average_absolute_difference(tripsdatabase.matched_tls_nodes[ids_tripsdatabase_pers])
            MA_matched_tls_left_turns[i] = self.get_average_absolute_difference(tripsdatabase.matched_tls_left_turns[ids_tripsdatabase_pers])
            MA_matched_tls_right_turns[i] = self.get_average_absolute_difference(tripsdatabase.matched_tls_right_turns[ids_tripsdatabase_pers])
            MA_matched_tls_crossings[i] = self.get_average_absolute_difference(tripsdatabase.matched_tls_crossings[ids_tripsdatabase_pers])
            MA_av_n_connections_per_node[i] = self.get_average_absolute_difference(tripsdatabase.av_n_connections_per_node[ids_tripsdatabase_pers])
            MA_shortest_length[i] = self.get_average_absolute_difference(tripsdatabase.shortest_length[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_numbers_prioritychange[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_numbers_prioritychange[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_exclusive[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_exclusive[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_contrary[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_contrary[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_mixed[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_mixed[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_lowpriority[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_lowpriority[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_nodes[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_nodes[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_left_turns[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_left_turns[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_right_turns[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_right_turns[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_crossings[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_crossings[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_tls_nodes[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_tls_nodes[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_tls_left_turns[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_tls_left_turns[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_tls_right_turns[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_tls_right_turns[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_tls_crossings[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_tls_crossings[ids_tripsdatabase_pers])
            MA_shortest_vs_matched_av_n_connections_per_node[i] = self.get_average_absolute_difference(tripsdatabase.shortest_vs_matched_av_n_connections_per_node[ids_tripsdatabase_pers])
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            

            if ids_tripsdatabase_dyn != []:
                MA_speeds_inmotion_av[i] = self.get_average_absolute_difference(tripsdatabase.speeds_inmotion_av[ids_tripsdatabase_dyn ])
                MA_waiting_times_share[i] = self.get_average_absolute_difference(tripsdatabase.waiting_times_share[ids_tripsdatabase_dyn ])
                MA_intersections_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.intersections_waiting_times[ids_tripsdatabase_dyn ])
                if ids_tripsdatabase_left_turn != []:
                    MA_left_turns_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.left_turns_waiting_times[ids_tripsdatabase_left_turn])
                    MA_real_vs_expected_waiting_time_left_turns[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_left_turns[ids_tripsdatabase_left_turn])
                else:
                    MA_left_turns_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_left_turns[i] = -1
                if ids_tripsdatabase_right_turn != []:
                    MA_right_turns_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.right_turns_waiting_times[ids_tripsdatabase_right_turn])
                    MA_real_vs_expected_waiting_time_right_turns[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_right_turns[ids_tripsdatabase_right_turn])
                else:
                    MA_right_turns_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_right_turns[i] = -1
                if ids_tripsdatabase_crossing != []:
                    MA_crossings_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.crossings_waiting_times[ids_tripsdatabase_crossing])
                    MA_real_vs_expected_waiting_time_crossings[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_crossings[ids_tripsdatabase_crossing])
                else:
                    MA_crossings_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_crossings[i] = -1
                if ids_tripsdatabase_tls_node != []:
                    MA_tls_intersections_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.tls_intersections_waiting_times[ids_tripsdatabase_tls_node])
                    MA_real_vs_expected_waiting_time_tls_nodes[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_tls_nodes[ids_tripsdatabase_tls_node])
                else:
                    MA_tls_intersections_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_tls_nodes[i] = -1
                if ids_tripsdatabase_tls_left_turn != []:
                    MA_tls_left_turns_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.tls_left_turns_waiting_times[ids_tripsdatabase_tls_left_turn])
                    MA_real_vs_expected_waiting_time_left_turns_tls[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_left_turns_tls[ids_tripsdatabase_tls_left_turn])
                else:
                    MA_tls_left_turns_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                if ids_tripsdatabase_tls_right_turn != []:
                    MA_tls_right_turns_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.tls_right_turns_waiting_times[ids_tripsdatabase_tls_right_turn])
                    MA_real_vs_expected_waiting_time_right_turns_tls[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_right_turns_tls[ids_tripsdatabase_tls_right_turn])
                else:
                    MA_tls_right_turns_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                if ids_tripsdatabase_tls_crossing != []:
                    MA_tls_crossings_waiting_times[i] = self.get_average_absolute_difference(tripsdatabase.tls_crossings_waiting_times[ids_tripsdatabase_tls_crossing])
                    MA_real_vs_expected_waiting_time_crossings_tls[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_crossings_tls[ids_tripsdatabase_tls_crossing])
                else:
                    MA_tls_crossings_waiting_times[i] = -1
                    MA_real_vs_expected_waiting_time_crossings_tls[i] = -1
                    
                MA_edges_waiting_times_av[i] = self.get_average_absolute_difference(tripsdatabase.edges_waiting_times_av[ids_tripsdatabase_dyn])
                MA_real_vs_expected_waiting_time_edges[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_edges[ids_tripsdatabase_dyn])
                MA_real_vs_expected_waiting_time_nodes[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected_waiting_time_nodes[ids_tripsdatabase_dyn])
                MA_real_vs_expected1_waiting_time[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected1_waiting_time[ids_tripsdatabase_dyn])
                MA_real_vs_expected2_waiting_time[i] = self.get_average_absolute_difference(tripsdatabase.real_vs_expected2_waiting_time[ids_tripsdatabase_dyn])
            else:
                MA_speeds_inmotion_av[i] = -1
                MA_waiting_times_share[i] = -1
                MA_intersections_waiting_times[i] = -1
                MA_left_turns_waiting_times[i] = -1
                MA_right_turns_waiting_times[i] = -1
                MA_crossings_waiting_times[i] = -1
                MA_tls_intersections_waiting_times[i] = -1
                MA_tls_left_turns_waiting_times[i] = -1
                MA_tls_right_turns_waiting_times[i] = -1
                MA_tls_crossings_waiting_times[i] = -1
                MA_edges_waiting_times_av[i] = -1
                MA_real_vs_expected_waiting_time_edges[i] = -1
                MA_real_vs_expected_waiting_time_nodes[i] = -1
                MA_real_vs_expected_waiting_time_tls_nodes[i] = -1
                MA_real_vs_expected_waiting_time_left_turns[i] = -1
                MA_real_vs_expected_waiting_time_right_turns[i] = -1
                MA_real_vs_expected_waiting_time_crossings[i] = -1
                MA_real_vs_expected_waiting_time_left_turns_tls[i] = -1
                MA_real_vs_expected_waiting_time_right_turns_tls[i] = -1
                MA_real_vs_expected_waiting_time_crossings_tls[i] = -1
                MA_real_vs_expected1_waiting_time[i] = -1
                MA_real_vs_expected2_waiting_time[i] = -1
                


        ids_cyclistsdatabase_res = self.cyclistsdatabase.add_rows(    #User = users,
            ids_person = ids_person,
            ids_gender = ids_gender,
            age = age,
            are_frequent_user = are_frequent_user,
            numbers_tot_trip_mached = numbers_tot_trip_mached,
            n_dyn_trips = n_dynaanalysis,
            n_dyn_tls = n_dyn_tls,
            n_dyn_left = n_dyn_left,
            n_dyn_right = n_dyn_right,
            n_dyn_crossing = n_dyn_crossing,
            n_dyn_left_tls = n_dyn_left_tls,
            n_dyn_right_tls = n_dyn_right_tls,
            n_dyn_crossing_tls = n_dyn_crossing_tls,
            #-------------------------------------------------------------------------------AV NORMAL AVERAGE REFERRED TO THE CARRIED OUT TRIPS
            AV_lengths_route_matched = AV_lengths_route_matched,
            AV_lengths_within_center = AV_lengths_within_center,
            AV_speeds_av_matched = AV_speeds_av_matched,
            AV_numbers_prioritychange = AV_numbers_prioritychange,
            AV_matched_exclusive = AV_matched_exclusive,
            AV_matched_contrary = AV_matched_contrary,
            AV_matched_mixed = AV_matched_mixed,
            AV_matched_lowpriority = AV_matched_lowpriority,
            AV_matched_nodes = AV_matched_nodes,
            AV_matched_left_turns = AV_matched_left_turns,
            AV_matched_right_turns = AV_matched_right_turns,
            AV_matched_crossings = AV_matched_crossings,
            AV_matched_tls_nodes = AV_matched_tls_nodes,
            AV_matched_tls_left_turns = AV_matched_tls_left_turns,
            AV_matched_tls_right_turns = AV_matched_tls_right_turns,
            AV_matched_tls_crossings = AV_matched_tls_crossings,
            AV_av_n_connections_per_node = AV_av_n_connections_per_node,
            AV_shortest_length = AV_shortest_length,
            AV_shortest_vs_matched_numbers_prioritychange = AV_shortest_vs_matched_numbers_prioritychange,
            AV_shortest_vs_matched_exclusive = AV_shortest_vs_matched_exclusive,
            AV_shortest_vs_matched_contrary = AV_shortest_vs_matched_contrary,
            AV_shortest_vs_matched_mixed = AV_shortest_vs_matched_mixed,
            AV_shortest_vs_matched_lowpriority = AV_shortest_vs_matched_lowpriority,
            AV_shortest_vs_matched_nodes = AV_shortest_vs_matched_nodes,
            AV_shortest_vs_matched_left_turns = AV_shortest_vs_matched_left_turns,
            AV_shortest_vs_matched_right_turns = AV_shortest_vs_matched_right_turns,
            AV_shortest_vs_matched_crossings = AV_shortest_vs_matched_crossings,
            AV_shortest_vs_matched_tls_nodes = AV_shortest_vs_matched_tls_nodes,
            AV_shortest_vs_matched_tls_left_turns = AV_shortest_vs_matched_tls_left_turns,
            AV_shortest_vs_matched_tls_right_turns = AV_shortest_vs_matched_tls_right_turns,
            AV_shortest_vs_matched_tls_crossings = AV_shortest_vs_matched_tls_crossings,
            AV_shortest_vs_matched_av_n_connections_per_node = AV_shortest_vs_matched_av_n_connections_per_node,
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            AV_speeds_inmotion_av = AV_speeds_inmotion_av,
            AV_waiting_times_share = AV_waiting_times_share,
            AV_intersections_waiting_times = AV_intersections_waiting_times,
            AV_left_turns_waiting_times = AV_left_turns_waiting_times,
            AV_right_turns_waiting_times = AV_right_turns_waiting_times,
            AV_crossings_waiting_times = AV_crossings_waiting_times,
            AV_tls_intersections_waiting_times = AV_tls_intersections_waiting_times,
            AV_tls_left_turns_waiting_times = AV_tls_left_turns_waiting_times,
            AV_tls_right_turns_waiting_times = AV_tls_right_turns_waiting_times,
            AV_tls_crossings_waiting_times = AV_tls_crossings_waiting_times,
            AV_edges_waiting_times_av = AV_edges_waiting_times_av,
            AV_real_vs_expected_waiting_time_edges = AV_real_vs_expected_waiting_time_edges,
            AV_real_vs_expected_waiting_time_nodes = AV_real_vs_expected_waiting_time_nodes,
            AV_real_vs_expected_waiting_time_tls_nodes = AV_real_vs_expected_waiting_time_tls_nodes,
            AV_real_vs_expected_waiting_time_left_turns = AV_real_vs_expected_waiting_time_left_turns,
            AV_real_vs_expected_waiting_time_right_turns = AV_real_vs_expected_waiting_time_right_turns,
            AV_real_vs_expected_waiting_time_crossings = AV_real_vs_expected_waiting_time_crossings,
            AV_real_vs_expected_waiting_time_left_turns_tls = AV_real_vs_expected_waiting_time_left_turns_tls,
            AV_real_vs_expected_waiting_time_right_turns_tls = AV_real_vs_expected_waiting_time_right_turns_tls,
            AV_real_vs_expected_waiting_time_crossings_tls =AV_real_vs_expected_waiting_time_crossings_tls ,
            AV_real_vs_expected1_waiting_time = AV_real_vs_expected1_waiting_time,
            AV_real_vs_expected2_waiting_time = AV_real_vs_expected2_waiting_time,
            #-------------------------------------------------------------------------------WL AVERAGE WEIGHTED WITH LENGTH
##            WL_lengths_route_matched = WL_lengths_route_matched,
            WL_lengths_within_center = WL_lengths_within_center,
            WL_speeds_av_matched = WL_speeds_av_matched,
            WL_numbers_prioritychange = WL_numbers_prioritychange,
            WL_matched_exclusive = WL_matched_exclusive,
            WL_matched_contrary = WL_matched_contrary,
            WL_matched_mixed = WL_matched_mixed ,
            WL_matched_lowpriority = WL_matched_lowpriority,
            WL_matched_nodes = WL_matched_nodes,
            WL_matched_left_turns =WL_matched_left_turns ,
            WL_matched_right_turns = WL_matched_right_turns,
            WL_matched_crossings = WL_matched_crossings,
            WL_matched_tls_nodes = WL_matched_tls_nodes,
            WL_matched_tls_left_turns = WL_matched_tls_left_turns,
            WL_matched_tls_right_turns = WL_matched_tls_right_turns,
            WL_matched_tls_crossings = WL_matched_tls_crossings,
            WL_av_n_connections_per_node = WL_av_n_connections_per_node,
            WL_shortest_length =WL_shortest_length ,
            WL_shortest_vs_matched_numbers_prioritychange = WL_shortest_vs_matched_numbers_prioritychange,
            WL_shortest_vs_matched_exclusive = WL_shortest_vs_matched_exclusive,
            WL_shortest_vs_matched_contrary = WL_shortest_vs_matched_contrary,
            WL_shortest_vs_matched_mixed = WL_shortest_vs_matched_mixed,
            WL_shortest_vs_matched_lowpriority = WL_shortest_vs_matched_lowpriority,
            WL_shortest_vs_matched_nodes = WL_shortest_vs_matched_nodes ,
            WL_shortest_vs_matched_left_turns = WL_shortest_vs_matched_left_turns,
            WL_shortest_vs_matched_right_turns = WL_shortest_vs_matched_right_turns,
            WL_shortest_vs_matched_crossings = WL_shortest_vs_matched_crossings,
            WL_shortest_vs_matched_tls_nodes =WL_shortest_vs_matched_tls_nodes ,
            WL_shortest_vs_matched_tls_left_turns = WL_shortest_vs_matched_tls_left_turns,
            WL_shortest_vs_matched_tls_right_turns = WL_shortest_vs_matched_tls_right_turns,
            WL_shortest_vs_matched_tls_crossings = WL_shortest_vs_matched_tls_crossings ,
            WL_shortest_vs_matched_av_n_connections_per_node = WL_shortest_vs_matched_av_n_connections_per_node,
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            WL_speeds_inmotion_av =WL_speeds_inmotion_av ,
            WL_waiting_times_share = WL_waiting_times_share,
            WL_intersections_waiting_times = WL_intersections_waiting_times,
            WL_left_turns_waiting_times = WL_left_turns_waiting_times,
            WL_right_turns_waiting_times  = WL_right_turns_waiting_times,
            WL_crossings_waiting_times = WL_crossings_waiting_times,
            WL_tls_intersections_waiting_times = WL_tls_intersections_waiting_times,
            WL_tls_left_turns_waiting_times = WL_tls_left_turns_waiting_times,
            WL_tls_right_turns_waiting_times =WL_tls_right_turns_waiting_times ,
            WL_tls_crossings_waiting_times = WL_tls_crossings_waiting_times,
            WL_edges_waiting_times_av =WL_edges_waiting_times_av ,
            WL_real_vs_expected_waiting_time_edges = WL_real_vs_expected_waiting_time_edges,
            WL_real_vs_expected_waiting_time_nodes = WL_real_vs_expected_waiting_time_nodes,
            WL_real_vs_expected_waiting_time_tls_nodes = WL_real_vs_expected_waiting_time_tls_nodes,
            WL_real_vs_expected_waiting_time_left_turns = WL_real_vs_expected_waiting_time_left_turns,
            WL_real_vs_expected_waiting_time_right_turns = WL_real_vs_expected_waiting_time_right_turns,
            WL_real_vs_expected_waiting_time_crossings = WL_real_vs_expected_waiting_time_crossings,
            WL_real_vs_expected_waiting_time_left_turns_tls =WL_real_vs_expected_waiting_time_left_turns_tls ,
            WL_real_vs_expected_waiting_time_right_turns_tls = WL_real_vs_expected_waiting_time_right_turns_tls,
            WL_real_vs_expected_waiting_time_crossings_tls = WL_real_vs_expected_waiting_time_crossings_tls,
            WL_real_vs_expected1_waiting_time = WL_real_vs_expected1_waiting_time,
            WL_real_vs_expected2_waiting_time = WL_real_vs_expected2_waiting_time,
            #-------------------------------------------------------------------------------MD MEDIAN
            MD_lengths_route_matched = MD_lengths_route_matched,
            MD_lengths_within_center = MD_lengths_within_center,
            MD_speeds_av_matched = MD_speeds_av_matched,
            MD_numbers_prioritychange = MD_numbers_prioritychange,
            MD_matched_exclusive = MD_matched_exclusive,
            MD_matched_contrary = MD_matched_contrary,
            MD_matched_mixed = MD_matched_mixed,
            MD_matched_lowpriority = MD_matched_lowpriority,
            MD_matched_nodes = MD_matched_nodes,
            MD_matched_left_turns = MD_matched_left_turns,
            MD_matched_right_turns = MD_matched_right_turns,
            MD_matched_crossings =MD_matched_crossings ,
            MD_matched_tls_nodes = MD_matched_tls_nodes,
            MD_matched_tls_left_turns = MD_matched_tls_left_turns,
            MD_matched_tls_right_turns = MD_matched_tls_right_turns,
            MD_matched_tls_crossings = MD_matched_tls_crossings,
            MD_av_n_connections_per_node = MD_av_n_connections_per_node,
            MD_shortest_length = MD_shortest_length,
            MD_shortest_vs_matched_numbers_prioritychange = MD_shortest_vs_matched_numbers_prioritychange,
            MD_shortest_vs_matched_exclusive = MD_shortest_vs_matched_exclusive,
            MD_shortest_vs_matched_contrary = MD_shortest_vs_matched_contrary,
            MD_shortest_vs_matched_mixed = MD_shortest_vs_matched_mixed,
            MD_shortest_vs_matched_lowpriority = MD_shortest_vs_matched_lowpriority,
            MD_shortest_vs_matched_nodes = MD_shortest_vs_matched_nodes,
            MD_shortest_vs_matched_left_turns = MD_shortest_vs_matched_left_turns,
            MD_shortest_vs_matched_right_turns = MD_shortest_vs_matched_right_turns,
            MD_shortest_vs_matched_crossings = MD_shortest_vs_matched_crossings,
            MD_shortest_vs_matched_tls_nodes =MD_shortest_vs_matched_tls_nodes,
            MD_shortest_vs_matched_tls_left_turns = MD_shortest_vs_matched_tls_left_turns,
            MD_shortest_vs_matched_tls_right_turns = MD_shortest_vs_matched_tls_right_turns,
            MD_shortest_vs_matched_tls_crossings = MD_shortest_vs_matched_tls_crossings,
            MD_shortest_vs_matched_av_n_connections_per_node = MD_shortest_vs_matched_av_n_connections_per_node,
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            MD_speeds_inmotion_av = MD_speeds_inmotion_av,
            MD_waiting_times_share = MD_waiting_times_share,
            MD_intersections_waiting_times = MD_intersections_waiting_times,
            MD_left_turns_waiting_times = MD_left_turns_waiting_times,
            MD_right_turns_waiting_times = MD_right_turns_waiting_times,
            MD_crossings_waiting_times = MD_crossings_waiting_times,
            MD_tls_intersections_waiting_times = MD_tls_intersections_waiting_times,
            MD_tls_left_turns_waiting_times =MD_tls_left_turns_waiting_times ,
            MD_tls_right_turns_waiting_times = MD_tls_right_turns_waiting_times,
            MD_tls_crossings_waiting_times = MD_tls_crossings_waiting_times,
            MD_edges_waiting_times_av = MD_edges_waiting_times_av,
            MD_real_vs_expected_waiting_time_edges = MD_real_vs_expected_waiting_time_edges,
            MD_real_vs_expected_waiting_time_nodes = MD_real_vs_expected_waiting_time_nodes,
            MD_real_vs_expected_waiting_time_tls_nodes = MD_real_vs_expected_waiting_time_tls_nodes,
            MD_real_vs_expected_waiting_time_left_turns =MD_real_vs_expected_waiting_time_left_turns ,
            MD_real_vs_expected_waiting_time_right_turns = MD_real_vs_expected_waiting_time_right_turns,
            MD_real_vs_expected_waiting_time_crossings = MD_real_vs_expected_waiting_time_crossings,
            MD_real_vs_expected_waiting_time_left_turns_tls = MD_real_vs_expected_waiting_time_left_turns_tls,
            MD_real_vs_expected_waiting_time_right_turns_tls = MD_real_vs_expected_waiting_time_right_turns_tls,
            MD_real_vs_expected_waiting_time_crossings_tls = MD_real_vs_expected_waiting_time_crossings_tls,
            MD_real_vs_expected1_waiting_time = MD_real_vs_expected1_waiting_time,
            MD_real_vs_expected2_waiting_time = MD_real_vs_expected2_waiting_time,
            #-------------------------------------------------------------------------------SD STANDARD DEVIATION
            SD_lengths_route_matched = SD_lengths_route_matched,
            SD_lengths_within_center = SD_lengths_within_center,
            SD_speeds_av_matched = SD_speeds_av_matched,
            SD_matched_exclusive = SD_matched_exclusive,
            SD_matched_contrary = SD_matched_contrary,
            SD_numbers_prioritychange = SD_numbers_prioritychange,
            SD_matched_mixed = SD_matched_mixed,
            SD_matched_lowpriority = SD_matched_lowpriority,
            SD_matched_nodes = SD_matched_nodes,
            SD_matched_left_turns = SD_matched_left_turns,
            SD_matched_right_turns = SD_matched_right_turns,
            SD_matched_crossings = SD_matched_crossings,
            SD_matched_tls_nodes = SD_matched_tls_nodes,
            SD_matched_tls_left_turns = SD_matched_tls_left_turns,
            SD_matched_tls_right_turns = SD_matched_tls_right_turns,
            SD_matched_tls_crossings = SD_matched_tls_crossings,
            SD_av_n_connections_per_node = SD_av_n_connections_per_node,
            SD_shortest_length = SD_shortest_length,
            SD_shortest_vs_matched_numbers_prioritychange = SD_shortest_vs_matched_numbers_prioritychange,
            SD_shortest_vs_matched_exclusive = SD_shortest_vs_matched_exclusive,
            SD_shortest_vs_matched_contrary = SD_shortest_vs_matched_contrary,
            SD_shortest_vs_matched_mixed = SD_shortest_vs_matched_mixed,
            SD_shortest_vs_matched_lowpriority = SD_shortest_vs_matched_lowpriority,
            SD_shortest_vs_matched_nodes = SD_shortest_vs_matched_nodes,
            SD_shortest_vs_matched_left_turns = SD_shortest_vs_matched_left_turns,
            SD_shortest_vs_matched_right_turns = SD_shortest_vs_matched_right_turns,
            SD_shortest_vs_matched_crossings = SD_shortest_vs_matched_crossings,
            SD_shortest_vs_matched_tls_nodes = SD_shortest_vs_matched_tls_nodes,
            SD_shortest_vs_matched_tls_left_turns = SD_shortest_vs_matched_tls_left_turns,
            SD_shortest_vs_matched_tls_right_turns = SD_shortest_vs_matched_tls_right_turns,
            SD_shortest_vs_matched_tls_crossings = SD_shortest_vs_matched_tls_crossings,
            SD_shortest_vs_matched_av_n_connections_per_node = SD_shortest_vs_matched_av_n_connections_per_node,
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
            SD_speeds_inmotion_av = SD_speeds_inmotion_av,
            SD_waiting_times_share = SD_waiting_times_share,
            SD_intersections_waiting_times = SD_intersections_waiting_times,
            SD_left_turns_waiting_times = SD_left_turns_waiting_times,
            SD_right_turns_waiting_times = SD_right_turns_waiting_times,
            SD_crossings_waiting_times = SD_crossings_waiting_times,
            SD_tls_intersections_waiting_times = SD_tls_intersections_waiting_times,
            SD_tls_left_turns_waiting_times = SD_tls_left_turns_waiting_times,
            SD_tls_right_turns_waiting_times = SD_tls_right_turns_waiting_times,
            SD_tls_crossings_waiting_times = SD_tls_crossings_waiting_times,
            SD_edges_waiting_times_av = SD_edges_waiting_times_av,
            SD_real_vs_expected_waiting_time_edges = SD_real_vs_expected_waiting_time_edges,
            SD_real_vs_expected_waiting_time_nodes = SD_real_vs_expected_waiting_time_nodes,
            SD_real_vs_expected_waiting_time_tls_nodes = SD_real_vs_expected_waiting_time_tls_nodes,
            SD_real_vs_expected_waiting_time_left_turns = SD_real_vs_expected_waiting_time_left_turns,
            SD_real_vs_expected_waiting_time_right_turns = SD_real_vs_expected_waiting_time_right_turns,
            SD_real_vs_expected_waiting_time_crossings = SD_real_vs_expected_waiting_time_crossings,
            SD_real_vs_expected_waiting_time_left_turns_tls = SD_real_vs_expected_waiting_time_left_turns_tls,
            SD_real_vs_expected_waiting_time_right_turns_tls = SD_real_vs_expected_waiting_time_right_turns_tls,
            SD_real_vs_expected_waiting_time_crossings_tls = SD_real_vs_expected_waiting_time_crossings_tls,
            SD_real_vs_expected1_waiting_time = SD_real_vs_expected1_waiting_time,
            SD_real_vs_expected2_waiting_time = SD_real_vs_expected2_waiting_time,
            #-------------------------------------------------------------------------------MA MEAN ABSOLUTE DIFFERENCE
##            MA_lengths_route_matched = MA_lengths_route_matched,
##            MA_lengths_within_center = MA_lengths_within_center,
##            MA_speeds_av_matched = MA_speeds_av_matched,
##            MA_numbers_prioritychange = MA_numbers_prioritychange,
##            MA_matched_exclusive =MA_matched_exclusive ,
##            MA_matched_contrary =MA_matched_contrary ,
##            MA_matched_mixed = MA_matched_mixed,
##            MA_matched_lowpriority = MA_matched_lowpriority,
##            MA_matched_nodes = MA_matched_nodes,
##            MA_matched_left_turns = MA_matched_left_turns,
##            MA_matched_right_turns = MA_matched_right_turns,
##            MA_matched_crossings = MA_matched_crossings,
##            MA_matched_tls_nodes = MA_matched_tls_nodes,
##            MA_matched_tls_left_turns = MA_matched_tls_left_turns,
##            MA_matched_tls_right_turns = MA_matched_tls_right_turns,
##            MA_matched_tls_crossings =MA_matched_tls_crossings ,
##            MA_av_n_connections_per_node = MA_av_n_connections_per_node,
##            MA_shortest_length = MA_shortest_length,
##            MA_shortest_vs_matched_numbers_prioritychange = MA_shortest_vs_matched_numbers_prioritychange,
##            MA_shortest_vs_matched_exclusive = MA_shortest_vs_matched_exclusive,
##            MA_shortest_vs_matched_contrary = MA_shortest_vs_matched_contrary,
##            MA_shortest_vs_matched_mixed = MA_shortest_vs_matched_mixed,
##            MA_shortest_vs_matched_lowpriority = MA_shortest_vs_matched_lowpriority ,
##            MA_shortest_vs_matched_nodes = MA_shortest_vs_matched_nodes,
##            MA_shortest_vs_matched_left_turns = MA_shortest_vs_matched_left_turns,
##            MA_shortest_vs_matched_right_turns = MA_shortest_vs_matched_right_turns,
##            MA_shortest_vs_matched_crossings = MA_shortest_vs_matched_crossings,
##            MA_shortest_vs_matched_tls_nodes = MA_shortest_vs_matched_tls_nodes ,
##            MA_shortest_vs_matched_tls_left_turns = MA_shortest_vs_matched_tls_left_turns,
##            MA_shortest_vs_matched_tls_right_turns = MA_shortest_vs_matched_tls_right_turns,
##            MA_shortest_vs_matched_tls_crossings = MA_shortest_vs_matched_tls_crossings,
##            MA_shortest_vs_matched_av_n_connections_per_node = MA_shortest_vs_matched_av_n_connections_per_node,
            #    ------------------------------------------------------------------------------------------------------------------Dynamic analysis
##            MA_speeds_inmotion_av = MA_speeds_inmotion_av,
##            MA_waiting_times_share = MA_waiting_times_share,
##            MA_intersections_waiting_times = MA_intersections_waiting_times,
##            MA_left_turns_waiting_times = MA_left_turns_waiting_times,
##            MA_right_turns_waiting_times = MA_right_turns_waiting_times,
##            MA_crossings_waiting_times = MA_crossings_waiting_times,
##            MA_tls_intersections_waiting_times = MA_tls_intersections_waiting_times,
##            MA_tls_left_turns_waiting_times = MA_tls_left_turns_waiting_times,
##            MA_tls_right_turns_waiting_times = MA_tls_right_turns_waiting_times,
##            MA_tls_crossings_waiting_times = MA_tls_crossings_waiting_times,
##            MA_edges_waiting_times_av = MA_edges_waiting_times_av,
##            MA_real_vs_expected_waiting_time_edges = MA_real_vs_expected_waiting_time_edges,
##            MA_real_vs_expected_waiting_time_nodes = MA_real_vs_expected_waiting_time_nodes,
##            MA_real_vs_expected_waiting_time_tls_nodes = MA_real_vs_expected_waiting_time_tls_nodes,
##            MA_real_vs_expected_waiting_time_left_turns = MA_real_vs_expected_waiting_time_left_turns,
##            MA_real_vs_expected_waiting_time_right_turns = MA_real_vs_expected_waiting_time_right_turns,
##            MA_real_vs_expected_waiting_time_crossings = MA_real_vs_expected_waiting_time_crossings,
##            MA_real_vs_expected_waiting_time_left_turns_tls = MA_real_vs_expected_waiting_time_left_turns_tls,
##            MA_real_vs_expected_waiting_time_right_turns_tls = MA_real_vs_expected_waiting_time_right_turns_tls ,
##            MA_real_vs_expected_waiting_time_crossings_tls = MA_real_vs_expected_waiting_time_crossings_tls,
##            MA_real_vs_expected1_waiting_time = MA_real_vs_expected1_waiting_time,
##            MA_real_vs_expected2_waiting_time = MA_real_vs_expected2_waiting_time,
        )
        self.cyclistsdatabase.MA_lengths_route_matched[ids_cyclistsdatabase_res] = MA_lengths_route_matched,
        self.cyclistsdatabase.MA_lengths_within_center[ids_cyclistsdatabase_res] = MA_lengths_within_center,
        self.cyclistsdatabase.MA_speeds_av_matched[ids_cyclistsdatabase_res] = MA_speeds_av_matched,
        self.cyclistsdatabase.MA_numbers_prioritychange[ids_cyclistsdatabase_res] = MA_numbers_prioritychange,
        self.cyclistsdatabase.MA_matched_exclusive[ids_cyclistsdatabase_res] =MA_matched_exclusive ,
        self.cyclistsdatabase.MA_matched_contrary[ids_cyclistsdatabase_res] =MA_matched_contrary ,
        self.cyclistsdatabase.MA_matched_mixed[ids_cyclistsdatabase_res] = MA_matched_mixed,
        self.cyclistsdatabase.MA_matched_lowpriority[ids_cyclistsdatabase_res] = MA_matched_lowpriority,
        self.cyclistsdatabase.MA_matched_nodes[ids_cyclistsdatabase_res] = MA_matched_nodes,
        self.cyclistsdatabase.MA_matched_left_turns[ids_cyclistsdatabase_res] = MA_matched_left_turns,
        self.cyclistsdatabase.MA_matched_right_turns[ids_cyclistsdatabase_res] = MA_matched_right_turns,
        self.cyclistsdatabase.MA_matched_crossings[ids_cyclistsdatabase_res] = MA_matched_crossings,
        self.cyclistsdatabase.MA_matched_tls_nodes[ids_cyclistsdatabase_res] = MA_matched_tls_nodes,
        self.cyclistsdatabase.MA_matched_tls_left_turns[ids_cyclistsdatabase_res] = MA_matched_tls_left_turns,
        self.cyclistsdatabase.MA_matched_tls_right_turns[ids_cyclistsdatabase_res] = MA_matched_tls_right_turns,
        self.cyclistsdatabase.MA_matched_tls_crossings[ids_cyclistsdatabase_res] =MA_matched_tls_crossings ,
        self.cyclistsdatabase.MA_av_n_connections_per_node[ids_cyclistsdatabase_res] = MA_av_n_connections_per_node,
        self.cyclistsdatabase.MA_shortest_length[ids_cyclistsdatabase_res] = MA_shortest_length,
        self.cyclistsdatabase.MA_shortest_vs_matched_numbers_prioritychange[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_numbers_prioritychange,
        self.cyclistsdatabase.MA_shortest_vs_matched_exclusive[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_exclusive,
        self.cyclistsdatabase.MA_shortest_vs_matched_contrary[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_contrary,
        self.cyclistsdatabase.MA_shortest_vs_matched_mixed[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_mixed,
        self.cyclistsdatabase.MA_shortest_vs_matched_lowpriority[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_lowpriority ,
        self.cyclistsdatabase.MA_shortest_vs_matched_nodes[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_nodes,
        self.cyclistsdatabase.MA_shortest_vs_matched_left_turns[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_left_turns,
        self.cyclistsdatabase.MA_shortest_vs_matched_right_turns[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_right_turns,
        self.cyclistsdatabase.MA_shortest_vs_matched_crossings[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_crossings,
        self.cyclistsdatabase.MA_shortest_vs_matched_tls_nodes[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_tls_nodes ,
        self.cyclistsdatabase.MA_shortest_vs_matched_tls_left_turns[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_tls_left_turns,
        self.cyclistsdatabase.MA_shortest_vs_matched_tls_right_turns[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_tls_right_turns,
        self.cyclistsdatabase.MA_shortest_vs_matched_tls_crossings[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_tls_crossings,
        self.cyclistsdatabase.MA_shortest_vs_matched_av_n_connections_per_node[ids_cyclistsdatabase_res] = MA_shortest_vs_matched_av_n_connections_per_node,
        self.cyclistsdatabase.MA_speeds_inmotion_av[ids_cyclistsdatabase_res] = MA_speeds_inmotion_av
        self.cyclistsdatabase.MA_waiting_times_share[ids_cyclistsdatabase_res] = MA_waiting_times_share
        self.cyclistsdatabase.MA_intersections_waiting_times[ids_cyclistsdatabase_res] = MA_intersections_waiting_times
        self.cyclistsdatabase.MA_left_turns_waiting_times[ids_cyclistsdatabase_res] = MA_left_turns_waiting_times
        self.cyclistsdatabase.MA_right_turns_waiting_times[ids_cyclistsdatabase_res] = MA_right_turns_waiting_times
        self.cyclistsdatabase.MA_crossings_waiting_times[ids_cyclistsdatabase_res] = MA_crossings_waiting_times
        self.cyclistsdatabase.MA_tls_intersections_waiting_times[ids_cyclistsdatabase_res] = MA_tls_intersections_waiting_times
        self.cyclistsdatabase.MA_tls_left_turns_waiting_times[ids_cyclistsdatabase_res] = MA_tls_left_turns_waiting_times
        self.cyclistsdatabase.MA_tls_right_turns_waiting_times[ids_cyclistsdatabase_res] = MA_tls_right_turns_waiting_times
        self.cyclistsdatabase.MA_tls_crossings_waiting_times[ids_cyclistsdatabase_res] = MA_tls_crossings_waiting_times
        self.cyclistsdatabase.MA_edges_waiting_times_av[ids_cyclistsdatabase_res] = MA_edges_waiting_times_av
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_edges[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_edges
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_nodes[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_nodes
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_tls_nodes[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_tls_nodes
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_left_turns[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_left_turns
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_right_turns[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_right_turns
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_crossings[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_crossings
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_left_turns_tls[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_left_turns_tls
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_right_turns_tls[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_right_turns_tls 
        self.cyclistsdatabase.MA_real_vs_expected_waiting_time_crossings_tls[ids_cyclistsdatabase_res] = MA_real_vs_expected_waiting_time_crossings_tls
        self.cyclistsdatabase.MA_real_vs_expected1_waiting_time[ids_cyclistsdatabase_res] = MA_real_vs_expected1_waiting_time
        self.cyclistsdatabase.MA_real_vs_expected2_waiting_time[ids_cyclistsdatabase_res] = MA_real_vs_expected2_waiting_time
        
        
        return   True
        
    def   get_average_absolute_difference(self, array):
        values = []
        for i in array:
            for j in array:
                values.append(np.absolute(i-j))

        if len(values) > 1:
            sum = np.sum(values)
            average_absolute_difference = sum/(len(array)*(len(array)-1))
            return average_absolute_difference
        else : 
            return 0
                                    
    
class TripsDatabase(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'Trips database results', 
                             info = 'Table with elaborated trips results',
                             **kwargs):
    
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        
##        self.add_col(SumoIdsConf('User', xmltag = 'id'))
        
        self.add_col(am.ArrayConf('ids_person', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'Person',
                                    info = 'Person ID.',
                                    ))                                          
        
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
                                        

        self.add_col(am.ArrayConf('ids_gender', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    choices = GENDERS,
                                    name = 'P Gender',
                                    info = 'Gender of person.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('age', default = -1,
                                    dtype = np.int32,
                                    groupnames = ['parameters'], 
                                    name = 'P Age',
                                    info = 'Age of the user.',
                                    ))                                        
        
##            self.add_col(am.ArrayConf('ids_occupation', default = OCCUPATIONS['unknown'],
##                                        dtype = np.int32,
##                                        choices = OCCUPATIONS,
##                                        groupnames = ['parameters'], 
##                                        name = 'occupation',
##                                        info = 'Tupe of occupation',
##                                        ))       
                                    
        self.add_col(am.ArrayConf('are_frequent_user', False,
                                    dtype = np.bool_,
                                    groupnames = ['parameters'], 
                                    name = 'P Frequent user',
                                    info = 'If true, this person is a frequent user of the recorded transport mode.',
                                    )) 
                                    
##            self.add_col(am.ArrayConf('numbers_tot_trip_gps', 0,
##                                    dtype = np.int32,
##                                    groupnames = ['results'], 
##                                    name = 'tot. trips',
##                                    symbol = 'N tot GPS',
##                                    info = 'Total number of recorded GPS traces from the person.',
##                                    ))         
                                    
        self.add_col(am.ArrayConf('numbers_tot_trip_mached', 0,
                                    dtype = np.int32,
                                    groupnames = ['results'], 
                                    name = 'tot. trips matched',
                                    symbol = 'P N tot match',
                                    info = 'Total number of correctly matched GPS traces from the person.',
                                    ))                                    
        
        self.add_col(am.ArrayConf('lengths_route_matched', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'matched length',
                                    symbol = 'L match',
                                    unit = 'm',
                                    info = 'length of matched route.',
                                    )) 
                                    
                                    
        self.add_col(am.ArrayConf('lengths_within_center', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share oh lenght inside center',
                                    symbol = 'L center share',
                                    unit = '%',
                                    info = 'Share of matched route length inside the center of the city',
                                    ))
                                    
        self.add_col(am.ArrayConf('speeds_av_matched', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average speed matched',
                                    symbol = 'V av matched',
                                    unit = 'm/s',
                                    info = 'Average speed of matched GPS trace.',
                                    ))                               

        self.add_col(am.ArrayConf('numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'N priority changes',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority per km.',
                                    ))
#-------------------------------------------------------------------------------------------------------
 
        self.add_col(am.ArrayConf('matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in exclusive bike path',
                                    symbol = 'Excl share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in exclusive bike path',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in the contrary direction',
                                    symbol = 'Contr share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in the contrary direction',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in mixed access roads',
                                    symbol = 'Mixed share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in mixed access roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of matched route in lowpriority roads',
                                    symbol = 'Lowprio share match',
                                    unit = '%',
                                    info = 'Share of matched route traveled in lowpriority roads',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes in the matched route  per km',
                                    symbol = 'Nodes match',
                                    unit = '1/km',
                                    info = 'Total number of nodes in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns in the matched route, per km',
                                    symbol = 'Left turns match',
                                    unit = '1/km',
                                    info = 'Total number of left turns in the matched route, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns in the matched route, per km',
                                    symbol = 'Right turns match',
                                    unit = '1/km',
                                    info = 'Total number of right turns in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings in the matched route, per km',
                                    symbol = 'Crossings match',
                                    unit = '1/km',
                                    info = 'Total number of crossings in the matched route, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes in the matched route  per km',
                                    symbol = 'Tls nodes match',
                                    unit = '1/km',
                                    info = 'Total number of traffic light system in the matched route, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns in the matched routes, per km',
                                    symbol = 'Tls left turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns in the matched routes, divided by the total length.',
                                    )) 

        self.add_col(am.ArrayConf('matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns in the matched routes, per km',
                                    symbol = 'Tls right turns match',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns in the matched routes, divided by the total length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings in the matched routes, per km',
                                    symbol = 'Tls crossings match',
                                    unit = '1/km',
                                    info = 'Total number of tls crossing in the matched routes, divided by the total length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'N av connections per node',
                                    info = 'Average number of connections inside the crossed intersections from the matched route. Higher is the number and more big and complex are the crossed intersections.',
                                    ))
                                    
#-------------------------------------------------------------------------------------------------------

        self.add_col(am.ArrayConf('shortest_length', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share tot shortest legth on the tot matched length',
                                    symbol = 'Share shortest length',
                                    unit = '%',
                                    info = 'Share of the total shortest length on the total matched length ',
                                    )) 
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_numbers_prioritychange', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'number of prio. change',
                                    symbol = 'N priority changes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of change in road priority in the matched route minus the total number in the shortest route, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_exclusive', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Exclusive-access links',
                                    symbol = 'Share Excl match vs short',
                                    unit = '%',
                                    info = 'Share of Exclusive roads in the matched length minus share of Exlusive roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_contrary', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only contrary-direction links',
                                    symbol = 'Share Contr match vs short',
                                    unit = '%',
                                    info = 'Share of contrary-direction roads in the matched length minus share of contrary-direction roads in the shortest length.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_mixed', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Mixed-access links',
                                    symbol = 'Share Mixed match vs short',
                                    unit = '%',
                                    info = 'Share of Mixed roads in the matched length minus share of Mixed roads in the shortest length.',
                                    ))    
                                             
        self.add_col(am.ArrayConf('shortest_vs_matched_lowpriority', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share. matched length vs share shortest length - only Lowpriority links',
                                    symbol = 'Share Lowprio match vs short',
                                    unit = '%',
                                    info = 'Share of Lowpriority roads in the matched length minus share of Lowpriority roads in the shortest length.',
                                    ))  
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. nodes per km in the matched route vs Tot. nodes per km in the shortest routes',
                                    symbol = 'Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of nodes per km in the matched length minus total number of nodes per km in the  shortest length.',
                                    )) 
                                      

        self.add_col(am.ArrayConf('shortest_vs_matched_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'Left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of left turns per km in the matched length minus total number of left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('shortest_vs_matched_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'Right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of right turns per km in the matched length minus total number of right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('shortest_vs_matched_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'Crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of crossings per km in the matched length minus total number of crossings per km in the  shortest length.',
                                    ))
                                     
        self.add_col(am.ArrayConf('shortest_vs_matched_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls nodes per km in the matched route vs Tot. tls nodes per km in the shortest routes',
                                    symbol = 'Tls Nodes match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls nodes per km in the matched length minus total number of tls nodes per km in the shortest length.',
                                    ))
                                    
        self.add_col(am.ArrayConf('shortest_vs_matched_tls_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls left turns per km in the matched route vs Tot. left turns per km in the shortest routes',
                                    symbol = 'Tls left turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls left turns per km in the matched length minus total number of tls left turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('shortest_vs_matched_tls_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls right turns per km in the matched route vs Tot. right turns per km in the shortest routes',
                                    symbol = 'Tls right turns match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls right turns per km in the matched length minus total number of tls right turns per km in the  shortest length.',
                                    )) 

        self.add_col(am.ArrayConf('shortest_vs_matched_tls_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Tot. tls crossings per km in the matched route vs Tot. crossings per km in the shortest routes',
                                    symbol = 'Tls crossings match vs short',
                                    unit = '1/km',
                                    info = 'Total number of tls crossings per km in the matched length minus total number of tls crossings per km in the  shortest length.',
                                    ))

        self.add_col(am.ArrayConf('shortest_vs_matched_av_n_connections_per_node', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average number of connections per crossed node from the matched route',
                                    symbol = 'N av connections per node match vs short',
                                    unit = '',
                                    info = 'Difference between the average number of connections inside intersection passed from the matched route, and those from the shortest route.',
                                    ))
#    ------------------------------------------------------------------------------------------------------------------
        self.add_col(am.ArrayConf('are_dynaanalysis', 0,
                                    dtype = np.bool_,
                                    groupnames = ['results'], 
                                    name = 'is Dyn',
                                    symbol = 'Dyn',
                                    info = 'Is this trace suitable for the dynamic analysis?.',
                                    ))

        self.add_col(am.ArrayConf('speeds_inmotion_av', 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Average motion speed matched',
                                    symbol = 'V av in motion matched',
                                    unit = 'm/s',
                                    info = 'Average motion speed of matched routes referred to valid GPS traces used for the dynamic analysis.',
                                    ))


        self.add_col(am.ArrayConf('waiting_times_share', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Share of waiting times',
                                    symbol = 'Wait time share',
                                    unit = '%',
                                    info = 'Share of waiting time of the total trips duration.',
                                    ))
                                    
        self.add_col(am.ArrayConf('intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per inters',
                                    symbol = 'Nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per intersection.',
                                    ))
                                    
                                    
        self.add_col(am.ArrayConf('left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per left turn',
                                    symbol = 'Left turns wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per right turn',
                                    symbol = 'Right turn wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per crossing',
                                    symbol = 'Crossings wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per crossing.',
                                    ))
                                    
        self.add_col(am.ArrayConf('tls_intersections_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls inters',
                                    symbol = 'Tls nodes wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls intersection.',
                                    ))
                                    
        self.add_col(am.ArrayConf('tls_left_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls left turn',
                                    symbol = 'Left turns at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls left turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('tls_right_turns_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls right turn',
                                    symbol = 'Right turn at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls right turn.',
                                    ))
                                    
        self.add_col(am.ArrayConf('tls_crossings_waiting_times', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Av. waiting times per tls crossing',
                                    symbol = 'Crossings at tls wait time av',
                                    unit = 's',
                                    info = 'Average waiting time per tls crossing.',
                                    ))

                                    
        self.add_col(am.ArrayConf('edges_waiting_times_av', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = '% edges waiting times per km',
                                    symbol = 'Edge wait time av',
                                    unit = 's/km',
                                    info = 'Average waiting time registered on edges, per km.',
                                    ))
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_edges', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at edges',
                                    symbol = 'Real VS expected wait time Edges',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at edges, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at nodes',
                                    symbol = 'Real VS expected wait time Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_tls_nodes', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls nodes',
                                    symbol = 'Real VS expected wait time tls Nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls nodes, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_left_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at left turns',
                                    symbol = 'Real VS expected wait time left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_right_turns', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at right turns',
                                    symbol = 'Real VS expected wait time right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_crossings', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at crossings',
                                    symbol = 'Real VS expected wait time crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_left_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls left turns',
                                    symbol = 'Real VS expected wait time tls left turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls left turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_right_turns_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls right turns',
                                    symbol = 'Real VS expected wait time tls right turns',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time at tls right turns, per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected_waiting_time_crossings_tls', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected waiting time at tls crossings',
                                    symbol = 'Real VS expected wait time tls crossings',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time  at tls crossings, per km',
                                    )) 

        self.add_col(am.ArrayConf('real_vs_expected1_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected1 waiting time at edges/connections',
                                    symbol = 'Real VS expected wait time edges/connections',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at connections/edges), per km',
                                    )) 
                                    
        self.add_col(am.ArrayConf('real_vs_expected2_waiting_time', default = -1.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Real vs expected2 waiting time at edges/nodes',
                                    symbol = 'Real VS expected wait time edges/nodes',
                                    unit = 's/km',
                                    info = 'Real waiting time minus expected waiting time (evaluated by considering average waiting times at intersections/edges), per km',
                                    )) 
                                    
  
    
    
class TripsDatabaseAnalyzer(Process):
    def __init__(self, ident, mapmatching, results = None, logger = None, **kwargs):
##        self._init_objman(  ident='trips_database_analyzer', parent=parent, 
##                            name = 'Trips database analyzer',
##                            version = 0.1,
##                            **kwargs)
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
            
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Trips Database Analyzer', 
                            logger = logger,
                            info =""" Elaborated analysis of the GPS trips
                            """
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.year = attrsman.add(cm.AttrConf( 'year',kwargs.get('year',2017),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Year dataset', 
                            info = 'Year of the dataset, fo evaluating the age of the users.',
                            ))
        
                     
        results = self.get_results()  
        results.config(TripsDatabase('tripsdatabase',results),name = 'Results of trips')    
        
        
    def get_results(self):
        return   self._results 
                                    
    def do(self):

        results = self.get_results()
        tripsdatabase = results.tripsdatabase
        self.tripsdatabase = results.tripsdatabase
        self.tripsdatabase.clear()
        persons = self.parent.persons
##            personsresults = results.personsresults
        routesresults_matched = results.routesresults_matched
        routesresults_shortest = results.routesresults_shortest
        
        ids_routesresults_matched = routesresults_matched.get_ids()
        
        ids_person = np.zeros(len(ids_routesresults_matched), dtype = np.int32)
        ids_routesresults_shortest = np.zeros(len(ids_routesresults_matched), dtype = np.int32)
        for i, id_routesresults_matched in zip(list(range(len(ids_routesresults_matched))),ids_routesresults_matched):
            #if self.parent.trips.ids_person[self.parent.trips.get_routes().ids_trip[routesresults_matched.ids_route[id_routesresults_matched]]] > 0:
##                print id_routesresults_matched
            id_route = routesresults_matched.ids_route[id_routesresults_matched]
##                print id_route
            id_trip = self.parent.trips.get_routes().ids_trip[id_route]
##                print id_trip
            id_person = self.parent.trips.ids_person[id_trip]
##                print id_person
            if id_person > -1:
                ids_person[i] = id_person
            
            ids_routesresults_shortest[i] = routesresults_shortest.ids_route.get_id_from_index(routesresults_matched.ids_route[id_routesresults_matched])
        
##        users = np.zeros(len(ids_routesresults_matched)+1)
        ids_gender = np.zeros(len(ids_routesresults_matched)+1)
        age = np.zeros(len(ids_routesresults_matched)+1)
        are_frequent_user = np.zeros(len(ids_routesresults_matched)+1)
        numbers_tot_trip_mached = np.zeros(len(ids_routesresults_matched)+1)
##            dev_st_lengths_tot_route_matched = np.zeros(len(ids_routesresults_matched)+1)
        
        ids_person_correct = ids_person[(ids_person > 0)] 
        ids_routesresults_matched_corrected = ids_routesresults_matched[(ids_person > 0)] 
        
        for id_person_correct, id_routesresults_matched_corrected in zip(ids_person_correct, ids_routesresults_matched_corrected):
            ids_gender[id_routesresults_matched_corrected] = persons.ids_gender[id_person_correct]
            age[id_routesresults_matched_corrected] = self.year - persons.years_birth[id_person_correct]
            are_frequent_user[id_routesresults_matched_corrected] = persons.are_frequent_user[id_person_correct]
            numbers_tot_trip_mached[id_routesresults_matched_corrected] = persons.numbers_tot_trip_mached[id_person_correct]
##                dev_st_lengths_tot_route_matched[id_routesresults_matched_corrected] = personsresults.dev_st_lengths_tot_route_matched[id_person_personsresults_correct]
##            users[id_routesresults_matched_corrected] = persons.ids_sumo[id_person_correct]
        lengths_route_matched = routesresults_matched.distances[ids_routesresults_matched]
        lengths_within_center = routesresults_matched.lengths_within_center[ids_routesresults_matched]/lengths_route_matched*100.
        speeds_av_matched = routesresults_matched.average_speeds[ids_routesresults_matched]
        
        numbers_prioritychange = routesresults_matched.numbers_prioritychange[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_exclusive = routesresults_matched.lengths_exclusive[ids_routesresults_matched]/lengths_route_matched*100.
        matched_contrary = routesresults_matched.lengths_contrary[ids_routesresults_matched]/lengths_route_matched*100.
        matched_mixed = routesresults_matched.lengths_mixed[ids_routesresults_matched]/lengths_route_matched*100.
        matched_lowpriority = routesresults_matched.lengths_low_priority[ids_routesresults_matched]/lengths_route_matched*100.
        matched_nodes = routesresults_matched.numbers_nodes[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_tls_nodes = routesresults_matched.numbers_nodes_tls[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_left_turns = routesresults_matched.numbers_left_turns[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_right_turns = routesresults_matched.numbers_right_turns[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_crossings = routesresults_matched.numbers_crossings[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_tls_left_turns = routesresults_matched.numbers_tls_left_turns[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_tls_right_turns = routesresults_matched.numbers_tls_right_turns[ids_routesresults_matched]/lengths_route_matched*1000.
        matched_tls_crossings = routesresults_matched.numbers_tls_crossings[ids_routesresults_matched]/lengths_route_matched*1000.
        av_n_connections_per_node = routesresults_matched.av_n_connections_per_intersection[ids_routesresults_matched]
        
        lengths_route_shortest = routesresults_shortest.distances[ids_routesresults_shortest]
        shortest_length = routesresults_shortest.distances[ids_routesresults_shortest]/lengths_route_matched*100.
        shortest_vs_matched_numbers_prioritychange = numbers_prioritychange - routesresults_shortest.numbers_prioritychange[ids_routesresults_matched]/lengths_route_shortest*1000.
        shortest_vs_matched_exclusive = matched_exclusive - routesresults_shortest.lengths_exclusive[ids_routesresults_shortest]/lengths_route_shortest*100.
        shortest_vs_matched_contrary = matched_contrary - routesresults_shortest.lengths_contrary[ids_routesresults_shortest]/lengths_route_shortest*100.
        shortest_vs_matched_mixed = matched_mixed - routesresults_shortest.lengths_mixed[ids_routesresults_shortest]/lengths_route_shortest*100.
        shortest_vs_matched_lowpriority = matched_lowpriority - routesresults_shortest.lengths_low_priority[ids_routesresults_shortest]/lengths_route_shortest*100.  
        shortest_vs_matched_nodes = matched_nodes - routesresults_shortest.numbers_nodes[ids_routesresults_shortest]/lengths_route_shortest*1000.  
        shortest_vs_matched_tls_nodes =  matched_tls_nodes - routesresults_shortest.numbers_nodes_tls[ids_routesresults_shortest]/lengths_route_shortest*1000.   
        shortest_vs_matched_left_turns = matched_left_turns - routesresults_shortest.numbers_left_turns[ids_routesresults_shortest]/lengths_route_shortest*1000.   
        shortest_vs_matched_right_turns = matched_right_turns - routesresults_shortest.numbers_right_turns[ids_routesresults_shortest]/lengths_route_shortest*1000. 
        shortest_vs_matched_crossings = matched_crossings - routesresults_shortest.numbers_crossings[ids_routesresults_shortest]/lengths_route_shortest*1000. 
        shortest_vs_matched_tls_left_turns = matched_tls_left_turns - routesresults_shortest.numbers_tls_left_turns[ids_routesresults_shortest]/lengths_route_shortest*1000.   
        shortest_vs_matched_tls_right_turns = matched_tls_right_turns - routesresults_shortest.numbers_tls_right_turns[ids_routesresults_shortest]/lengths_route_shortest*1000. 
        shortest_vs_matched_tls_crossings = matched_tls_crossings - routesresults_shortest.numbers_tls_crossings[ids_routesresults_shortest]/lengths_route_shortest*1000. 
        shortest_vs_matched_av_n_connections_per_node = av_n_connections_per_node - routesresults_shortest.av_n_connections_per_intersection[ids_routesresults_shortest]
        
        are_dynaanalysis = routesresults_matched.distances_real[ids_routesresults_matched] > 0
        ids_routesresults_matched_dyn = ids_routesresults_matched[(are_dynaanalysis == 1)]
        lengths_route_matched_real = routesresults_matched.distances_real[ids_routesresults_matched]
        lengths_route_matched_dyn = lengths_route_matched_real[(are_dynaanalysis == 1)]
        ids_routesresults_matched_dyn_tls = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_tls[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_left_turns = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_left_turn[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_right_turns = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_right_turn[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_crossings = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_crossing[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_left_turns_tls = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_left_turn_tls[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_right_turns_tls = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_right_turn_tls[ids_routesresults_matched_dyn]>0)]
        ids_routesresults_matched_dyn_crossings_tls = ids_routesresults_matched_dyn[(routesresults_matched.n_times_wait_crossing_tls[ids_routesresults_matched_dyn]>0)]
        
        speeds_inmotion_av = np.zeros(len(ids_routesresults_matched)+1)
        waiting_times_share = np.zeros(len(ids_routesresults_matched)+1)
        intersections_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        left_turns_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        right_turns_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        crossings_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        tls_intersections_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        tls_left_turns_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        tls_right_turns_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        tls_crossings_waiting_times = np.zeros(len(ids_routesresults_matched)+1)
        edges_waiting_times_av = np.zeros(len(ids_routesresults_matched)+1)
        
        real_vs_expected_waiting_time_edges = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_nodes = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_tls_nodes = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_left_turns = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_right_turns = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_crossings= np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_left_turns_tls = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_right_turns_tls= np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected_waiting_time_crossings_tls = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected1_waiting_time = np.zeros(len(ids_routesresults_matched)+1)
        real_vs_expected2_waiting_time = np.zeros(len(ids_routesresults_matched)+1)
        
        speeds_inmotion_av[ids_routesresults_matched_dyn] = lengths_route_matched_dyn/routesresults_matched.times_inmotion[ids_routesresults_matched_dyn]
        waiting_times_share[ids_routesresults_matched_dyn] = routesresults_matched.times_wait[ids_routesresults_matched_dyn]/routesresults_matched.durations_real[ids_routesresults_matched_dyn]*100.0
        intersections_waiting_times[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait[ids_routesresults_matched_dyn]-routesresults_matched.times_wait_edges[ids_routesresults_matched_dyn])/routesresults_matched.numbers_nodes[ids_routesresults_matched_dyn]
        left_turns_waiting_times[ids_routesresults_matched_dyn_left_turns] = routesresults_matched.times_wait_left_turn[ids_routesresults_matched_dyn_left_turns]/routesresults_matched.numbers_left_turns[ids_routesresults_matched_dyn_left_turns]
        right_turns_waiting_times[ids_routesresults_matched_dyn_right_turns] = routesresults_matched.times_wait_right_turn[ids_routesresults_matched_dyn_right_turns]/routesresults_matched.numbers_right_turns[ids_routesresults_matched_dyn_right_turns]
        crossings_waiting_times[ids_routesresults_matched_dyn_crossings] = routesresults_matched.times_wait_crossing[ids_routesresults_matched_dyn_crossings]/routesresults_matched.numbers_crossings[ids_routesresults_matched_dyn_crossings]
        tls_intersections_waiting_times[ids_routesresults_matched_dyn_tls] = routesresults_matched.times_wait_tls[ids_routesresults_matched_dyn_tls]/routesresults_matched.numbers_nodes_tls[ids_routesresults_matched_dyn_tls]
        tls_left_turns_waiting_times[ids_routesresults_matched_dyn_left_turns_tls] = routesresults_matched.times_wait_left_turn_tls[ids_routesresults_matched_dyn_left_turns_tls]/routesresults_matched.numbers_tls_left_turns[ids_routesresults_matched_dyn_left_turns_tls]
        tls_right_turns_waiting_times[ids_routesresults_matched_dyn_right_turns_tls] = routesresults_matched.times_wait_right_turn_tls[ids_routesresults_matched_dyn_right_turns_tls]/routesresults_matched.numbers_tls_right_turns[ids_routesresults_matched_dyn_right_turns_tls]
        tls_crossings_waiting_times[ids_routesresults_matched_dyn_crossings_tls] = routesresults_matched.times_wait_crossing_tls[ids_routesresults_matched_dyn_crossings_tls]/routesresults_matched.numbers_tls_crossings[ids_routesresults_matched_dyn_crossings_tls]
        
        edges_waiting_times_av[ids_routesresults_matched_dyn] = routesresults_matched.times_wait_edges[ids_routesresults_matched_dyn]/lengths_route_matched_dyn*1000.0
        
        ids_trip_dynaanalysis = self.parent.trips.get_routes().ids_trip[routesresults_matched.ids_route[ids_routesresults_matched_dyn]]
       
        expected_waiting_times_tot_route_matched1 = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched2 = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_nodes = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_tls_nodes = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_edges = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_left_turn = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_left_turn_tls = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_right_turn = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_right_turn_tls = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_crossing = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_crossing_tls = np.zeros(len(ids_routesresults_matched)+1)
        
        expected_waiting_times_tot_route_matched_left_turn[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_left_turn[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_right_turn[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_right_turn[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_crossing[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_crossing[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_left_turn_tls[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_left_turn_tls[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_right_turn_tls[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_right_turn_tls[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_crossing_tls[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_crossing_tls[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_nodes[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_nodes[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_tls_nodes[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_tls_nodes[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched_edges[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched_edges[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched1[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched1[ids_routesresults_matched_dyn]
        expected_waiting_times_tot_route_matched2[ids_routesresults_matched_dyn] = routesresults_matched.expected_waiting_times_tot_route_matched2[ids_routesresults_matched_dyn]
        
        real_vs_expected_waiting_time_edges[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_edges[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_edges[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_nodes[ids_routesresults_matched_dyn] = ((routesresults_matched.times_wait[ids_routesresults_matched_dyn]-routesresults_matched.times_wait_edges[ids_routesresults_matched_dyn]) - expected_waiting_times_tot_route_matched_nodes[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_tls_nodes[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_tls[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_tls_nodes[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_left_turns[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_left_turn[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_left_turn[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_right_turns[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_right_turn[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_right_turn[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_crossings[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_crossing[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_crossing[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_left_turns_tls[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_left_turn_tls[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_left_turn_tls[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_right_turns_tls[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_right_turn_tls[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_right_turn_tls[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected_waiting_time_crossings_tls[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait_crossing_tls[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched_crossing_tls[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected1_waiting_time[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched1[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        real_vs_expected2_waiting_time[ids_routesresults_matched_dyn] = (routesresults_matched.times_wait[ids_routesresults_matched_dyn] - expected_waiting_times_tot_route_matched2[ids_routesresults_matched_dyn])/lengths_route_matched_dyn*1000.0
        
        #Insert a -1 for empty cells
        ids_no_dyn = ids_routesresults_matched[(are_dynaanalysis == 0)]
        ids_no_left_turns = ids_routesresults_matched[(matched_left_turns == 0)]
        ids_no_right_turns = ids_routesresults_matched[(matched_right_turns == 0)]
        ids_no_crossings =  ids_routesresults_matched[(matched_crossings == 0)]
        ids_no_tls = ids_routesresults_matched[(matched_tls_nodes == 0)]
        ids_no_tls_left_turns = ids_routesresults_matched[(matched_tls_left_turns == 0)]
        ids_no_tls_right_turns = ids_routesresults_matched[(matched_tls_right_turns == 0)]
        ids_no_tls_crossings = ids_routesresults_matched[(matched_tls_crossings == 0)]
        
        left_turns_waiting_times[ids_no_left_turns] = -1.
        right_turns_waiting_times[ids_no_right_turns] = -1.
        crossings_waiting_times[ids_no_crossings] = -1.
        tls_intersections_waiting_times[ids_no_tls] = -1.
        tls_left_turns_waiting_times[ids_no_tls_left_turns] = -1.
        tls_right_turns_waiting_times[ids_no_tls_right_turns] = -1.
        tls_crossings_waiting_times[ids_no_tls_crossings] = -1.
        
        real_vs_expected_waiting_time_left_turns[ids_no_left_turns] = -1.
        real_vs_expected_waiting_time_right_turns[ids_no_right_turns] = -1.
        real_vs_expected_waiting_time_crossings[ids_no_crossings] = -1.
        real_vs_expected_waiting_time_tls_nodes[ids_no_tls] = -1.
        real_vs_expected_waiting_time_left_turns_tls[ids_no_tls_left_turns] = -1.
        real_vs_expected_waiting_time_right_turns_tls[ids_no_tls_right_turns] = -1.
        real_vs_expected_waiting_time_crossings_tls[ids_no_tls_crossings] = -1.

        speeds_inmotion_av[ids_no_dyn] = -1.
        waiting_times_share[ids_no_dyn] = -1.
        intersections_waiting_times[ids_no_dyn] = -1.
        left_turns_waiting_times[ids_no_dyn] = -1.
        right_turns_waiting_times[ids_no_dyn] = -1.
        crossings_waiting_times[ids_no_dyn] = -1.
        tls_intersections_waiting_times[ids_no_dyn] = -1.
        tls_left_turns_waiting_times[ids_no_dyn] = -1.
        tls_right_turns_waiting_times[ids_no_dyn] = -1.
        tls_crossings_waiting_times[ids_no_dyn] = -1.
        edges_waiting_times_av[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_edges[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_nodes[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_tls_nodes[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_left_turns[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_right_turns[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_crossings[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_left_turns_tls[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_right_turns_tls[ids_no_dyn] = -1.
        real_vs_expected_waiting_time_crossings_tls[ids_no_dyn] = -1.
        real_vs_expected1_waiting_time[ids_no_dyn] = -1.
        real_vs_expected2_waiting_time[ids_no_dyn] = -1.
        
        
                 
        ids_tripsdatabase = self.tripsdatabase.add_rows(  #User = users,
                                            ids_person = ids_person,
                                            ids_gender = ids_gender[ids_routesresults_matched],
                                            age = age[ids_routesresults_matched],
                                            are_frequent_user = are_frequent_user[ids_routesresults_matched],
                                            numbers_tot_trip_mached = numbers_tot_trip_mached[ids_routesresults_matched],
                                            lengths_route_matched = lengths_route_matched,
##                                                dev_st_lengths_tot_route_matched = dev_st_lengths_tot_route_matched[ids_routesresults_matched],
                                            lengths_within_center = lengths_within_center,
                                            speeds_av_matched = speeds_av_matched,
                                            
                                            numbers_prioritychange = numbers_prioritychange,
                                            matched_exclusive = matched_exclusive,
                                            matched_contrary = matched_contrary,
                                            matched_mixed = matched_mixed,
                                            matched_lowpriority = matched_lowpriority,
                                            matched_nodes = matched_nodes,
                                            matched_tls_nodes = matched_tls_nodes,
                                            matched_left_turns = matched_left_turns,
                                            matched_right_turns = matched_right_turns,
                                            matched_crossings = matched_crossings,
                                            matched_tls_left_turns = matched_tls_left_turns,
                                            matched_tls_right_turns = matched_tls_right_turns,
                                            matched_tls_crossings = matched_tls_crossings,
                                            av_n_connections_per_node = av_n_connections_per_node,
                                            
                                            shortest_length = shortest_length,
                                            shortest_vs_matched_numbers_prioritychange = shortest_vs_matched_numbers_prioritychange,
                                            shortest_vs_matched_exclusive = shortest_vs_matched_exclusive,
                                            shortest_vs_matched_contrary = shortest_vs_matched_contrary,
                                            shortest_vs_matched_mixed = shortest_vs_matched_mixed,
                                            shortest_vs_matched_lowpriority = shortest_vs_matched_lowpriority,
                                            shortest_vs_matched_nodes = shortest_vs_matched_nodes,
                                            shortest_vs_matched_tls_nodes = shortest_vs_matched_tls_nodes,
                                            shortest_vs_matched_left_turns = shortest_vs_matched_left_turns,
                                            shortest_vs_matched_right_turns = shortest_vs_matched_right_turns,
                                            shortest_vs_matched_crossings = shortest_vs_matched_crossings,
                                            shortest_vs_matched_tls_left_turns = shortest_vs_matched_tls_left_turns,
                                            shortest_vs_matched_tls_right_turns = shortest_vs_matched_tls_right_turns,
                                            shortest_vs_matched_tls_crossings = shortest_vs_matched_tls_crossings,
                                            shortest_vs_matched_av_n_connections_per_node = shortest_vs_matched_av_n_connections_per_node,
                                            
                                            are_dynaanalysis = are_dynaanalysis,
                                            speeds_inmotion_av = speeds_inmotion_av[ids_routesresults_matched],
                                            waiting_times_share = waiting_times_share[ids_routesresults_matched],
                                            intersections_waiting_times = intersections_waiting_times[ids_routesresults_matched],
                                            left_turns_waiting_times = left_turns_waiting_times[ids_routesresults_matched],
                                            right_turns_waiting_times = right_turns_waiting_times[ids_routesresults_matched],
                                            crossings_waiting_times = crossings_waiting_times[ids_routesresults_matched],
                                            tls_intersections_waiting_times = tls_intersections_waiting_times[ids_routesresults_matched],
                                            tls_left_turns_waiting_times = tls_left_turns_waiting_times[ids_routesresults_matched],
                                            tls_right_turns_waiting_times = tls_right_turns_waiting_times[ids_routesresults_matched],
                                            tls_crossings_waiting_times = tls_crossings_waiting_times[ids_routesresults_matched],
                                            edges_waiting_times_av = edges_waiting_times_av[ids_routesresults_matched],
                                            
                                            real_vs_expected_waiting_time_edges = real_vs_expected_waiting_time_edges[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_nodes = real_vs_expected_waiting_time_nodes[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_tls_nodes = real_vs_expected_waiting_time_tls_nodes[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_left_turns = real_vs_expected_waiting_time_left_turns[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_right_turns = real_vs_expected_waiting_time_right_turns[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_crossings = real_vs_expected_waiting_time_crossings[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_left_turns_tls =real_vs_expected_waiting_time_left_turns_tls[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_right_turns_tls = real_vs_expected_waiting_time_right_turns_tls[ids_routesresults_matched],
                                            real_vs_expected_waiting_time_crossings_tls = real_vs_expected_waiting_time_crossings_tls[ids_routesresults_matched],
                                            real_vs_expected1_waiting_time = real_vs_expected1_waiting_time[ids_routesresults_matched],
                                            real_vs_expected2_waiting_time = real_vs_expected2_waiting_time[ids_routesresults_matched],
                            )
        

        return   True 
    
class Routesanalyzer(Process):
    def __init__(self, ident, mapmatching, results = None,  logger = None, **kwargs):
        print('Routesanalyzer.__init__')
        
        # TODO: let this be independent, link to it or child??
        
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
            
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'Routes Analyzer', 
                            logger = logger,
                            info ='Determines attributes of matched route and alternative routes.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        self.priority_max_low = attrsman.add(cm.AttrConf( 'priority_max_low',kwargs.get('priority_max_low',6),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max. low priority', 
                            info = 'Maximum priority of an edge, such that it is still considered low priority.',
                            ))
                            
        self.timeloss_intersection = attrsman.add(cm.AttrConf( 'timeloss_intersection',kwargs.get('timeloss_intersection',5.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Timeloss intersection', 
                            info = 'Estimated timeloss at intersections due to waiting or reduction of speed. This value is used to estimate travel time of route alternatives.',
                            ))
        
        self.timeloss_tl = attrsman.add(cm.AttrConf( 'timeloss_tl',kwargs.get('timeloss_tl',15.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Timeloss TL', 
                            info = 'Additional estimated timeloss at traffic light intersections due to waiting at the red light. This value is used to estimate travel time of route alternatives.',
                            ))
                             
                            
        self.edgedurations_tot_min = attrsman.add(cm.AttrConf( 'edgedurations_tot_min',kwargs.get('edgedurations_tot_min',10.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'min tot. edge duration', 
                            unit = 's',
                            info = 'Minimum total duration of all users in an edge in order to calculate average speed. A too small value distors the results.',
                            ))  
                            
        self.is_analyze_lengths_within_center = attrsman.add(cm.AttrConf( 'is_analyze_lengths_within_center',kwargs.get('is_analyze_lengths_within_center', False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Analyze lenghts within center', 
                            info = 'If True, for each route will be analyzed the total lenght inside edges that start or end in the center zone.',
                            )) 
        scenario = self.parent.get_scenario()
        self.zones = scenario.landuse.zones

        
        if   len(self.zones) > 0:                          
            self.center_zone = attrsman.add(cm.AttrConf(  'center_zone', kwargs.get('center_zone', self.zones.ids_sumo[0]),
                                            groupnames = ['options'], 
                                            choices = self.zones.ids_sumo,
                                            name = 'Center zone', 
                                            info = 'Center zone for individuating the percentage of matched trips in the center area.',
                                            ))
        
        self.is_speedana = attrsman.add(cm.AttrConf( 'is_speedana',kwargs.get('is_speedana', False),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Speed analysis?', 
                            info = 'If True, speed analysis will be performed (can take some time and use a lot of memory).',
                            )) 
        
        self.edgespeed_max = attrsman.add(cm.AttrConf( 'edgespeed_max',kwargs.get('edgespeed_max',14.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max edge speed', 
                            unit = 'm/s',
                            info = 'Edge speed estimates will be clipped at this speed.',
                            ))
                              
        self.pointspeed_max = attrsman.add(cm.AttrConf( 'pointspeed_max',kwargs.get('edgespeed_max',14.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Max point speed', 
                            unit = 'm/s',
                            info = 'Point speed estimates will be clipped at this speed.',
                            ))  
                            
        self.waitspeed = attrsman.add(cm.AttrConf( 'waitspeed',kwargs.get('waitspeed',1.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Wait speed', 
                            unit = 'm/s',
                            info = 'Below this speed, the vehicle is considered waiting.',
                            ))  
        
        self.junctionrange = attrsman.add(cm.AttrConf( 'junctionrange',kwargs.get('junctionrange',40.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Junction range', 
                            unit = 'm',
                            info = 'This is the lenght of the last part of edges where waiting times are considered part of the intersection where this edge is directed.',
                            ))  
                            
        self.n_point_min = attrsman.add(cm.AttrConf( 'n_point_min',kwargs.get('n_point_min',5),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. number of points', 
                            info = """Only trips with a number of points greater than this minimum number will receive wait times of any kind.""",
                            ))
        self.n_arc_min = attrsman.add(cm.AttrConf( 'n_arc_min',kwargs.get('n_arc_min',3),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Min. number of arcs', 
                            info = """Only trips with a number of arcs greater than this minimum number will receive wait times of any kind. Both edges and connections are considered arcs""",
                            ))
                              

        self.n_external_points = attrsman.add(cm.AttrConf( 'n_external_points',kwargs.get('n_external_points',7),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'External points', 
                            info = """Number of external trip points to be deleted (either at the beginning or at the end of the trip) if there are some starting and ending ambiguites (points collocated after his successive point).""",
                            ))  
##        self.lengths_waitedge_min = attrsman.add(cm.AttrConf( 'lengths_waitedge_min',kwargs.get('lengths_waitedge_min',20.0),
##                            groupnames = ['options'], 
##                            perm='rw', 
##                            name = 'Min. wait-edge length', 
##                            unit = 'm',
##                            info = """Only edges with a length greater than this minimum length will receive wait times of any kind. 
##                                      these long edges will also reveive the wait times measured in successive shorter edges.""",
##                            ))  
                            
        
##        self.is_nodeana = attrsman.add(cm.AttrConf( 'is_nodeana',kwargs.get('is_nodeana',False),
##                            groupnames = ['options'], 
##                            perm='rw', 
##                            name = 'Node analysis?', 
##                            info = 'If True, Node analysis will be performed. This is mainly the calculation of the average waiting time at different types of junctions.',
##                            ))
                            
        self.flowfactor = attrsman.add(cm.AttrConf( 'flowfactor',kwargs.get('flowfactor',10.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Flow factor', 
                            info = 'This is the factor which will be multiplied with the edge probability to estimate the absolute vehicle flows.',
                            )) 
         
        #self.is_oldversion = attrsman.add(cm.AttrConf( 'is_oldversion',kwargs.get('is_oldversion',False),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Old mapmatch?', 
        #                    info = 'If True, analyze for old mapmatch version.',
        #                    ))
        
                                                
        results = self.get_results()
        results.config(Routesresults('routesresults_matched',results,name = 'Results of matched routes'),name = 'Results of matched routes')
        results.config(Routesresults('routesresults_shortest',results,name = 'Results of shortest routes'),name = 'Results of shortest routes')
        results.config(Routesresults('routesresults_fastest',results,name = 'Results of fastest routes'),name = 'Results of fastest routes')
        
        results.config(Routesresults('routesresults_matched_nonoverlap',results,name = 'Results of matched, non-overlapping routes'),name = 'Results of matched routes using only edges which do not overlap with the shortest route.')
        results.config(Routesresults('routesresults_shortest_nonoverlap',results,name = 'Results of shortest, non-overlapping routes'),name = 'Results of shortest routes using only edges which do not overlap with the matched route.')
        
        results.config(Connectionsresults('connectionsresults',results), name = 'Results associated to edges')
        results.config(Edgesresults('edgesresults',results), name = 'Results associated to connections')
        results.config(Nodesresults('nodesresults',results), name = 'Results associated to node')
        
                            
    def get_results(self):
        return   self._results 
    
    
    
    def do(self):
        print('Routesanalyzer.do')
        # results
        results = self.get_results()
        routesresults_shortest = results.routesresults_shortest
        routesresults_fastest = results.routesresults_fastest
        routesresults_matched = results.routesresults_matched
        
        routesresults_shortest_nonoverlap = results.routesresults_shortest_nonoverlap
        routesresults_matched_nonoverlap = results.routesresults_matched_nonoverlap
        
        connectionsresults = results.connectionsresults
        edgesresults = results.edgesresults
        nodesresults = results.nodesresults
        
        
        # links
        mapmatching = self.parent
        trips = mapmatching.trips
        
        routes = trips.get_routes()
        scenario = mapmatching.get_scenario()
        edges = scenario.net.edges
        ids_edge = edges.get_ids()
        lanes = scenario.net.lanes
        nodes = scenario.net.nodes
        zones = scenario.landuse.zones
        connections = scenario.net.connections
        ids_connection = connections.get_ids()
        net = scenario.net
        id_mode_ped = scenario.net.modes.get_id_mode('pedestrian')
        get_pos_from_coord = edges.get_pos_from_coord
        
        vtypes = scenario.demand.vtypes
        points = mapmatching.points
        timestamps = points.timestamps
        
        priority_max_low = self.priority_max_low
        timeloss_intersection = self.timeloss_intersection
        timeloss_tl = self.timeloss_tl
        
        distancesmap = mapmatching.get_distancesmap(is_check_lanes = False)
        accesslevelsmap = mapmatching.get_accesslevelsmap()
        priorities = edges.priorities
        ids_tonode = edges.ids_tonode
        nodetypes = scenario.net.nodes.types
        tlstype = nodetypes.choices['traffic_light']
        ids_tls = scenario.net.nodes.ids_tls
        
        ids_trip_sel = trips.get_ids_selected()
        
        ids_route_sel = trips.ids_route_matched[ids_trip_sel]
        inds_valid = np.flatnonzero(ids_route_sel>0)
        
        ids_route = ids_route_sel[inds_valid]
        ids_trip = ids_trip_sel[inds_valid]
        ids_route_shortest = trips.ids_route_shortest[ids_trip]
        ids_route_fastest = trips.ids_route_fastest[ids_trip]
        #print '  ids_route_fastest',ids_route_fastest
        #print '  ids_route_fastest',trips.ids_route_fastest.get_value()
        ids_vtype = trips.ids_vtype[ids_trip]
        ids_mode = vtypes.ids_mode[ids_vtype]
        
        
        #ind = 0
        if len(ids_trip)==0:
            print('WARNING: no trips selected.')
            return True
        
        print('  analyzing %d trips'%len(ids_trip))
        
        routesresults_shortest.clear()
        routesresults_matched.clear()
        routesresults_fastest.clear()
        routesresults_shortest_nonoverlap.clear()
        routesresults_matched_nonoverlap.clear()
        nodesresults.clear()
        edgesresults.clear()
        connectionsresults.clear()
        ids_res = routesresults_matched.add_rows(n=len(ids_trip),)
        routesresults_shortest.add_rows( ids =ids_res)
        routesresults_fastest.add_rows( ids =ids_res)
        routesresults_matched_nonoverlap.add_rows( ids =ids_res)
        routesresults_shortest_nonoverlap.add_rows( ids =ids_res)
        
        ids_edgeresmap = edgesresults.init_for_routes(routes)
        ids_node = nodes.get_ids()
        
        wait_times_node = np.zeros(max(ids_node)+1, dtype = np.float32)
        n_wait_times_node = np.zeros(max(ids_node)+1, dtype = np.float32)
        node_n_matched_for_speed_analysis = np.zeros(max(ids_node)+1, dtype = np.float32)
        node_n_matched = np.zeros(max(ids_node)+1, dtype = np.float32)
        
        wait_times_conn = np.zeros(max(ids_connection)+1, dtype = np.float32)
        n_wait_times_conn = np.zeros(max(ids_connection)+1, dtype = np.int32)
        tls_wait_times_conn = np.zeros(max(ids_connection)+1, dtype = np.float32)
        n_tls_wait_times_conn = np.zeros(max(ids_connection)+1, dtype = np.int32)
        conn_n_matched = np.zeros(max(ids_connection)+1, dtype = np.int32)
        conn_n_matched_for_speed_analysis = np.zeros(max(ids_connection)+1, dtype = np.int32)
        
        wait_times_edge = np.zeros(max(ids_edge)+1, dtype = np.float32)
        n_wait_times_edge  = np.zeros(max(ids_edge)+1, dtype = np.int32)
        wait_times_edge_at_int = np.zeros(max(ids_edge)+1, dtype = np.float32)
        n_wait_times_edge_at_int = np.zeros(max(ids_edge)+1, dtype = np.int32)
        tls_wait_times_edge_at_int  = np.zeros(max(ids_edge)+1, dtype = np.float32)
        n_tls_wait_times_edge_at_int  = np.zeros(max(ids_edge)+1, dtype = np.float32)
        edge_n_matched_for_speed_analysis  = np.zeros(max(ids_edge)+1, dtype = np.float32)
        
        edge_times  = np.zeros(max(ids_edge)+1, dtype = np.float32)

        distances_real = np.zeros(max(ids_route)+1, dtype = np.float32)
        durations_real = np.zeros(max(ids_route)+1, dtype = np.float32)
        average_speeds = np.zeros(max(ids_route)+1, dtype = np.float32)
        average_speeds_real = np.zeros(max(ids_route)+1, dtype = np.float32)

 
        edge_speeds = []
        edge_speeds_in_motion = []
        for i in range(max(ids_edge)+1): 
            edge_speeds.append([])
            edge_speeds_in_motion.append([])
        connections_waitingtimes = []
        nodes_waitingtimes = []
        for i in range(max(ids_connection)+1): 
            connections_waitingtimes.append([])
        for i in range(max(ids_node)+1): 
            nodes_waitingtimes.append([])
        good_traces = 0
        bad_traces = 0
        all_connections = []
        all_edges = []
        for id_trip, id_route_matched, id_route_shortest, id_route_fastest, id_mode, id_res, dist_gps, duration_gps\
                in zip( ids_trip, 
                        ids_route, 
                        ids_route_shortest, 
                        ids_route_fastest,
                        ids_mode, 
                        ids_res, 
                        trips.lengths_gpsroute_matched[ids_trip], 
                        trips.durations_route_matched[ids_trip]):
            
            
            if 1:
                print(70*'-')
                print('id_trip',id_trip,'Routes: id_matched',id_route_matched,'id_shortest',id_route_shortest,'id_fastest',id_route_fastest)
                
            #ids_point = trips.ids_points[id_trip]
            distances = distancesmap[id_mode]
            
            # here we cut of first edge for analysis
            ids_edge_matched = np.array(routes.ids_edges[id_route_matched], dtype = np.int32)
            ids_edge = routes.ids_edges[id_route_matched]
            
            #if self.is_oldversion:
            ids_points_edgeend = trips.ids_points_edgeend[id_trip]
            #print '  ids_points_edgeend',ids_points_edgeend
            #else:
            #    ids_points_edgeend = np.array(trips.ids_points_edgeend[id_trip][1:], dtype = np.int32)
            
            #print '  pre-detect edges for each point'  
            # by scanning the ids_points_ee array
                
            #ids_points = np.array(trips.ids_points[id_trip], dtype = np.int32)
            ids_points = trips.ids_points[id_trip]
            coords = points.coords[ids_points]
            n_points = len(ids_points)
            ind_points = 0
            ids_pointedge = []
            pointsposition = []
            basepos = 0.0
            
            ####################################################################
            #pointposition of each trip point in the matched edge
            ind_points = ids_points_edgeend.index(ids_points_edgeend[0])
            ind_start = ind_points
            for id_edge, id_point_ee in zip(ids_edge_matched, ids_points_edgeend[1:]):
                #print '    ind_points',ind_points,'id_edge',id_edge,'id_point_ee',id_point_ee,'ids_points[ind_points]',ids_points[ind_points]
                edgesresults.numbers_tot_matched[ids_edgeresmap[id_edge]] += 1
                if id_point_ee > -1:
                    is_cont = True
                    while is_cont:
                        ids_pointedge.append(id_edge)
                        
                        pointsposition.append(basepos + get_pos_from_coord(id_edge, coords[ind_points]))
                        
                        if ind_points < n_points:
                            is_cont = ids_points[ind_points] != id_point_ee
                            
                                
                        else:
                            is_cont = False
                        #print '  ids_pointedge',is_cont,ids_pointedge
                        ind_points += 1
                        
                basepos += edges.lengths[id_edge]
            #################################################################### 
##            ids_points = ids_points[ind_start:ind_points]
##            n_points = len(ids_points)
            #print '  ids_points_edgeend',ids_points_edgeend
            #print '  ids_pointedge',ids_pointedge
            #print '  len(ids_pointedge),n_points',len(ids_pointedge),n_points
            
                
            # 
##            times = points.timestamps[ids_points]
            accesslevels_matched = accesslevelsmap[id_mode][ids_edge_matched]
            priorities_matched = priorities[ids_edge_matched]
            nodetypes_matched = nodetypes[ids_tonode[ids_edge_matched]]
            
            
            #print '    len(ids_edge_matched)',len(ids_edge_matched)
            #print '    len(ids_points_edgeend)',len(ids_points_edgeend)
            #print '    ids_edge_matched',ids_edge_matched
            #print '    accesslevels_matched',accesslevels_matched
            #print '    tlstype',tlstype
            #print '    accesslevels_mixed',accesslevels_matched==1
            #print '    accesslevels_mixed',np.flatnonzero(accesslevels_matched==1)
            #print '    distances',
            #print '    nodetypes_matched',nodetypes_matched
            if len(ids_points_edgeend)>1:
                dist_matched = np.sum(distances[ids_edge_matched])
                duration_matched = timestamps[ids_points_edgeend[-1]] - timestamps[ids_points_edgeend[0]]
                n_nodes_matched = len(nodetypes_matched)
                n_tls_matched = np.sum(nodetypes_matched==tlstype)
                lineduration_matched = duration_matched-n_nodes_matched*timeloss_intersection-n_tls_matched*timeloss_tl
                linespeed_matched = dist_matched/lineduration_matched
                average_speed = dist_matched/duration_matched
                
            else:
                dist_matched = 0.0
                duration_matched = 0.0
                n_nodes_matched = 0
                n_tls_matched = 0
                lineduration_matched = 0.0
                linespeed_matched = 0.0
            
            
            ###SPEED ANALYSIS##################################################
            # do edge by edge analyses if at least more than 1 edge
            n_matched = len(ids_edge)
            
            # do speed analyses only if there are edges

            #shape of the stretched route
            stretch_shape = []
            #list of arc traveled, taking into account both edges and connections
            ids_arc = []
            #list of arc dists
            dists = []
            
##                possible_connections_pre = []
##                possible_connections = []
            are_connection = []
            id_edge_pre = 0
            number_left_turns = 0
            number_right_turns = 0
            number_crossings = 0
            number_u_turns = 0
            tls_number_left_turns = 0
            tls_number_right_turns = 0
            tls_number_crossings = 0
            lenght_inside_center = 0
            n_connections_per_intersection = 0
            for id_edge in ids_edge:
##                    print 'analyzing edge', id_edge
                node_n_matched[edges.ids_tonode[id_edge]] += 1
                #print len(connections.ids_node[ids_connection][(connections.ids_node[ids_connection] ==  edges.ids_tonode[id_edge])])
                n_connections_per_intersection += nodes.n_connections[edges.ids_tonode[id_edge]]
                if self.is_analyze_lengths_within_center:
                    if id_edge in zones.ids_edges_orig[zones.ids_sumo.get_id_from_index(self.center_zone)] or id_edge in zones.ids_edges_dest[zones.ids_sumo.get_id_from_index(self.center_zone)]:
                        lenght_inside_center += distances[id_edge]
                        #print 'lenght_inside_center', lenght_inside_center
                if id_edge_pre != 0:
                    shape0 = edges.shapes[id_edge][0][:2]
                    shape1 = edges.shapes[id_edge_pre][-1][:2]
##                        print 'shape0', shape0, 'shape1', shape1
                    distance_connection = np.sqrt(np.sum((shape0 - shape1)**2)) 
                    dists.append(distance_connection)
                    #add connection attributes
                    ids_lane_pre = edges.ids_lanes[id_edge_pre]
                    ids_lane = edges.ids_lanes[id_edge]
                    possible_connections_pre = []
                    possible_connections = []
                    for id_lane_pre in ids_lane_pre:
                        poss_connections_pre = ids_connection[(connections.ids_fromlane[ids_connection] == id_lane_pre)]
                        for poss_connection_pre in poss_connections_pre:
                            possible_connections_pre.append(poss_connection_pre)
                    for id_lane in ids_lane:
                        poss_connections = ids_connection[(connections.ids_tolane[ids_connection] == id_lane)]
                        for poss_connection in poss_connections:
                            possible_connections.append(poss_connection)
                    connections_list = []
                    for element in possible_connections_pre:
                        if element in possible_connections:
                            connections_list.append(element)
                    if connections_list == []:
                        print('Error: map matching process should be done without ignoring connections')  
                        break 
                    lane_index_connections = lanes.indexes[connections.ids_fromlane[connections_list]] + lanes.indexes[connections.ids_tolane[connections_list]]
                    connection = connections_list[np.argmin(lane_index_connections)]
                    ids_arc.append(connection)
                    conn_n_matched[connection] +=1
                    if connections.turns_type[connection] == 'left_turn':
                        number_left_turns += 1
                        if connections.are_tls[connection]:
                            tls_number_left_turns += 1
                        #print 'number_left_turns',number_left_turns
                    elif connections.turns_type[connection] == 'right_turn':
                        number_right_turns += 1
                        if connections.are_tls[connection]:
                            tls_number_right_turns += 1                        #print 'number_right_turns',number_right_turns
                    elif connections.turns_type[connection] == 'crossing':
                        number_crossings += 1
                        if connections.are_tls[connection]:
                            tls_number_crossings += 1                        #print 'number_crossings',number_crossings
                    elif connections.turns_type[connection] == 'u_turn':
                        number_u_turns += 1
                    are_connection.append(1)
                    
                #add edge attributes
                for shape in edges.shapes[id_edge]: 
                    stretch_shape.append(shape.tolist())
                dists.append(edges.lengths[id_edge])
                ids_arc.append(id_edge)
                are_connection.append(0)
                
                id_edge_pre = id_edge
            cumulative_dists_initial = np.cumsum(dists)
                
            if self.is_speedana == True :
                
                routesresults_matched.cumulative_dists[id_res] = cumulative_dists_initial
                routesresults_matched.ids_arc[id_res] = ids_arc
                routesresults_matched.is_connection[id_res] = are_connection 

                #Match trip points to the strechted polyline
                point_positions_on_poly = []
                point_times = []
                ids_points = trips.ids_points[id_trip]
                coords = points.coords[ids_points]
                for id_point, coord in zip(ids_points, coords):
                    point_positions_on_poly.append(get_pos_on_polyline_from_coord(stretch_shape, coord))
                    point_times.append(points.timestamps[id_point]-trips.timestamps[id_trip])
                
                #Delete either inadeguate points or, if necessary, the whole trip
                n_irregular_points = 0
                irregular_indices = []
                
##                print 'id_trip', id_trip
                #aggiustare gli ELSEEE
                for i in range(len(point_positions_on_poly)-1):
##                    print 'i', i
                    #If there are sufficient points and arcs
                    if len(point_positions_on_poly) > self.n_point_min and len(dists) > self.n_arc_min:
                    #If the i+1 point if collocated in the polyline after the i point
##                        print '1'
                        if point_positions_on_poly[i]<=point_positions_on_poly[i+1]:
                            is_adeguate = True
##                            print '2'
                        elif point_positions_on_poly[i]>point_positions_on_poly[i+1] and i < self.n_external_points:
##                            print '3'
                            for j in range(self.n_external_points):
                                irregular_indices.append(j)
####                                print '4'

                        elif point_positions_on_poly[i]>point_positions_on_poly[i+1] and i > len(point_positions_on_poly)- self.n_external_points:
##                            print '5'
                            for j in range(self.n_external_points):
##                                print '6'
                                irregular_indices.append(len(ids_points)-j)
                            
                        
                        elif i < len(point_positions_on_poly) -2 and point_positions_on_poly[i]<=point_positions_on_poly[i+2]:
##                            #If i+2 point if collocated in the polyline after the i point
##                                if point_positions_on_poly[i]<=point_positions_on_poly[i+2]:
##                                    print '7'
                                    n_irregular_points += 1
                                    #Delete i+1 point
                                    irregular_indices.append(i+1)
                                                  
##                                if  i > 0:   
##                                    #If i-1 point if collocated in the polyline before the i+1 point
##                                    if point_positions_on_poly[i-1]<=point_positions_on_poly[i+1]:
##                                        n_irregular_points += 1
##                                        #Delete i point
##                                        irregular_indices.append(i)
##                                        break
                        elif i < len(point_positions_on_poly) -3 and point_positions_on_poly[i]<=point_positions_on_poly[i+3]:
##                                #If i+3 point if collocated in the polyline after the i point
##                                if point_positions_on_poly[i]<=point_positions_on_poly[i+3]:
                                    n_irregular_points += 2
                                    #Delete i+1 and i+2 points
                                    irregular_indices.append(i+1)
                                    irregular_indices.append(i+2)
##                                    print '8'
##                                if  i > 1:
##                                    #If i-2 point if collocated in the polyline before the i+1 point
##                                    if point_positions_on_poly[i-2]<=point_positions_on_poly[i+1]:
##                                        n_irregular_points += 2
##                                        #Delete i and i-1 points
##                                        irregular_indices.append(i)
##                                        irregular_indices.append(i-1)
                        elif i < len(point_positions_on_poly) -4 and point_positions_on_poly[i]<=point_positions_on_poly[i+4]:
##                                #If i+3 point if collocated in the polyline after the i point
##                                if point_positions_on_poly[i]<=point_positions_on_poly[i+4]:
                                    n_irregular_points += 3
                                    #Delete i+1 and i+2 points
                                    irregular_indices.append(i+1)
                                    irregular_indices.append(i+2)
                                    irregular_indices.append(i+3)
##                                    print '9'

                        else:
##                            print '10'
                            is_adeguate = False 
                            break

                    else:
##                        print '11'
                        is_adeguate = False
                        print('too few or points or edges involved') 
                        break
##                                                #If the problem occour to the second points 
##                                                else:
##                                                    irregular_indices.append(0)
##                                                    irregular_indices.append(1)
##                                #If the problem occour to the penultimate point            
##                                else:
##                                    irregular_indices.append(len(ids_points)-2)

                #Even if the last and first point does not have problems, it is useful to delete them           
                irregular_indices.append(len(ids_points)-1)
                irregular_indices.append(0)
                
##                print irregular_indices
                irregular_indices = np.unique(irregular_indices)
##                print irregular_indices
                #Plot hourly law of motion1
                ####################################################################
##                import matplotlib
##                import matplotlib.pyplot as plt
##
##                fig, ax = plt.subplots()
##                ax.plot(point_positions_on_poly, point_times)
                ######################################################################
                #Consider only valid points
##                print 'n_irregular_points', n_irregular_points, 'irregular_indices', irregular_indices
##                print point_positions_on_poly
                point_positions_on_poly = np.delete(point_positions_on_poly, irregular_indices)
##                print point_positions_on_poly
##                ids_arc_points = np.delete(ids_arc_points, irregular_indices)

##                print ids_points
                ids_points = np.delete(ids_points, irregular_indices)
##                print ids_points
                coords = np.delete(coords, irregular_indices)
##                print point_times
                point_times = np.delete(point_times, irregular_indices)
##                print point_times
##                print 'stretch_shape', stretch_shape, 'dists', dists, 'point_positions_on_poly', point_positions_on_poly, 'point_times', point_times
                ids_pointedge = np.delete(ids_pointedge, irregular_indices).tolist()
##                cumulative_dists = np.delete(cumulative_dists, irregular_indices)
                if len(point_positions_on_poly) < 1:
                    is_adeguate = False 


                if is_adeguate:
                    
                    distance_real = point_positions_on_poly[-1]-point_positions_on_poly[0]
                    duration_real = point_times[-1]-point_times[0] 
                    distances_real[id_route_matched] = distance_real
                    durations_real[id_route_matched] = duration_real  
                    average_speed_real = distance_real/duration_real
##                    print 'stretch_shape', stretch_shape, 'dists', dists, 'point_positions_on_poly', point_positions_on_poly, 'point_times', point_times
                    #Plot hourly law of motion2
                    ####################################################################

##                    ax.plot(point_positions_on_poly, point_times, linewidth = 4)
##                    
##                    ax.set(xlabel='space (m)', ylabel='time (s)',
##                           title='Hourly law of motion trip %d'%(id_trip))
##                    ax.grid()
##    
##                    fig.savefig("test trip %d.png"%(id_trip))
##                    plt.show()
                    ####################################################################
                    good_traces+=1
                    #Match points to edge and connections
                    cumulative_dists = np.zeros(len(cumulative_dists_initial))
                    #Correct cumulative distances, adding a lenght to the connections from the backward
                    for is_connection, i in zip(are_connection, list(range(len(cumulative_dists_initial)))):
                        if is_connection == 0 and i < len(cumulative_dists_initial)-1:
                            if i>0:
                                if cumulative_dists_initial[i] - self.junctionrange > cumulative_dists_initial[i-1]:
                                    cumulative_dists[i] = cumulative_dists_initial[i] - self.junctionrange
                                else:
                                     cumulative_dists[i] = cumulative_dists_initial[i] - (cumulative_dists_initial[i]-cumulative_dists_initial[i-1]-0.1)
                            else:
                                if cumulative_dists_initial[i] - self.junctionrange   > 0:
                                    cumulative_dists[i] = cumulative_dists_initial[i] - self.junctionrange   
                                 
                        else:
                            cumulative_dists[i] = cumulative_dists_initial[i] 
                                                 
                    #match points to arcs
                    ids_arc_point = np.zeros(len(point_positions_on_poly), dtype = np.int32)
                    are_connection_points = np.zeros(len(point_positions_on_poly), dtype = np.int32)
                    diffs = cumulative_dists-point_positions_on_poly[0]
                    for diff, i in  zip(diffs, list(range(len(diffs)))):
                        if diff < 0:
                            diffs[i] = np.max(diffs)+1
                    ids_arc_point[0] = ids_arc[np.argmin(diffs[(diffs>0)])]
                    are_connection_points[0] = are_connection[np.argmin(diffs[(diffs>0)])]
                    for i in range(len(point_positions_on_poly)-1):
                        for  j in  range(len(cumulative_dists)-1):
                            if point_positions_on_poly[i+1]<=cumulative_dists[0]:
                                ids_arc_point[i+1] = ids_arc[0]
                                are_connection_points[i+1] = are_connection[0]
                            elif cumulative_dists[j] <= point_positions_on_poly[i+1] and point_positions_on_poly[i+1] < cumulative_dists[j+1]:
                                ids_arc_point[i+1] = ids_arc[j+1]
                                are_connection_points[i+1] = are_connection[j+1]
                    
                    routesresults_matched.ids_arc_point[id_res] = ids_arc_point
                    routesresults_matched.is_connection_point[id_res] = are_connection_points
                    
                    
                    
                    time_wait_route = 0
                    time_wait_tls_route = 0
                    time_wait_junction_route = 0
                    number_wait_route = 0
                    number_wait_route_at_junktions = 0
                    number_wait_route_at_tls_junktions = 0
                    number_wait_route_at_left_turns = 0 
                    number_wait_route_at_right_turns = 0
                    number_wait_route_at_crossings = 0
                    time_wait_left_turn_route = 0
                    time_wait_right_turn_route = 0
                    time_wait_crossing_route = 0
                    number_wait_route_at_left_turns_tls = 0 
                    number_wait_route_at_right_turns_tls = 0
                    number_wait_route_at_crossings_tls = 0
                    time_wait_left_turn_route_tls = 0
                    time_wait_right_turn_route_tls = 0
                    time_wait_crossing_route_tls = 0
    
    
                    #calculate average edge speed
                    for i, id_arc, is_connection in zip(list(range(len(ids_arc))), ids_arc, are_connection):
                        if i>0 and i<len(ids_arc)-1:   
                            if  not is_connection:
                                edge_points_time = point_times[(ids_arc_point == id_arc)]
                                edge_points_pos = point_positions_on_poly[(ids_arc_point == id_arc)]
                                if edge_points_time != []:
                                    if edge_points_time[0] != edge_points_time[-1]:
                                        delta_edge_pos = edge_points_pos[-1]-edge_points_pos[0]
                                        delta_edge_time = edge_points_time[-1]-edge_points_time[0]
                                        av_speed = (delta_edge_pos)/(delta_edge_time)
                                        if av_speed > self.edgespeed_max:
                                            edge_speeds[:][id_arc].append(self.edgespeed_max)
                                        else:
                                            edge_speeds[:][id_arc].append(av_speed)
                                        edge_times[id_arc] += delta_edge_time
                                        #speed in motion
                                        delta_edge_pos_motion = 0
                                        delta_edge_timemotion = 0
                                        for i in range(len(edge_points_time)):
                                            if i>0:
                                                if (edge_points_pos[i]-edge_points_pos[i-1])/(edge_points_time[i]-edge_points_time[i-1]) > self.waitspeed:
                                                    delta_edge_pos_motion += edge_points_pos[i]-edge_points_pos[i-1]
                                                    delta_edge_timemotion += edge_points_time[i]-edge_points_time[i-1]
                                        if  delta_edge_timemotion > 0:
                                            av_speed_mot = (delta_edge_pos_motion)/(delta_edge_timemotion)
                                            if av_speed_mot > self.edgespeed_max:
                                                edge_speeds_in_motion[:][id_arc].append(self.edgespeed_max)
                                            else:
                                                edge_speeds_in_motion[:][id_arc].append(av_speed_mot)
                    



                    
                    #calculating waiting times
                    point_speeds = [self.waitspeed]
                    
                    current_waitingtime_total = 0.0
                        
##                    actual_edge_starting_time_and_position = [point_times[0], point_positions_on_poly[0]]
                    id_arc_pre = 0
                    last_conn = 0
                    for i, point_position_on_poly, point_time, id_arc_point, is_connection_point   in zip(list(range(len(point_times))), point_positions_on_poly, point_times, ids_arc_point, are_connection_points):

                        if i>0:
                            delta_time = point_times[i] - point_times[i-1]  
                            if  delta_time > 0:
                                velocity =  (point_positions_on_poly[i]-point_positions_on_poly[i-1])/delta_time
                                if velocity > self.pointspeed_max:
                                    point_speeds.append(self.pointspeed_max)
                                    velocity = self.pointspeed_max
                                else:
                                    point_speeds.append(velocity)
                                    
                            else:
                                velocity =  self.waitspeed
                                point_speeds.append(self.waitspeed)
                                

                            if velocity < self.waitspeed:
                                number_wait_route +=1
                                time_wait_route += delta_time
                                
                                if is_connection_point:
                                    number_wait_route_at_junktions +=1
                                    time_wait_junction_route += delta_time
                                    
                                    wait_times_node[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]] += delta_time
                                    #
                                    wait_times_conn[id_arc_point] += delta_time
                                    #
                                    wait_times_edge_at_int[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]] += delta_time
                                    #
                                    if connections.turns_type[id_arc_point] == 'left_turn':
                                        time_wait_left_turn_route += delta_time
                                    if connections.turns_type[id_arc_point] == 'right_turn':
                                        time_wait_right_turn_route += delta_time
                                    if connections.turns_type[id_arc_point] == 'crossing':
                                        time_wait_crossing_route += delta_time
                                        
                                    if id_arc_point != id_arc_pre:
                                        n_wait_times_edge_at_int[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]] += 1
                                        print('connection', id_arc_point, 'n +1')
                                        n_wait_times_node[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]] += 1
                                        if connections.turns_type[id_arc_point] == 'left_turn':
                                            number_wait_route_at_left_turns += 1
                                        if connections.turns_type[id_arc_point] == 'right_turn': 
                                            number_wait_route_at_right_turns += 1
                                        if connections.turns_type[id_arc_point] == 'crossing':
                                            number_wait_route_at_crossings += 1

                                            
                                            
                                    if nodes.types[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]] == 1 or nodes.types[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]] == 5 or nodes.types[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]] == 9:
                                        time_wait_tls_route += delta_time
                                        number_wait_route_at_tls_junktions += 1
                                                                               
                                        tls_wait_times_conn[id_arc_point] += delta_time
                                        #
                                        tls_wait_times_edge_at_int[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]] += delta_time
                                        
                                        if connections.turns_type[id_arc_point] == 'left_turn':
                                            time_wait_left_turn_route_tls += delta_time
                                        if connections.turns_type[id_arc_point] == 'right_turn':
                                            time_wait_right_turn_route_tls += delta_time
                                        if connections.turns_type[id_arc_point] == 'crossing':
                                            time_wait_crossing_route_tls += delta_time
                                            
                                        if id_arc_point != id_arc_pre:
                                            n_tls_wait_times_edge_at_int[lanes.ids_edge[connections.ids_fromlane[id_arc_point]]]+=1
                                            n_tls_wait_times_conn[id_arc_point] +=1
                                            if connections.turns_type[id_arc_point] == 'left_turn':
                                                number_wait_route_at_left_turns_tls += 1
                                            if connections.turns_type[id_arc_point] == 'right_turn': 
                                                number_wait_route_at_right_turns_tls += 1
                                            if connections.turns_type[id_arc_point] == 'crossing':
                                                number_wait_route_at_crossings_tls += 1                                
                                else:

                                    wait_times_edge[id_arc_point] += delta_time
                                    if id_arc_point != id_arc_pre:
                                        n_wait_times_edge[id_arc_point] += 1
                                
                                if id_arc_point != id_arc_pre and last_conn > 0:
                                    connections_waitingtimes[last_conn].append(current_waitingtime_total)
                                    nodes_waitingtimes[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[last_conn]]]].append(current_waitingtime_total)
                                    print('ADD', 'current_waitingtime_total', current_waitingtime_total, 'id_arc_point', last_conn)
                                    current_waitingtime_total = 0.0
                                
                                if is_connection_point:
                                    last_conn = id_arc_point
                                else:
                                    last_conn = 0
                                
                                
                                if id_arc_point != id_arc_pre:
                                    current_waitingtime_total = 0.0
                                current_waitingtime_total  += delta_time
                                print('current_waitingtime_total', current_waitingtime_total, 'id_arc_point', id_arc_point)
                                id_arc_pre = id_arc_point

                        
                    #add_last_waiting_time
                    if last_conn > 0:
                        connections_waitingtimes[last_conn].append(current_waitingtime_total)
                        nodes_waitingtimes[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[last_conn]]]].append(current_waitingtime_total)
                        print('ADD', 'current_waitingtime_total', current_waitingtime_total, 'id_arc_point', last_conn)
                        current_waitingtime_total = 0.0
                    print('there were', len(irregular_indices), 'irregular indices')    
                    print('time_wait_route', time_wait_route,'time_wait_junction_route',time_wait_junction_route, 'time_wait_tls_route', time_wait_tls_route)
##                    print 'GOOD TRACES', good_traces, 'OF',(good_traces+bad_traces) ,'/',len(ids_trip)        
                    
                    #Find all connection for the connection results
                    connections_list = []
                    edges_list = []
                    nodes_list = []
                    id_arc_pre = 0
                    for id_arc, is_connection in zip(ids_arc_point, are_connection_points):
                        
                        if is_connection:
                            if id_arc != id_arc_pre:
                                connections_list.append(id_arc)
                                nodes_list.append(edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc]]])
##                            all_connections.append(int(id_arc))
##                            if id_arc != id_arc_pre:
##                                conn_n_matched_for_speed_analysis[id_arc] += 1
##                                node_n_matched_for_speed_analysis[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[id_arc]]]] += 1
##                            id_arc_pre = id_arc
                            id_arc_pre = id_arc
                        else: 
                            if id_arc != id_arc_pre:
                                edges_list.append(id_arc) 
                            id_arc_pre = id_arc
##                            all_edges.append(id_arc)
##                            if id_arc != id_arc_pre:
##                                edge_n_matched_for_speed_analysis[id_arc] += 1
##                    edges_list = np.unique(edges_list)
##                    connections_list = np.unique(connections_list)
##                    nodes_list = np.unique(nodes_list)
                    for edge in edges_list:
                        all_edges.append(edge) 
                        edge_n_matched_for_speed_analysis[edge] += 1 
                    for connection in connections_list:
                        all_connections.append(connection) 
                        conn_n_matched_for_speed_analysis[connection] += 1 
                        node_n_matched_for_speed_analysis[edges.ids_tonode[lanes.ids_edge[connections.ids_fromlane[connection]]]] += 1
                    trips.edges_list_dyn[id_trip] = edges_list
                    trips.connections_list_dyn[id_trip] = connections_list
                    trips.nodes_list_dyn[id_trip] = nodes_list
##                            id_arc_pre = id_arc
##                    print ids_arc, are_connection, all_connections
                        
    ##                    edgesresults.numbers_wait_junc[ids_waitedgeres_used] += 1
    ##                    
                    
                    routesresults_matched.n_times_wait[id_res] = number_wait_route
                    routesresults_matched.times_wait[id_res] = time_wait_route
                    routesresults_matched.times_wait_edges[id_res] = time_wait_route-time_wait_junction_route
                    routesresults_matched.n_times_wait_edges[id_res] = number_wait_route - number_wait_route_at_junktions
                    routesresults_matched.n_times_wait_tls[id_res] = number_wait_route_at_tls_junktions
                    routesresults_matched.times_wait_tls[id_res] = time_wait_tls_route
                    routesresults_matched.n_times_wait_junction[id_res] = number_wait_route_at_junktions
                    routesresults_matched.times_wait_junction[id_res]= time_wait_junction_route
                    routesresults_matched.times_inmotion[id_res] = duration_real-time_wait_route
                    routesresults_matched.n_times_wait_left_turn[id_res] = number_wait_route_at_left_turns
                    routesresults_matched.times_wait_left_turn[id_res]= time_wait_left_turn_route
                    routesresults_matched.n_times_wait_right_turn[id_res] = number_wait_route_at_right_turns
                    routesresults_matched.times_wait_right_turn[id_res]= time_wait_right_turn_route
                    routesresults_matched.n_times_wait_crossing[id_res] = number_wait_route_at_crossings
                    routesresults_matched.times_wait_crossing[id_res]= time_wait_crossing_route
                    
                    routesresults_matched.n_times_wait_left_turn_tls[id_res] = number_wait_route_at_left_turns_tls
                    routesresults_matched.times_wait_left_turn_tls[id_res]= time_wait_left_turn_route_tls
                    routesresults_matched.n_times_wait_right_turn_tls[id_res] = number_wait_route_at_right_turns_tls
                    routesresults_matched.times_wait_right_turn_tls[id_res]= time_wait_right_turn_route_tls
                    routesresults_matched.n_times_wait_crossing_tls[id_res] = number_wait_route_at_crossings_tls
                    routesresults_matched.times_wait_crossing_tls[id_res]= time_wait_crossing_route_tls                 
                    
                    routesresults_matched.ids_valid_point_speedana[id_res] = ids_points
                    routesresults_matched.speedana_point_speeds[id_res] = point_speeds 
                    routesresults_matched.speedana_point_pos[id_res] =  point_positions_on_poly
                    routesresults_matched.speedana_point_times[id_res] =  point_times 
##                    routesresults_matched.edge_cumulative_dists[id_res] = cumulative_dists 
                    routesresults_matched.ids_pointedges[id_res] = ids_pointedge
##                    print   ids_points
##                    print point_speeds
##                    print point_positions_on_poly
##                    print point_times
##                    print cumulative_dists
##                    print ids_pointedge
                    #TUTTO DA AGGIUNGERE ALLA FINE, OLTRE A CONNECTIONS RESULT

    ##                    routesresults_matched.pointspositions[id_res] = pointsposition
    ##                    routesresults_matched.pointsspeeds[id_res] = pointsspeed
    ##                    routesresults_matched.pointstimes[id_res] = pointstime
    ##                    routesresults_matched.ids_pointedges[id_res] = ids_pointedge
                        
                    
                else:
##                    ax.set(xlabel='space (m)', ylabel='time (s)',
##                    title='Hourly law of motion trip %d'%(id_trip))
##                    ax.grid()
##                    fig.savefig("test trip_err %d.png"%(id_trip))

##                    plt.show()
                    bad_traces+=1
                    print('trip', id_trip, 'is not adeguate for the speed analysis or too few points remained after point filtering, with', len(irregular_indices), 'irregular points')

                    routesresults_matched.times_inmotion[id_res]= -1.
                    routesresults_matched.n_times_wait[id_res] = -1.
                    routesresults_matched.times_wait[id_res] = -1.
                    routesresults_matched.times_wait_edges[id_res] = -1.
                    routesresults_matched.n_times_wait_edges[id_res] = -1.
                    routesresults_matched.n_times_wait_tls[id_res] = -1.
                    routesresults_matched.times_wait_tls[id_res] = -1.
                    routesresults_matched.n_times_wait_junction[id_res] = -1.
                    routesresults_matched.times_wait_junction[id_res]= -1.
                    routesresults_matched.n_times_wait_left_turn[id_res]= -1.
                    routesresults_matched.times_wait_left_turn[id_res]= -1.
                    routesresults_matched.n_times_wait_right_turn[id_res]= -1.
                    routesresults_matched.times_wait_right_turn[id_res]= -1.
                    routesresults_matched.n_times_wait_crossing[id_res]= -1.
                    routesresults_matched.times_wait_crossing[id_res]= -1.
                    
                    routesresults_matched.n_times_wait_left_turn_tls[id_res]= -1.
                    routesresults_matched.times_wait_left_turn_tls[id_res]= -1.
                    routesresults_matched.n_times_wait_right_turn_tls[id_res]= -1.
                    routesresults_matched.times_wait_right_turn_tls[id_res]= -1.
                    routesresults_matched.n_times_wait_crossing_tls[id_res]= -1.
                    routesresults_matched.times_wait_crossing_tls[id_res]= -1.
                    distances_real[id_route_matched] = -1.
                    durations_real[id_route_matched] = -1.
                    average_speed_real = -1.
                    
                    
                
                print('There were', good_traces, 'good traces and', bad_traces, 'bad traces', 'of',(good_traces+bad_traces) ,'/',len(ids_trip))
                    

        
##            print distances_real
##            print id_route_matched
            ## save final results routesresults_matched
            if self.is_speedana:
                routesresults_matched.set_row(  id_res,
                                                ids_route = id_route_matched,
                                                lengths_within_center = lenght_inside_center,
                                                numbers_left_turns = number_left_turns,
                                                numbers_right_turns = number_right_turns,
                                                numbers_crossings = number_crossings,
                                                numbers_u_turns = number_u_turns,
                                                numbers_tls_left_turns = tls_number_left_turns,
                                                numbers_tls_right_turns = tls_number_right_turns,
                                                numbers_tls_crossings = tls_number_crossings,
                                                av_n_connections_per_intersection = n_connections_per_intersection/np.float(n_nodes_matched),
                                                distances = dist_matched,
                                                distances_real = distances_real[id_route_matched],
                                                durations = duration_matched,
                                                durations_real = durations_real[id_route_matched],
                                                average_speeds = average_speed,
                                                average_speeds_real = average_speed_real,
                                                timestamps = trips.timestamps[trips.routes.get_value().ids_trip[id_route_matched]],
                                                lengths_mixed = np.sum(distances[ids_edge_matched[accesslevels_matched==1]]),
                                                lengths_exclusive = np.sum(distances[ids_edge_matched[accesslevels_matched==2]]),
                                                lengths_contrary = np.sum(distances[ids_edge_matched[accesslevels_matched==-2]]),
                                                lengths_low_priority = np.sum(distances[ids_edge_matched[priorities_matched <= priority_max_low]]),
                                                lengths_overlap_matched = dist_matched,# 100% overlap
                                                numbers_nodes = n_nodes_matched,
                                                numbers_nodes_tls = n_tls_matched,
                                                numbers_prioritychange = np.sum(np.logical_not(priorities_matched[:-1],priorities_matched[1:])))
            else: 
                routesresults_matched.set_row(  id_res,
                                                ids_route = id_route_matched,
                                                distances = dist_matched,
                                                lengths_within_center = lenght_inside_center,
                                                numbers_left_turns = number_left_turns,
                                                numbers_right_turns = number_right_turns,
                                                numbers_crossings = number_crossings,
                                                numbers_u_turns = number_u_turns,
                                                numbers_tls_left_turns = tls_number_left_turns,
                                                numbers_tls_right_turns = tls_number_right_turns,
                                                numbers_tls_crossings = tls_number_crossings,
##                                                distances_real = distances_real[id_route_matched],
                                                durations = duration_matched,
##                                                durations_real = durations_real[id_route_matched],
                                                average_speeds = average_speed,
##                                                average_speeds_real = average_speed_real,
                                                timestamps = trips.timestamps[trips.routes.get_value().ids_trip[id_route_matched]],
                                                lengths_mixed = np.sum(distances[ids_edge_matched[accesslevels_matched==1]]),
                                                lengths_exclusive = np.sum(distances[ids_edge_matched[accesslevels_matched==2]]),
                                                lengths_contrary = np.sum(distances[ids_edge_matched[accesslevels_matched==-2]]),
                                                lengths_low_priority = np.sum(distances[ids_edge_matched[priorities_matched <= priority_max_low]]),
                                                lengths_overlap_matched = dist_matched,# 100% overlap
                                                numbers_nodes = n_nodes_matched,
                                                numbers_nodes_tls = n_tls_matched,
                                                numbers_prioritychanind_edges_endge = np.sum(np.logical_not(priorities_matched[:-1],priorities_matched[1:])),                                  
                                            #pointspositions = pointsposition,
                                            #pointsspeeds = pointsspeed,
                                            #pointstimes = pointstime,
                                            #ids_pointedges = ids_pointedge,
                                            )
            #print '  AFTER:'    
            #print '  routesresults_matched.ids_route',routesresults_matched.ids_route.get_value()
            #print '  routesresults_matched.ids_pointedges',routesresults_matched.ids_pointedges.get_value()
            #print '  routesresults_matched.pointsposition',routesresults_matched.pointspositions.get_value()
                    
            if id_route_shortest>=0:
                ids_edge_shortest = np.array(routes.ids_edges[id_route_shortest], dtype = np.int32)[1:]
                id_edge_pre = 0
                number_left_turns_shortest = 0
                number_right_turns_shortest = 0
                number_crossings_shortest = 0
                tls_number_left_turns_shortest = 0
                tls_number_right_turns_shortest = 0
                tls_number_crossings_shortest = 0
                lenght_inside_center_shortest = 0
                n_connections_per_intersection_shortest = 0
                for id_edge_shortest in ids_edge_shortest:
    ##                    print 'analyzing edge', id_edge
                    n_connections_per_intersection_shortest += nodes.n_connections[edges.ids_tonode[id_edge_shortest]]
                    if self.is_analyze_lengths_within_center:
                        if id_edge_shortest in zones.ids_edges_orig[zones.ids_sumo.get_id_from_index(self.center_zone)] or id_edge_shortest in zones.ids_edges_dest[zones.ids_sumo.get_id_from_index(self.center_zone)]:
                            lenght_inside_center_shortest += distances[id_edge_shortest]
                            #print 'lenght_inside_center', lenght_inside_center
                    if id_edge_pre != 0:

                        #add connection attributes
                        ids_lane_pre = edges.ids_lanes[id_edge_pre]
                        ids_lane = edges.ids_lanes[id_edge_shortest]
                        possible_connections_pre = []
                        possible_connections = []
                        for id_lane_pre in ids_lane_pre:
                            poss_connections_pre = ids_connection[(connections.ids_fromlane[ids_connection] == id_lane_pre)]
                            for poss_connection_pre in poss_connections_pre:
                                possible_connections_pre.append(poss_connection_pre)
                        for id_lane in ids_lane:
                            poss_connections = ids_connection[(connections.ids_tolane[ids_connection] == id_lane)]
                            for poss_connection in poss_connections:
                                possible_connections.append(poss_connection)
                        connections_list = []
                        for element in possible_connections_pre:
                            if element in possible_connections:
                                connections_list.append(element)
                        if connections_list == []:
                            print('Error: map matching process should be done without ignoring connections')  
                            break 
                        lane_index_connections = lanes.indexes[connections.ids_fromlane[connections_list]] + lanes.indexes[connections.ids_tolane[connections_list]]
                        connection = connections_list[np.argmin(lane_index_connections)]
                        if connections.turns_type[connection] == 'left_turn':
                            number_left_turns_shortest += 1
                            if connections.are_tls[connection]:
                                tls_number_left_turns_shortest += 1
                            #print 'number_left_turns',number_left_turns
                        elif connections.turns_type[connection] == 'right_turn':
                            number_right_turns_shortest += 1
                            if connections.are_tls[connection]:
                                tls_number_right_turns_shortest += 1                            
                            #print 'number_right_turns',number_right_turns
                        elif connections.turns_type[connection] == 'crossing':
                            number_crossings_shortest += 1
                            if connections.are_tls[connection]:
                                tls_number_crossings_shortest += 1
                            #print 'number_crossings',number_crossings
                    id_edge_pre = id_edge_shortest
       
                accesslevels_shortest = accesslevelsmap[id_mode][ids_edge_shortest]
                priorities_shortest = priorities[ids_edge_shortest]
                nodetypes_shortest = nodetypes[ids_tonode[ids_edge_shortest]]
                
                # common edges of matched and shaortest path
                ids_edge_overlap = list(set(ids_edge_matched).intersection(ids_edge_shortest))
                
                # edges that are in the shortest path, but not in the matched path
                ids_edge_shortest_nonoverlap = np.array(list(set(ids_edge_shortest).difference(ids_edge_matched)),dtype = np.int32)
                
                # edges that are on the matched path, but not on the shortest path
                ids_edge_match_nonoverlap = np.array(list(set(ids_edge_matched).difference(ids_edge_shortest)),dtype = np.int32)
                
                dist_shortest = np.sum(distances[ids_edge_shortest])
                
                if 0:
                    print('\n  shortest dist ana')
                    d_cum = 0.0
                    l_cum = 0.0
                    for id_edge in ids_edge_shortest:
                        d_cum += distances[id_edge]
                        l_cum += edges.lengths[id_edge]
                        print('  id_edge_short',id_edge,'dist',distances[id_edge],'length',edges.lengths[id_edge],'c_cum',d_cum,'l_cum',l_cum)
                    print()
                    d_cum = 0.0
                    l_cum = 0.0
                    for id_edge in ids_edge:
                        d_cum += distances[id_edge]
                        l_cum += edges.lengths[id_edge]
                        print('  id_edge_matched',id_edge,'dist',distances[id_edge],'length',edges.lengths[id_edge],'c_cum',d_cum,'l_cum',l_cum)
                            
                    
                n_nodes_shortest = len(nodetypes_shortest)
                n_tls_shortest = np.sum(nodetypes_shortest ==tlstype)
                
                edgesresults.numbers_tot_shortest[ids_edgeresmap[ids_edge_shortest]] += 1
                #edgesresults.differences_dist_tot_shortest[ids_edgeresmap[ids_edge_shortest_nonoverlap]] += dist_matched-dist_shortest
                
                
                routesresults_shortest.set_row( id_res,
                                                ids_route = id_route_matched,
                                                numbers_left_turns = number_left_turns_shortest,
                                                numbers_right_turns = number_right_turns_shortest,
                                                numbers_crossings = number_crossings_shortest,
                                                numbers_tls_left_turns = tls_number_left_turns_shortest,
                                                numbers_tls_right_turns = tls_number_right_turns_shortest,
                                                numbers_tls_crossings = tls_number_crossings_shortest,
                                                av_n_connections_per_intersection = n_connections_per_intersection_shortest/np.float(n_nodes_shortest),
                                                lengths_within_center = lenght_inside_center_shortest,
                                                distances = dist_shortest,
                                                durations = dist_shortest/linespeed_matched+timeloss_intersection * n_nodes_shortest + timeloss_tl * n_tls_shortest,
                                                lengths_mixed = np.sum(distances[ids_edge_shortest[accesslevels_shortest==1]]),
                                                lengths_exclusive = np.sum(distances[ids_edge_shortest[accesslevels_shortest==2]]),
                                                lengths_contrary = np.sum(distances[ids_edge_shortest[accesslevels_shortest==-2]]),
                                                lengths_low_priority = np.sum(distances[ids_edge_shortest[priorities_shortest <= priority_max_low]]),
                                                numbers_nodes = n_nodes_shortest,
                                                numbers_nodes_tls = n_tls_shortest,
                                                numbers_prioritychange = np.sum(np.logical_not(priorities_shortest[:-1],priorities_shortest[1:])),
                                                lengths_overlap_matched = np.sum(distances[ids_edge_overlap]),# common edges of shortest and matched
                                                )
                                                
                
                
                accesslevels_nonoverlap= accesslevelsmap[id_mode][ids_edge_match_nonoverlap]
                #print '  ids_edge_match_nonoverlap',ids_edge_match_nonoverlap
                #print '  accesslevels_nonoverlap==1',np.flatnonzero(accesslevels_nonoverlap==1),np.flatnonzero(accesslevels_nonoverlap==1).dtype
                #print '  ids_edge_match_nonoverlap[accesslevels_nonoverlap==1]',ids_edge_match_nonoverlap[np.flatnonzero(accesslevels_nonoverlap==1)]
                priorities_nonoverlap = priorities[ids_edge_match_nonoverlap]
                nodetypes_nonoverlap = nodetypes[ids_tonode[ids_edge_match_nonoverlap]]
                
                dist_nonoverlap = np.sum(distances[ids_edge_match_nonoverlap])
                n_nodes_nonoverlap = len(nodetypes_nonoverlap)
                n_tls_nonoverlap = np.sum(nodetypes_nonoverlap ==tlstype)
                
                routesresults_matched_nonoverlap.set_row(  id_res,
                                            ids_route = id_route_matched,
                                            distances = dist_nonoverlap,
                                            durations = dist_nonoverlap/linespeed_matched+timeloss_intersection * n_nodes_nonoverlap + timeloss_tl * n_tls_nonoverlap,
                                            lengths_mixed = np.sum(distances[ids_edge_match_nonoverlap[accesslevels_nonoverlap==1]]),
                                            lengths_exclusive = np.sum(distances[ids_edge_match_nonoverlap[accesslevels_nonoverlap==2]]),
                                            lengths_low_priority = np.sum(distances[ids_edge_match_nonoverlap[priorities_nonoverlap <= priority_max_low]]),
                                            lengths_overlap_matched = dist_nonoverlap,# completely overlapping with itself
                                            numbers_nodes = n_nodes_nonoverlap,
                                            numbers_prioritychange = np.sum(np.logical_not(priorities_nonoverlap[:-1],priorities_nonoverlap[1:])),
                                            numbers_nodes_tls = n_tls_nonoverlap,
                                            )
                                            
                accesslevels_nonoverlap= accesslevelsmap[id_mode][ids_edge_shortest_nonoverlap]
                priorities_nonoverlap = priorities[ids_edge_shortest_nonoverlap]
                nodetypes_nonoverlap = nodetypes[ids_tonode[ids_edge_shortest_nonoverlap]]
                
                dist_nonoverlap = np.sum(distances[ids_edge_shortest_nonoverlap])
                n_nodes_nonoverlap = len(nodetypes_nonoverlap)
                n_tls_nonoverlap = np.sum(nodetypes_nonoverlap ==tlstype)
                
                routesresults_shortest_nonoverlap.set_row( id_res,
                                            ids_route = id_route_matched,
                                            distances = dist_nonoverlap,
                                            durations = dist_nonoverlap/linespeed_matched+timeloss_intersection * n_nodes_nonoverlap + timeloss_tl * n_tls_nonoverlap,
                                            lengths_mixed = np.sum(distances[ids_edge_shortest_nonoverlap[accesslevels_nonoverlap==1]]),
                                            lengths_exclusive = np.sum(distances[ids_edge_shortest_nonoverlap[accesslevels_nonoverlap==2]]),
                                            lengths_low_priority = np.sum(distances[ids_edge_shortest_nonoverlap[priorities_nonoverlap <= priority_max_low]]),
                                            lengths_overlap_matched = 0.0, # there is zero overlap with matched route
                                            numbers_nodes = n_nodes_nonoverlap,
                                            numbers_nodes_tls = n_tls_nonoverlap,
                                            numbers_prioritychange = np.sum(np.logical_not(priorities_nonoverlap[:-1],priorities_nonoverlap[1:])),
                                            )
           
            
            #print '  analyzing id_trip',id_trip,'Done.'                                                                    
        
            #print '  analyzing id_route_fastest',id_route_fastest,'id_res',id_res,id_route_fastest>-1
            if id_route_fastest>-1:
                
                ids_edge_fastest = np.array(routes.ids_edges[id_route_fastest], dtype = np.int32)[1:]
                accesslevels_fastest = accesslevelsmap[id_mode][ids_edge_fastest]
                priorities_fastest = priorities[ids_edge_fastest]
                nodetypes_fastest = nodetypes[ids_tonode[ids_edge_fastest]]
                
                # overlap analyses 
                ids_edge_overlap = list(set(ids_edge_matched).intersection(ids_edge_fastest))
                ids_edge_fastest_nonoverlap = np.array(list(set(ids_edge_fastest).difference(ids_edge_matched)),dtype = np.int32)
                ids_edge_match_nonoverlap = np.array(list(set(ids_edge_matched).difference(ids_edge_fastest)),dtype = np.int32)
                
                dist_fastest = trips.lengths_route_fastest[id_trip]#np.sum(distances[ids_edge_fastest])
                n_nodes_fastest = len(nodetypes_fastest)
                n_tls_fastest = np.sum(nodetypes_fastest ==tlstype)
                
                edgesresults.numbers_tot_fastest[ids_edgeresmap[ids_edge_fastest]] += 1
                edgesresults.differences_dist_tot_fastest[ids_edgeresmap[ids_edge_fastest_nonoverlap]] += dist_matched-dist_fastest
                
                
                routesresults_fastest.set_row( id_res,
                                                ids_route = id_route_fastest,
                                                distances = dist_fastest,
                                                durations = trips.durations_route_fastest[id_trip],
                                                lengths_mixed = np.sum(distances[ids_edge_fastest[accesslevels_fastest==1]]),
                                                lengths_exclusive = np.sum(distances[ids_edge_fastest[accesslevels_fastest==2]]),
                                                lengths_low_priority = np.sum(distances[ids_edge_fastest[priorities_fastest <= priority_max_low]]),
                                                numbers_nodes = n_nodes_fastest,
                                                numbers_nodes_tls = n_tls_fastest,
                                                numbers_prioritychange = np.sum(np.logical_not(priorities_fastest[:-1],priorities_fastest[1:])),
                                                lengths_overlap_matched = np.sum(distances[ids_edge_overlap]),
                                                )


    #load connettor results       
        if self.is_speedana:
            
            all_connections = np.unique(all_connections)
            ids_connectionresmap = connectionsresults.init_for_connections(all_connections)
                        
            for id_connection,\
                  wait_time_conn,\
                  n_wait_time_conn,\
                  tls_wait_time_conn,\
                  n_tls_wait_time_conn in\
                  zip(all_connections,\
                  wait_times_conn[all_connections],\
                  n_wait_times_conn[all_connections],\
                  tls_wait_times_conn[all_connections],\
                  n_tls_wait_times_conn[all_connections]):
                
                connectionsresults.numbers_matched_for_speed_analysis[ids_connectionresmap[id_connection]] =  conn_n_matched_for_speed_analysis[id_connection]   
                connectionsresults.numbers_tot_matched[ids_connectionresmap[id_connection]] =  conn_n_matched[id_connection]
                if n_wait_time_conn > 0:
                    connectionsresults.times_wait[ids_connectionresmap[id_connection]] = wait_time_conn/conn_n_matched_for_speed_analysis[id_connection]
                #if n_tls_wait_time_conn > 0:
                    #connectionsresults.times_wait_tls[ids_connectionresmap[id_connection]] = tls_wait_time_conn/conn_n_matched_for_speed_analysis[id_connection]
                connectionsresults.numbers_wait[ids_connectionresmap[id_connection]] = n_wait_time_conn
                #connectionsresults.numbers_wait_tls[ids_connectionresmap[id_connection]] = n_tls_wait_time_conn
                
                connectionsresults.are_tls[ids_connectionresmap[id_connection]] = connections.are_tls[id_connection]
                connectionsresults.numbers_connections_at_node[ids_connectionresmap[id_connection]] = connections.numbers_connections_at_node[id_connection]
                connectionsresults.type_turn[ids_connectionresmap[id_connection]] = connections.turns_type[id_connection]
                connectionsresults.lengths[ids_connectionresmap[id_connection]] = connections.lengths[id_connection]
                connectionsresults.list_waiting_times[ids_connectionresmap[id_connection]] = np.array(connections_waitingtimes[id_connection])

            
            all_edges = np.unique(all_edges)
                        
            for id_edge,\
                  wait_time_edge,\
                  n_wait_time_edge,\
                  wait_time_edge_at_int,\
                  n_wait_time_edge_at_int,\
                  tls_wait_time_edge_at_int,\
                  n_tls_wait_time_edge_at_int,\
                  in\
                  zip(all_edges,\
                  wait_times_edge[all_edges],\
                  n_wait_times_edge[all_edges],\
                  wait_times_edge_at_int[all_edges],\
                  n_wait_times_edge_at_int[all_edges],\
                  tls_wait_times_edge_at_int[all_edges],\
                  n_tls_wait_times_edge_at_int[all_edges]):
                
                edgesresults.numbers_matched_for_speed_analysis[ids_edgeresmap[id_edge]] =  edge_n_matched_for_speed_analysis[id_edge]
                if edge_n_matched_for_speed_analysis[id_edge]  > 0:
                    edgesresults.times_wait[ids_edgeresmap[id_edge]] = wait_time_edge/edge_n_matched_for_speed_analysis[id_edge]            
                if edge_n_matched_for_speed_analysis[id_edge]  > 0:
                    edgesresults.times_wait_junc[ids_edgeresmap[id_edge]] = wait_time_edge_at_int/edge_n_matched_for_speed_analysis[id_edge]
                if edge_n_matched_for_speed_analysis[id_edge]  > 0:
                    edgesresults.times_wait_tls[ids_edgeresmap[id_edge]] = tls_wait_time_edge_at_int/edge_n_matched_for_speed_analysis[id_edge]
                edgesresults.numbers_wait_edge[ids_edgeresmap[id_edge]] = n_wait_time_edge
                edgesresults.numbers_wait_junc[ids_edgeresmap[id_edge]] = n_wait_time_edge_at_int 
                edgesresults.numbers_tls_wait_edge[ids_edgeresmap[id_edge]] = n_tls_wait_time_edge_at_int
                edgesresults.durations_tot_matched[ids_edgeresmap[id_edge]] = edge_times[id_edge]
        # do mass edge result operations
        #edgesresults.speeds_average[ids_valid] = np.clip(edges.lengths[edgesresults.ids_edge[ids_valid]]/(edgesresults.durations_tot_matched[ids_valid]/edgesresults.numbers_tot_matched[ids_valid]),0.0,self.edgespeed_max) 
        
        
        #ids_valid = edgesresults.select_ids(edgesresults.durations_tot_matched.get_value()>self.edgedurations_tot_min)
        ids_valid = edgesresults.select_ids(edgesresults.numbers_tot_matched.get_value() > 0)
        for id_valid in ids_valid:
            #print 'average speeds of edge', edgesresults.ids_edge[id_valid]
            #print edge_speeds[edgesresults.ids_edge[id_valid]]
            #print 'average speeds in motion of edge',edgesresults.ids_edge[id_valid]
            #print edge_speeds_in_motion[edgesresults.ids_edge[id_valid]]
            
            edgesresults.average_slopes[id_valid] = net.edges.average_slopes[edgesresults.ids_edge[id_valid]]
            
            if len(edge_speeds[edgesresults.ids_edge[id_valid]]) > 1:
                edgesresults.speed_average[id_valid] = np.mean(edge_speeds[edgesresults.ids_edge[id_valid]])
                edgesresults.edge_speeds[id_valid] = np.array(edge_speeds[edgesresults.ids_edge[id_valid]])
            else:
                edgesresults.speed_average[id_valid] = -1.
                edgesresults.edge_speeds[id_valid] = -1.
            if len(edge_speeds_in_motion[edgesresults.ids_edge[id_valid]])>1:
                edgesresults.speed_average_in_motion[id_valid] = np.mean(edge_speeds_in_motion[edgesresults.ids_edge[id_valid]])
                edgesresults.edge_speeds_in_motion[id_valid] = np.array(edge_speeds_in_motion[edgesresults.ids_edge[id_valid]])
            else:
                edgesresults.speed_average_in_motion[id_valid] = -1.
                edgesresults.edge_speeds_in_motion[id_valid] = -1.
                
##        np.savetxt('ids_valid.txt', edgesresults.ids_edge[id_valid])
##        np.savetxt('edge%d_speeds.txt'%(edgesresults.ids_edge[id_valid]]), edge_speeds[edgesresults.ids_edge[id_valid]])
        
##                edge_speeds[i] = -1
##                edge_speeds_in_motion[i] = -1
##        edge_speeds = edge_lenghts/edge_times
##        edge_speeds_in_motion = edge_lenghts_in_motion/edge_times_in_motion
##        for i in range(len(edge_speeds)):
##            if edge_speeds[i] != float:
##                edge_speeds[i] = -1
##                edge_speeds_in_motion[i] = -1
        
        edgesresults.probabilities_tot_matched[ids_valid] = edgesresults.numbers_tot_matched[ids_valid]/float(len(ids_valid))   
        edgesresults.flows_est[ids_valid] = self.flowfactor * edgesresults.numbers_tot_matched[ids_valid]
        ids_valid = edgesresults.select_ids(edgesresults.numbers_tot_matched.get_value()>0)

        
##        edgesresults.times_wait[ids_valid] = edgesresults.times_wait[ids_valid]/edge_n_matched_for_speed_analysis[ids_valid]
##        edgesresults.times_wait_tls[ids_valid] = edgesresults.times_wait_tls[ids_valid]/edge_n_matched_for_speed_analysis[ids_valid]
##        edgesresults.times_wait_junc[ids_valid] = edgesresults.times_wait_junc[ids_valid]/edge_n_matched_for_speed_analysis[ids_valid]
        
        #node results for significant nodes, where waiting time can be detected
        ids_node_raw =list(set(edges.ids_tonode[all_edges]))
        ids_node = nodesresults.get_nodes_significant(ids_node_raw)
        ids_noderes = nodesresults.add_rows(n=len(ids_node),ids_node = ids_node)
        
        for id_node, id_noderes in zip(ids_node, ids_noderes):
            nodesresults.numbers_matched_for_speed_analysis[id_noderes] = node_n_matched_for_speed_analysis[id_node]
            if n_wait_times_node[id_node]>0:
                nodesresults.times_wait[id_noderes] = wait_times_node[id_node]/node_n_matched_for_speed_analysis[id_node]
            nodesresults.numbers_wait_time[id_noderes] = n_wait_times_node[id_node]
            nodesresults.numbers_tot_matched[id_noderes] = node_n_matched[id_node]
            
            nodesresults.types[id_noderes] = nodes.types[id_node]
            nodesresults.types_tl[id_noderes] = nodes.types_tl[id_node]
            nodesresults.turnradii[id_noderes] = nodes.turnradii[id_node]
            nodesresults.n_connections[id_noderes] = nodes.n_connections[id_node]
            nodesresults.n_left_turns[id_noderes] = nodes.n_left_turns[id_node]
            nodesresults.n_right_turns[id_noderes] = nodes.n_right_turns[id_node]
            nodesresults.n_crossings[id_noderes] = nodes.n_crossings[id_node]
            nodesresults.n_u_turns[id_noderes] = nodes.n_u_turns[id_node]
            nodesresults.list_waiting_times[id_noderes] = nodes_waitingtimes[id_node]
        
##        if self.is_nodeana:
##            # do node type analyses
##            
##            # extract the set of nodes that are involved in the mapmatching
##            ids_edge = edgesresults.ids_edge[ids_valid]
##            #ids_node_raw = np.array(list(set(edges.ids_tonode[ids_edge])), dtype = np.int32)
##            ids_node_raw =list(set(edges.ids_tonode[ids_edge]))
##            
##            #print '  identified nodes:',ids_node_raw
##            #for id_node_raw in ids_node_raw:
##            #    print '    id_node_raw',id_node_raw
##            # 
##            #map_id_node_to_type = {}
##            
##                
##            
##            #print '  nodesresults.ids_node',nodesresults.ids_node.get_value()
##            get_id_nodesres = nodesresults.ids_node.get_id_from_index
##            has_id_node = nodesresults.ids_node.has_index
##            for id_nodes_to, nodetype, time_wait, time_wait_tls, n_wait in zip(\
##                                    edges.ids_tonode[ids_edge], 
##                                    nodes.types[edges.ids_tonode[ids_edge]],
##                                    edgesresults.times_wait_junc[ids_valid],
##                                    edgesresults.times_wait_tls[ids_valid],
##                                    edgesresults.numbers_wait_junc[ids_valid],
##                                    ):
##                #print '    id_nodes_to',id_nodes_to,nodetype
##                if has_id_node(id_nodes_to):
##                    id_nodseres = get_id_nodesres(id_nodes_to)
##                    
##                    if nodetype == 1:#traffic_light
##                        nodesresults.times_wait[id_nodseres] += time_wait_tls
##                    else:
##                        nodesresults.times_wait[id_nodseres] += time_wait
##                    nodesresults.numbers_tot_matched[id_nodseres] += n_wait
##                
##            nodesresults.times_wait[ids_noderes] = nodesresults.times_wait[ids_noderes]/nodesresults.numbers_tot_matched[ids_noderes]
        
        
        
        
        #Calculate expected waiting times
        
        ids_routesresults_matched = routesresults_matched.get_ids()
####        print ids_routesresults_matched
        are_dynaanalysis = routesresults_matched.times_inmotion[ids_routesresults_matched] > -1
##        print are_dynaanalysis
        ids_routesresults_matched_dyn = ids_routesresults_matched[(are_dynaanalysis == 1)]
##        print ids_routesresults_matched_dyn
        
        expected_waiting_times_tot_route_matched1 = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched2 = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_nodes = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_tls_nodes = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_edges = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_left_turn = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_left_turn_tls = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_right_turn = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_right_turn_tls = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_crossing = np.zeros(len(ids_routesresults_matched)+1)
        expected_waiting_times_tot_route_matched_crossing_tls = np.zeros(len(ids_routesresults_matched)+1)
##        scenario = self.parent.get_scenario()
##        connections = scenario.net.connections
##        nodes = scenario.net.nodes
##        edges = scenario.net.edges
##        connectionsresults = results.connectionsresults
##        nodesresults = results.nodesresults
##        edgesresults = results.edgesresults
        ids_trip_dynaanalysis = self.parent.trips.get_routes().ids_trip[routesresults_matched.ids_route[ids_routesresults_matched_dyn]]
##        print ids_trip_dynaanalysis
        connectionsresults_map = self.get_connectionsresults_map()
        nodesresults_map = self.get_nodesresults_map()
        edgesresults_map = self.get_edgesresults_map()
        for id_trip_dynaanalysis, i in zip(ids_trip_dynaanalysis, ids_routesresults_matched_dyn):
            
            ids_connection_dyn = self.parent.trips.connections_list_dyn[id_trip_dynaanalysis]
##            print ids_connection_dyn
            ids_edge_dyn = self.parent.trips.edges_list_dyn[id_trip_dynaanalysis]
##            print  ids_edge_dyn
            ids_node_dyn = self.parent.trips.nodes_list_dyn[id_trip_dynaanalysis] 
            for id_connection_dyn in ids_connection_dyn:
                
                waiting_time_connection = connectionsresults.times_wait[connectionsresults_map[id_connection_dyn]]
                
                expected_waiting_times_tot_route_matched1[i] += waiting_time_connection
                
                if connections.turns_type[id_connection_dyn] == 'left_turn':
                    expected_waiting_times_tot_route_matched_left_turn[i] += waiting_time_connection
                    if connections.are_tls[id_connection_dyn]:
                        expected_waiting_times_tot_route_matched_left_turn_tls[i] += waiting_time_connection
                if connections.turns_type[id_connection_dyn] == 'right_turn':
                    expected_waiting_times_tot_route_matched_right_turn[i] += waiting_time_connection
                    if connections.are_tls[id_connection_dyn]:
                        expected_waiting_times_tot_route_matched_right_turn_tls[i] += waiting_time_connection
                if connections.turns_type[id_connection_dyn] == 'crossing':
                    expected_waiting_times_tot_route_matched_crossing[i] += waiting_time_connection
                    if connections.are_tls[id_connection_dyn]:
                        expected_waiting_times_tot_route_matched_crossing_tls[i] += waiting_time_connection
            for id_node_dyn in ids_node_dyn:
                node_wait_time = nodesresults.times_wait[nodesresults_map[id_node_dyn]]
                expected_waiting_times_tot_route_matched2[i] += node_wait_time
                expected_waiting_times_tot_route_matched_nodes[i] += node_wait_time
                if nodes.types[id_node_dyn] == 1 or nodes.types[id_node_dyn] == 5 or nodes.types[id_node_dyn] == 9:   
                    expected_waiting_times_tot_route_matched_tls_nodes[i] += node_wait_time
                    
            for id_edge_dyn in ids_edge_dyn:       
                    edge_waiting_time = edgesresults.times_wait[edgesresults_map[id_edge_dyn]]
                    expected_waiting_times_tot_route_matched2[i] += edge_waiting_time
                    expected_waiting_times_tot_route_matched1[i] += edge_waiting_time
                    expected_waiting_times_tot_route_matched_edges[i] += edge_waiting_time
        
        routesresults_matched.expected_waiting_times_tot_route_matched1[ids_routesresults_matched] = expected_waiting_times_tot_route_matched1[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched2[ids_routesresults_matched] = expected_waiting_times_tot_route_matched2[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_left_turn[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_left_turn[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_left_turn_tls[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_left_turn_tls[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_right_turn[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_right_turn[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_right_turn_tls[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_right_turn_tls[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_crossing[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_crossing[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_crossing_tls[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_crossing_tls[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_nodes[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_nodes[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_tls_nodes[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_tls_nodes[ids_routesresults_matched]
        routesresults_matched.expected_waiting_times_tot_route_matched_edges[ids_routesresults_matched] = expected_waiting_times_tot_route_matched_edges[ids_routesresults_matched]
        
        print('  Route analyses done.')
        return True
    
    def get_connectionsresults_map(self):
        ids_connection = self.parent.get_scenario().net.connections.get_ids()
        connectionsresults = self.get_results().connectionsresults
        
        connectionsresults_map = np.zeros(np.max(ids_connection)+1, dtype = np.int32)
        for connectionresult in connectionsresults.get_ids():
           connectionsresults_map[connectionsresults.ids_connection[connectionresult]] = connectionresult
        return connectionsresults_map
    
    def get_edgesresults_map(self):
        ids_edge = self.parent.get_scenario().net.edges.get_ids()
        edgesresults = self.get_results().edgesresults
        
        edgesresults_map = np.zeros(np.max(ids_edge)+1, dtype = np.int32)
        for edgeresult in edgesresults.get_ids():
           edgesresults_map[edgesresults.ids_edge[edgeresult]] = edgeresult
        return edgesresults_map
    
    def get_nodesresults_map(self):
        ids_node =self.parent.get_scenario().net.nodes.get_ids()
        nodesresults = self.get_results().nodesresults
        nodesresults_map = np.zeros(np.max(ids_node)+1, dtype = np.int32)
        
        for noderesult in nodesresults.get_ids():
           nodesresults_map[nodesresults.ids_node[noderesult]] = noderesult
        return nodesresults_map
    
    def do_overlap(self):
        """
        Only overlap calculations.
        """
        print('Routesanalyzer.do_overlap')
        # results
        results = self.get_results()
        routesresults_shortest = results.routesresults_shortest
        routesresults_matched = results.routesresults_matched
        
        
        

        # links
        mapmatching = self.parent
        trips = mapmatching.trips
        
        routes = trips.get_routes()
        scenario = mapmatching.get_scenario()
        edges = scenario.net.edges
        lanes = scenario.net.lanes
        nodes = scenario.net.nodes
        ids_tonode = edges.ids_tonode
        nodetypes = nodes.types
        tlstype = nodetypes.choices['traffic_light']
        id_mode_ped = scenario.net.modes.get_id_mode('pedestrian')
        get_pos_from_coord = edges.get_pos_from_coord
        
        vtypes = scenario.demand.vtypes
        points = mapmatching.points
        timestamps = points.timestamps
        
        priority_max_low = self.priority_max_low
        timeloss_intersection = self.timeloss_intersection
        timeloss_tl = self.timeloss_tl
        
        distancesmap = mapmatching.get_distancesmap(is_check_lanes = False)
        accesslevelsmap = mapmatching.get_accesslevelsmap()
        priorities = edges.priorities
        
        
        ids_trip_sel = trips.get_ids_selected()
        ids_route_sel = trips.ids_route_matched[ids_trip_sel]
        inds_valid = np.flatnonzero(ids_route_sel>0)
        
        ids_route = ids_route_sel[inds_valid]
        ids_trip = ids_trip_sel[inds_valid]
        ids_route_shortest = trips.ids_route_shortest[ids_trip]
        
        #print '  ids_route_fastest',ids_route_fastest
        #print '  ids_route_fastest',trips.ids_route_fastest.get_value()
        ids_vtype = trips.ids_vtype[ids_trip]
        ids_mode = vtypes.ids_mode[ids_vtype]
        
        
        #ind = 0
        if len(ids_trip)==0:
            print('WARNING: no trips selected.')
            return True
        
        print('  analyzing %d trips'%len(ids_trip))
        
        routesresults_shortest.clear()
        #routesresults_matched.clear()
       
        
        ids_res = routesresults_matched.get_ids()#add_rows(n=len(ids_trip),)
        routesresults_shortest.add_rows( ids =ids_res)
        
        
        
        
        for id_trip, id_route_matched, id_route_shortest,id_mode, id_res, dist_gps, duration_gps\
                in zip( ids_trip, 
                        ids_route, 
                        ids_route_shortest, 
                        ids_mode, 
                        ids_res, 
                        trips.lengths_gpsroute_matched[ids_trip], 
                        trips.durations_route_matched[ids_trip]):
            
            
            if 0:
                print(70*'-')
                print('id_trip',id_trip,'Routes: id_matched',id_route_matched,'id_shortest',id_route_shortest,'id_fastest',id_route_fastest)
                
            #ids_point = trips.ids_points[id_trip]
            distances = distancesmap[id_mode]
            
            # here we cut of first edge for analysis
            ids_edge_matched = np.array(routes.ids_edges[id_route_matched], dtype = np.int32)
            ids_edge = routes.ids_edges[id_route_matched]
           

            # do edge by edge analyses if at least more than 1 edge
            n_matched = len(ids_edge)
            

            
            ## save final results routesresults_matched
            
            #print '  AFTER:'    
            #print '  routesresults_matched.ids_route',routesresults_matched.ids_route.get_value()
            #print '  routesresults_matched.ids_pointedges',routesresults_matched.ids_pointedges.get_value()
            #print '  routesresults_matched.pointsposition',routesresults_matched.pointspositions.get_value()
                    
            if id_route_shortest>=0:
                ids_edge_shortest = np.array(routes.ids_edges[id_route_shortest], dtype = np.int32)[1:]
                accesslevels_shortest = accesslevelsmap[id_mode][ids_edge_shortest]
                priorities_shortest = priorities[ids_edge_shortest]
                nodetypes_shortest = nodetypes[ids_tonode[ids_edge_shortest]]
                
                # common edges of matched and shaortest path
                ids_edge_overlap = list(set(ids_edge_matched).intersection(ids_edge_shortest))
                
                # edges that are in the shortest path, but not in the matched path
                ids_edge_shortest_nonoverlap = np.array(list(set(ids_edge_shortest).difference(ids_edge_matched)),dtype = np.int32)
                
                # edges that are on the matched path, but not on the shortest path
                ids_edge_match_nonoverlap = np.array(list(set(ids_edge_matched).difference(ids_edge_shortest)),dtype = np.int32)
                
                dist_shortest = np.sum(distances[ids_edge_shortest])
    
                    
                n_nodes_shortest = len(nodetypes_shortest)
                n_tls_shortest = np.sum(nodetypes_shortest ==tlstype)
                
                 
                
                routesresults_shortest.set_row( id_res,
                                                ids_route = id_route_shortest,
                                                distances = dist_shortest,
                                                #durations = dist_shortest/linespeed_matched+timeloss_intersection * n_nodes_shortest + timeloss_tl * n_tls_shortest,
                                                lengths_mixed = np.sum(distances[ids_edge_shortest[accesslevels_shortest==1]]),
                                                lengths_exclusive = np.sum(distances[ids_edge_shortest[accesslevels_shortest==2]]),
                                                lengths_contrary = np.sum(distances[ids_edge_shortest[accesslevels_shortest==-2]]),
                                                lengths_low_priority = np.sum(distances[ids_edge_shortest[priorities_shortest <= priority_max_low]]),
                                                numbers_nodes = n_nodes_shortest,
                                                numbers_nodes_tls = n_tls_shortest,
                                                numbers_prioritychange = np.sum(np.logical_not(priorities_shortest[:-1],priorities_shortest[1:])),
                                                lengths_overlap_matched = np.sum(distances[ids_edge_overlap]),# common edges of shortest and matched
                                                )
                                                
                
                
                
                
        
        
        print('  Route overlap analyses done.')
        return True

class PtRoutesresults(am.ArrayObjman):
    def __init__(self, ident, parent,**kwargs):
            self._init_objman(ident, parent=parent,
                              name='PT routes results',
                              info='Stop to stop Public transport route results.',
                              xmltag=('modesods', 'modeods', 'ids_mode'), **kwargs)
            self._init_attributes()
    
    def _init_attributes(self):
        
            ptlines = self.parent.parent.parent.get_scenario().demand.ptlines
            ptlinks = ptlines.get_ptlinks()
            net = self.parent.parent.get_scenario().net
    
            
            self.add_col(am.IdsArrayConf( 'ids_link', ptlinks, 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'ID link', 
                                            info = 'ID of Public transport link.',
                                            ))

            self.add_col(am.IdsArrayConf( 'ids_fromstop', net.ptstops, 
                                            groupnames = ['state'], 
                                            name = 'ID stop from', 
                                            info = 'ID of stop where the link starts.',
                                            xmltag = 'from',
                                            ))
    
            self.add_col(am.IdsArrayConf( 'ids_tostop', net.ptstops,
                                            groupnames = ['state'], 
                                            name = 'ID stop to', 
                                            info = 'ID of stop where the link ends.',
                                            xmltag = 'to',
                                            ))
                                            
            self.add_col(am.ArrayConf('tripnumbers', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Trip number',
                                    info = 'Number of persons travelled between these stops.',
                                    )) 

            
            
class PtLinesresults(am.ArrayObjman):
    def __init__(self, ident, parent, 
                             name = 'PT Lines results', 
                             info = 'Table with results from analysis of Public transport lines.',
                             **kwargs):
        
        self._init_objman(  ident = ident, 
                            parent = parent, # main results object
                            info = info, 
                            name = name, 
                            **kwargs)
        

        ptlines = self.parent.get_scenario().demand.ptlines
        
                                                    
        self.add_col(am.IdsArrayConf( 'ids_line', ptlines, 
                                            groupnames = ['state'], 
                                            is_index = True,
                                            name = 'ID line', 
                                            info = 'ID of Public transport line.',
                                            ))
        self._init_attributes()
    
    
    
    def _init_attributes(self):
        
        self.add_col(am.ArrayConf('tripnumbers_tot', default = 0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Total trip number',
                                    info = 'Total number of persons travelled on this PT line.',
                                    )) 
                                    
        self.add_col(am.ArrayConf('tripdistances_tot', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Total trip distance',
                                    unit = 'm',
                                    info = 'Total distance persons travelled on this PT line.',
                                    ))
                                    
        
        self.add_col(am.ArrayConf('triptimes_tot', default = 0.0,
                                    dtype = np.float32,
                                    groupnames = ['results'], 
                                    name = 'Total travel time',
                                    unit = 's',
                                    info = 'Total time persons spent on this PT line.',
                                    )) 
        
        self.add_col(cm.ObjsConf('ptroutesresults',
                                groupnames=['results'],
                                name='PT route results',
                                info='Stop-to-stop results of this PT line.',
                                ))
                                
       #id_inter = self.add_row(times_start=t_start, times_end=t_end,
       #                             ids_activitytype_orig=id_activitytype_orig,
       #                             ids_activitytype_dest=id_activitytype_dest,
       #                             )
       #odmodes = OdModes((self.odmodes.attrname, id_inter), parent=self,
       #                       modes=self.get_net().modes, zones=self.get_zones())
       #self.odmodes[id_inter] = odmodes
    
    def get_flowdiagramm(self, id_line, is_add_similar = False):
        #print 'get_flowdiagramm'
        ptlines = self.parent.get_scenario().demand.ptlines
        ptstops = self.parent.get_scenario().net.ptstops
        linerootname = ptlines.linenames[id_line].split('_')[0]
        id_res_line = self.ids_line.get_id_from_index(id_line)
        ptrouteresults_line = self.ptroutesresults[id_res_line]
        
        ids_routeres =  ptrouteresults_line.get_ids()
        ids_stoptuple_line = []
        tripnumbers_line = []
        for id_fromstop, id_tostop in zip(ptrouteresults_line.ids_fromstop[ids_routeres],ptrouteresults_line.ids_tostop[ids_routeres]):
            ids_stoptuple_line.append((id_fromstop, id_tostop))

        tripnumbers_line = ptrouteresults_line.tripnumbers[ids_routeres]
        
        #print '  ids_stoptuple_line',len(ids_stoptuple_line),ids_stoptuple_line
        #print '  tripnumbers_line',tripnumbers_line
        if is_add_similar:
            for id_line_other in self.ids_line[self.get_ids()]:
                if id_line_other != id_line:
                    linerootname_other = ptlines.linenames[id_line_other].split('_')[0]
                    
                    if linerootname == linerootname_other:
                        id_res_other = self.ids_line.get_id_from_index(id_line_other)
                        ptrouteresults_other = self.ptroutesresults[id_res_other]
                        ids_routeres_other =  ptrouteresults_other.get_ids()
                        ids_stoptuple_line_other = []
                        for id_fromstop, id_tostop in zip(ptrouteresults_other.ids_fromstop[ids_routeres_other],ptrouteresults_other.ids_tostop[ids_routeres_other]):
                            ids_stoptuple_line_other.append((id_fromstop, id_tostop))
                        
                        # first ectract common pattern then test for sublist
                        # if sublist use only common part 
                        ia,ib,n = find_longest_common_sequence_index(ids_stoptuple_line, ids_stoptuple_line_other)
                        if n >0:
                            ids_stoptuple_line = ids_stoptuple_line[ia:ia+n]
                            tripnumbers_line = tripnumbers_line[ia:ia+n]
                            tripnumbers_line += ptrouteresults_other.tripnumbers[ids_routeres_other][ib:ib+n]
                            
        #print '  ids_stoptuple_line',len(ids_stoptuple_line),ids_stoptuple_line
        #print '  tripnumbers_line',tripnumbers_line               
        return ids_stoptuple_line, tripnumbers_line
        
        
                 

class PtRoutesanalyzer(Process):
    def __init__(self, ident, mapmatching, results = None,  logger = None, **kwargs):
        print('Routesanalyzer.__init__')
        
        # TODO: let this be independent, link to it or child??
        
        if results is None:
            self._results = Matchresults(   'matchresults',mapmatching)
        else:
            self._results =  results
            
        self._init_common(  ident, 
                            parent = mapmatching,
                            name = 'PT Routes Analyzer', 
                            logger = logger,
                            info ='Analyzer of Public transport lines and routes.',
                            )
        
        
        attrsman = self.set_attrsman(cm.Attrsman(self))
        
        
                            
        #self.timeloss_intersection = attrsman.add(cm.AttrConf( 'timeloss_intersection',kwargs.get('timeloss_intersection',5.0),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Timeloss intersection', 
        #                    info = 'Estimated timeloss at intersections due to waiting or reduction of speed. This value is used to estimate travel time of route alternatives.',
        #                    ))
        
        
                            
        self.flowfactor = attrsman.add(cm.AttrConf( 'flowfactor',kwargs.get('flowfactor',10.0),
                            groupnames = ['options'], 
                            perm='rw', 
                            name = 'Flow factor', 
                            info = 'This is the factor which will be multiplied with the edge probability to estimate the absolute vehicle flows.',
                            )) 
         
        #self.is_oldversion = attrsman.add(cm.AttrConf( 'is_oldversion',kwargs.get('is_oldversion',False),
        #                    groupnames = ['options'], 
        #                    perm='rw', 
        #                    name = 'Old mapmatch?', 
        #                    info = 'If True, analyze for old mapmatch version.',
        #                    ))
        
                                                
        results = self.get_results()
        results.config(PtLinesresults('ptlinesresults',results))
        
                            
    def get_results(self):
        return   self._results 
    
    
    
    def do(self):
        print('Routesanalyzer.do')
        # results
        results = self.get_results()
        ptlinesresults = results.ptlinesresults
        
        # link object
        mapmatching = self.parent
        trips = mapmatching.trips
        routes = trips.get_routes()
        
        scenario = mapmatching.get_scenario()
        demand = scenario.demand
        ptlines = demand.ptlines
        ptlinks = ptlines.get_ptlinks()
        
        
        # get valid trip ids
        ids_trip_sel = trips.get_ids_selected()
        
        ids_route_sel = trips.ids_route_matched[ids_trip_sel]
        inds_valid = np.flatnonzero(ids_route_sel>0)
        
        ids_route = ids_route_sel[inds_valid]
        ids_trip = ids_trip_sel[inds_valid]
        
        numbercounter_lines = {}#np.zeros(len(ptlines)+1,dtype = np.int32)
        numbercounter_links = {}#np.zeros(len(ptlinks)+1,dtype = np.int32)
        distancecounter_lines = {}#np.zeros(len(ptlines)+1,dtype = np.int32)
        #distancecounter_links = {}#np.zeros(len(ptlinks)+1,dtype = np.int32)
        timecounter_lines = {}#np.zeros(len(ptlines)+1,dtype = np.int32)
        #timecounter_links = {}#np.zeros(len(ptlinks)+1,dtype = np.int32)
        
        ids_ptlinks = routes.ids_ptlinks[ids_route]
        
        ptlinktypenames = ptlinks.types.choices
        #type_enter = ptlinktypenames['enter']
        type_transit = ptlinktypenames['transit']
        
        
        for id_trip, id_route, ids_link in zip(ids_trip, ids_route, ids_ptlinks):
            if ids_link is not None:
                for id_link, id_line, type_link, dist_link, duration_link in\
                 zip(ids_link, ptlinks.ids_line[ids_link],ptlinks.types[ids_link],
                        ptlinks.lengths[ids_link],ptlinks.durations[ids_link]):
                            
                    if type_link == type_transit:
                        if id_line not in numbercounter_lines:
                            print(id_line)
                            numbercounter_lines[id_line] = 0
                            distancecounter_lines[id_line] = 0.0
                            timecounter_lines[id_line]  = 0.0
                        
                        numbercounter_lines[id_line] += 1
                        distancecounter_lines[id_line] += dist_link
                        timecounter_lines[id_line] += duration_link
                        
                        if id_link not in numbercounter_links:
                            numbercounter_links[id_link] = 0
                            
                        numbercounter_links[id_link] += 1
        
        ids_line = list(numbercounter_lines.keys())
        ids_lineres = ptlinesresults.add_rows(n=len(ids_line),ids_line = ids_line)
        id_line_to_id_lineres = {}
        
        map_stops_to_routeres = {}
        for id_line,linename, id_lineres  in zip(ids_line, ptlines.linenames[ids_line], ids_lineres):
            
            ptlinesresults.tripnumbers_tot[id_lineres] = numbercounter_lines[id_line]
            ptlinesresults.tripdistances_tot[id_lineres] = distancecounter_lines[id_line]
            ptlinesresults.triptimes_tot[id_lineres] = timecounter_lines[id_line]
            
            # initialize PT routeresults
            
            ptrouteresults = PtRoutesresults('route_'+linename,ptlinesresults)
            ptlinesresults.ptroutesresults[id_lineres] = ptrouteresults
            
            map_stops_to_routeres[id_line] = {}
            for id_fromstop, id_tostop in zip(ptlines.ids_stops[id_line][0:-1],ptlines.ids_stops[id_line][1:]):
                id_routeres = ptrouteresults.add_row(   ids_fromstop = id_fromstop, 
                                                        ids_tostop = id_tostop,
                                                        )
                map_stops_to_routeres[id_line][(id_fromstop, id_tostop)] = id_routeres      
                #print '    init id_line %d (%s) id_fromstop %d id_tostop %d'%(id_line,ptlines.linenames[id_line],id_fromstop, id_tostop)
            
            id_line_to_id_lineres[id_line] = id_lineres
        
        ids_link = list(numbercounter_links.keys())  
        for id_link, id_line, id_fromstop, id_tostop, n in zip(\
                        ids_link, ptlinks.ids_line[ids_link],
                        ptlinks.ids_fromstop[ids_link],ptlinks.ids_tostop[ids_link],
                        list(numbercounter_links.values())):
            
            ptrouteresults = ptlinesresults.ptroutesresults[id_line_to_id_lineres[id_line]]
            if (id_fromstop, id_tostop) in map_stops_to_routeres[id_line]:
                id_routeres = map_stops_to_routeres[id_line][(id_fromstop, id_tostop)]
##                print '    Add',n,'trips to id_line %d (%s) id_fromstop %d id_tostop %d'%(id_line,ptlines.linenames[id_line],id_fromstop, id_tostop)
                ptrouteresults.ids_link[id_routeres] = id_link
                ptrouteresults.tripnumbers[id_routeres] = n
         
        return True            

  
def routes_to_shapefile(mapmatching,results, filepath, 
                        dataname= 'routeresshapedata', name = 'Route result shape data', 
                        log = None):
    """
    Writes routes to file.
    """
    print('\n routes_to_shapefile for mapmatching')
    net = mapmatching.get_scenario().net
    edges = net.edges
    trips = mapmatching.trips
    routes = trips.get_routes()       
    shapedata = shapeformat.Shapedata(trips, dataname, 
                                    filepath = filepath,
                                    name=name, 
                                    projparams_shape = net.get_projparams(),
                                    offset = net.get_offset(), 
                                    shapetype = 3,# Polygons
                                    log = log,
                                    )
    
    fieldlength_default = 32
    attrlist = [\
        ('id','id','ID_TRIP','N',fieldlength_default,0),
        ('timestamps','','TIMESTAMP','C',fieldlength_default,0),
        ('durations_gps','','DUR_GPS','N',fieldlength_default,0),
        ('lengths_route_matched','','LEN_MATCH','N',fieldlength_default,3),
        ('lengths_route_shortest','','LEN_SHORT','N',fieldlength_default,3),
        ('lengths_route_matched_mixed','','LEN_MIX','N',fieldlength_default,3),
        ('lengths_route_matched_exclusive','','LEN_EXCL','N',fieldlength_default,3),
        ('lengthindexes','','LENGTHIND','N',fieldlength_default,0),
        ('errors_dist','','ERR_DIST','N',fieldlength_default,0),
        ('times_computation','','TIME_COMP','N',fieldlength_default,0),
        #('','','','N',fieldlength_default,3),
        ]
            

    for attr in attrlist:
        shapedata.add_field(attr[2:])
    
    ids_trip = trips.select_ids(trips.ids_route_matched.get_value()>-1)
    
    ids_shape = shapedata.add_rows(len(ids_trip))
    #print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
    shapedata.ID_TRIP[ids_shape] = ids_trip

    
    for id_shape, id_trip, ids_edge in zip(ids_shape, ids_trip, routes.ids_edges[trips.ids_route_matched[ids_trip]]):
        #print '  export id_route',id_route
        #route = traces.routes.get(id_route)
        if len(ids_edge)>0:
            polyline = []
            for shape in edges.shapes[ids_edge]:
                #print '    shape',type(shape),shape
                #print '    polyline',type(polyline),polyline
                polyline += shape.tolist()
            
            shapedata.shapes[id_shape] = polyline
            
    for netattrname, gettype, shapeattrname,x1,x2,x3 in attrlist:
        if netattrname not in ('id','timestamps'):
            getattr(shapedata,shapeattrname)[ids_shape] = getattr(trips,netattrname)[ids_trip]        

    for id_shape,  timestamp in zip(ids_shape,trips.timestamps[ids_trip]):
        shapedata.TIMESTAMP[id_shape] = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime(timestamp))
    
    shapedata.adjust_fieldlength() 
    shapedata.export_shapefile()
    return True

def edgesresults_to_shapefile(mapmatching,results, filepath, 
                        dataname= 'edgeresshapedata', name = 'Edge result shape data', 
                        log = None):
    """
    Writes edge results of mapmatching to shape-file.
    """
    print('\n edgesresults_to_shapefile for mapmatching')
    net = mapmatching.get_scenario().net
    edges = net.edges
    edgesresults = results.edgesresults
    #trips = mapmatching.trips
    #routes = trips.get_routes()     
    shapedata = shapeformat.Shapedata(edgesresults, dataname, 
                                    filepath = filepath,
                                    name=name, 
                                    projparams_shape = net.get_projparams(),
                                    offset = net.get_offset(), 
                                    shapetype = 3,# Polygons
                                    log = log,
                                    )
    
    fieldlength_default = 32
    attrlist = [\
        ('id','id','ID_ARC','N',fieldlength_default,0),
        ('speeds_average','','SPEED_AV','N',fieldlength_default,5),
        ('speeds_inmotion','','SPEED_MOV','N',fieldlength_default,5),
        ('durations_tot_matched','','TIME_TOT','N',fieldlength_default,0),
        ('numbers_tot_matched','','COUNTER','N',fieldlength_default,0),
        ('times_wait','','TIME_WAIT','N',fieldlength_default,5),
        #('','','','N',fieldlength_default,3),
        ]
    
 
    for attr in attrlist:
        shapedata.add_field(attr[2:])
    
    
    ids_edgeres = edgesresults.select_ids(edgesresults.numbers_tot_matched.get_value()>0)
    ids_edge = edgesresults.ids_edge[ids_edgeres]
    
    ids_shape = shapedata.add_rows(len(ids_edge))
    #print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
    shapedata.ID_ARC[ids_shape] = ids_edge
    shapedata.shapes[ids_shape] = edges.shapes[ids_edge]
        
    
    for netattrname, gettype, shapeattrname,x1,x2,x3 in attrlist:
        if netattrname not in ('id',):
            getattr(shapedata,shapeattrname)[ids_shape] = getattr(edgesresults,netattrname)[ids_edgeres]
        
 
            
    shapedata.adjust_fieldlength() 
    shapedata.export_shapefile()
    return True

def points_to_shapefile(mapmatching, filepath, dataname= 'pointshapedata', 
                         parent = None, log = None):
    """
    Export GPS points to shapefile.
    """
    net = mapmatching.get_scenario().net
    points = mapmatching.points
    trips = mapmatching.trips
    shapedata = shapeformat.Shapedata(parent,dataname, name = 'GPS points shape data', 
                                    filepath=filepath, 
                                    shapetype = shapeformat.SHAPETYPES['Point'],
                                    projparams_shape = net.get_projparams(),
                                    offset = net.get_offset(), log = log )
    
    #attrname, ftype, flen, fdigit = field
    attrlist = [\
    ('id','id','ID_POINT','N',12,0),
    ('timestamps','val','TIME','N',32,0),
    ('ids_trip','id','ID_TRIP','N',32,0),
    #('radii','val','RADIUS','N',5,3),
    ]
    # 
    
    
    print('nodes_to_shapefile',filepath)
    
    for attr in attrlist:
        shapedata.add_field(attr[2:])
    

    ids_point = points.select_ids(trips.ids_route_matched[points.ids_trip.get_value()]>-1)
    
    ids_shape = shapedata.add_rows(len(ids_point))
    #print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
    shapedata.ID_POINT[ids_shape] = ids_point
    shapedata.coords[ids_shape] = points.coords[ids_point]
    
        
    
    # copy rest of attributes
    for netattrname, gettype, shapeattrname,x1,x2,x3 in attrlist:
        if netattrname not in ('id',):
            getattr(shapedata,shapeattrname)[ids_shape] = getattr(points,netattrname)[ids_point]
        
    shapedata.adjust_fieldlength() 
    shapedata.export_shapefile()
    return True
