1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    mapmatching.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16"""
17This plugin provides methods to match GPS traces to a SUMO road network and to generate routes.
18
19The general structure is as follows:
20GPSPoints
21routes
22
23Traces
24
25"""
26import os
27import sys
28from xml.sax import saxutils, parse, handler
29
30import time
31import string
32import numpy as np
33from xml.sax import saxutils, parse, handler
34
35if __name__ == '__main__':
36    try:
37        APPDIR = os.path.dirname(os.path.abspath(__file__))
38    except:
39        APPDIR = os.path.dirname(os.path.abspath(sys.argv[0]))
40    SUMOPYDIR = os.path.join(APPDIR, '..', '..')
41    sys.path.append(SUMOPYDIR)
42
43
44from coremodules.modules_common import *
45import agilepy.lib_base.classman as cm
46import agilepy.lib_base.arrayman as am
47import agilepy.lib_base.xmlman as xm
48#from agilepy.lib_base.misc import get_inversemap
49from agilepy.lib_base.geometry import get_dist_point_to_segs
50from agilepy.lib_base.processes import Process  # ,CmlMixin,ff,call
51from coremodules.network.network import SumoIdsConf
52from coremodules.network import routing
53from coremodules.demand.demandbase import DemandobjMixin
54from coremodules.demand.demand import Trips, Routes
55from coremodules.misc import shapeformat
56try:
57    try:
58        import pyproj
59    except:
60        from mpl_toolkits.basemap import pyproj
61    try:
62        from shapely.geometry import Polygon, MultiPolygon, MultiLineString, Point, LineString, MultiPoint, asLineString, asMultiPoint
63        from shapely.ops import cascaded_union
64    except:
65        print 'Import error: No shapely module available.'
66except:
67    print 'Import error: in order to run the traces plugin please install the following modules:'
68    print '   mpl_toolkits.basemap and shapely'
69    print 'Please install these modules if you want to use it.'
70    print __doc__
71    raise
72
73TRIPPUROPSES = {'unknown': -1,
74                'HomeToWork': 1,
75                'WorkToHome': 2,
76                'HomeToSchool': 3,
77                'SchoolToHome': 4,
78                'SchoolToHome': 5,
79                'Leisure': 6,
80                'Other': 7,
81                }
82
83OCCUPATIONS = {'unknown': -1,
84               'worker': 1,
85               'student': 2,
86               'employee': 3,
87               'public employee': 4,
88               'selfemployed': 5,
89               'pensioneer': 6,
90               'other': 7
91               }
92
93GENDERS = {'male': 0, 'female': 1, 'unknown': -1}
94
95DEVICES = {'unknown': -1,
96           'cy-android': 1,
97           'cy-windows': 2,
98           'cy-ios': 3,
99           'cy-web-gpx': 4,
100           'cy-web-manual': 5,
101           'Other': 7
102           }
103
104WEEKDAYCHOICES = {'Monday-Friday': [0, 1, 2, 3, 4],
105                  'Monday-Saturday': [0, 1, 2, 3, 4, 5],
106                  'Saturday, Sunday': [5, 6],
107                  'all': [0, 1, 2, 3, 4, 5, 6],
108                  }
109
110COLOR_SHORTEST_ROUTE = np.array([108, 183, 0,  0.6*255], np.float32)/255
111COLOR_MATCHED_ROUTE = np.array([255, 210, 2,  0.6*255], np.float32)/255
112COLOR_FASTEST_ROUTE = np.array([198, 0, 255, 0.6*255], np.float32)/255
113# Structure GPS traces
114
115
116# 2016
117
118# UserID	                    TripID	                    TimeStamp	Start DT	                Distance	 ECC	 AvgSpeed	 TrackType	 Sex	 Year	 Profession	 Frequent User	 ZIP	 Source	  TypeOfBike	 TipeOfTrip	 Max Spd
119# 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
120# 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
121# 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
122# 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
123# 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
124# 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
125
126
127# csv 2016
128# TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
129# 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
130# 5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid
131# timestamp: 1464160924 after 1970
132
133
134# 2015 csv
135
136# workouts
137# UserID	 TripID	 TimeStamp	 Start DT	  Distance	 AvgSpeed	 TrackType	 Sex	 Year	 Profession	 Frequent User	 ZIP
138# 54eb068de71f393530a9a74d	54eb0737e71f394c2fa9a74d	1424692504	Mon, 23 Feb 2015 11:55:04 GMT	0	0	urban bicycle	M	1987	Developer	no
139# 54eb9374e71f39f02fa9a750	5505cb04e71f39542e25e2d4	1426442994	Sun, 15 Mar 2015 18:09:54 GMT	0	0.7	urban bicycle	M	1974	Worker	yes	40128
140
141
142#TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
143# 54eb0737e71f394c2fa9a74d,1424692509,"Mon, 23 Feb 2015 11:55:09 GMT",44.499096,11.361185,49.419395,0,0.000815,start
144
145
146# https://docs.python.org/2/library/time.html
147# >>> t = time.mktime((2017,03,07,13,46,0,0,0,0))
148# >>> lt = time.localtime(t)
149# >>> time.strftime("%a, %d %b %Y %H:%M:%S +0000", lt)
150#'Tue, 07 Mar 2017 13:46:00 +0000'
151# >>> time.strftime("%a, %d %b %Y %H:%M:%S", lt)
152#'Tue, 07 Mar 2017 13:46:00'
153
154def load_results(filepath, parent=None, logger=None):
155    # typically parent is the scenario
156    results = cm.load_obj(filepath, parent=parent)
157    if logger is not None:
158        results.set_logger(logger)
159    return results
160
161
162def calc_seconds(t_data,
163                 sep_date_clock=' ', sep_date='-', sep_clock=':',
164                 is_float=False):
165    """
166    Returns time in seconds after 1/1/1970.
167    Time format for time data string used:
168        2012-05-02 12:57:08.0
169    """
170
171    if len(t_data.split(sep_date_clock)) != 2:
172        return -1
173    (date, clock) = t_data.split(sep_date_clock)
174
175    if (len(clock.split(sep_clock)) == 3) & (len(date.split(sep_date)) == 3):
176
177        (year_str, month_str, day_str) = date.split(sep_date)
178        # print '  year_str,month_str,day_str',year_str,month_str,day_str
179        (hours_str, minutes_str, seconds_str) = clock.split(sep_clock)
180        # print '  hours_str,minutes_str,seconds_str',hours_str,minutes_str,seconds_str
181
182        t = time.mktime((int(year_str), int(month_str), int(day_str),
183                         int(hours_str), int(minutes_str), int(float(seconds_str)), -1, -1, -1))
184
185        # print 'calc_seconds',t
186        # print '  t_data'
187        # print '  tupel',int(year_str),int(month_str),int(day_str), int(hours_str),int(minutes_str),int(float(seconds_str)),0,0,0
188        if is_float:
189            return t
190        else:
191            return int(t)
192    else:
193        return -1
194
195
196def get_routelinestring(route, edges):
197        # print 'get_routelinestring'
198        # TODO: we could make the np.sum of shapes if shapes where lists
199    shape = []
200    for id_edge in route:
201            # print '    ',edges.shapes[id_edge ],type(edges.shapes[id_edge ])
202        shape += list(edges.shapes[id_edge])
203    # print '  ',shape
204
205    return LineString(shape)
206
207
208def get_boundary(coords):
209    # print 'get_boundary',len(coords),coords.shape
210    # print '  coords',coords
211    if len(coords) == 0:
212        return [0, 0, 0, 0]
213    else:
214        x_min, y_min = coords[:, :2].min(0)
215        x_max, y_max = coords[:, :2].max(0)
216
217        return [x_min, y_min, x_max, y_max]
218
219
220class BirgilMatcher(Process):
221    def __init__(self, ident, mapmatching,  logger=None, **kwargs):
222        print 'BirgilMatcher.__init__'
223
224        # TODO: let this be independent, link to it or child??
225
226        self._init_common(ident,
227                          parent=mapmatching,
228                          name='Birgillito Map matching',
229                          logger=logger,
230                          info='Birgillito Map matching.',
231                          )
232
233        attrsman = self.set_attrsman(cm.Attrsman(self))
234
235        self.width_buffer_max = attrsman.add(cm.AttrConf('width_buffer_max', kwargs.get('width_buffer_max', 40.0),
236                                                         groupnames=['options'],
237                                                         perm='rw',
238                                                         name='Max. buffer width',
239                                                         unit='m',
240                                                         info='GPS points are only valid if they are within the given maximum buffer width distant from a network edge.',
241                                                         ))
242
243        self.n_points_min = attrsman.add(cm.AttrConf('n_points_min', kwargs.get('n_points_min', 5),
244                                                     groupnames=['options'],
245                                                     perm='rw',
246                                                     name='Min. point number',
247                                                     info='Minimum number of valid GPS points. Only if this minimum number is reached, a map-matching attempt is performed.',
248                                                     ))
249
250        # self.dist_min = attrsman.add(cm.AttrConf( 'dist_min',kwargs.get('dist_min',3.0),
251        #                    groupnames = ['options'],
252        #                    name = 'Min. dist',
253        #                    unit = 'm',
254        #                    info = 'Minimum distance used in the calculation of the edge cost function.',
255        #                    ))
256
257        self.weight_cumlength = attrsman.add(cm.AttrConf('weight_cumlength', kwargs.get('weight_cumlength', 0.01),
258                                                         groupnames=['options'],
259                                                         name='Cumlength weight',
260                                                         info='Cost function weight of cumulative length.',
261                                                         ))
262
263        self.weight_angle = attrsman.add(cm.AttrConf('weight_angle', kwargs.get('weight_angle', 20.0),
264                                                     groupnames=['options'],
265                                                     name='Angle weight',
266                                                     info='Cost function weight of angular coincidence.',
267                                                     ))
268
269        self.weight_access = attrsman.add(cm.AttrConf('weight_access', kwargs.get('weight_access', 5.0),
270                                                      groupnames=['options'],
271                                                      name='Access weight',
272                                                      info='Cost function weight of access level for matched mode.',
273                                                      ))
274
275        self.n_routes_follow = attrsman.add(cm.AttrConf('n_routes_follow', kwargs.get('n_routes_follow', 20),
276                                                        groupnames=['options'],
277                                                        name='Followed routes',
278                                                        info='Number of routes which are followed in parallel.',
279                                                        ))
280
281        # print '  color_route',kwargs.get('color_route',COLOR_MATCHED_ROUTE),COLOR_MATCHED_ROUTE
282        self.color_route = attrsman.add(cm.AttrConf('color_route', kwargs.get('color_route', COLOR_MATCHED_ROUTE.copy()),
283                                                    groupnames=['options'],
284                                                    perm='wr',
285                                                    metatype='color',
286                                                    name='Route color',
287                                                    info='Color of matched routes.',
288                                                    ))
289
290    def do(self):
291        logger = self.get_logger()
292        timeconst = 0.95
293
294        trips = self.parent.trips
295        #routes = trips.get_routes()
296        edges = self.get_edges()
297        vtypes = self.parent.get_scenario().demand.vtypes
298
299        fstar = edges.get_fstar(is_return_arrays=True, is_ignor_connections=True)
300        times = edges.get_times(id_mode=2, is_check_lanes=False, speed_max=5.555)
301        ids_trip = trips.get_ids_selected()
302
303        ids_mode = vtypes.ids_mode[trips.ids_vtype[ids_trip]]
304
305        n_trips = len(ids_trip)
306        logger.w('Start mapmatching of %d GPS traces with Birgillito method' % n_trips)
307
308        distancesmap = self.parent.get_distancesmap()
309        accesslevelsmap = self.parent.get_accesslevelsmap()
310
311        n_trip_matched = 0
312        time_match_trace_av = 3.0  # initial guess of match time
313
314        # ,vtypes.speed_max[ids_vtype]
315        for id_trip, ids_point, id_mode in zip(ids_trip, trips.ids_points[ids_trip], ids_mode):
316            tick_before = time.time()
317            route, length_route, length_route_mixed, length_route_exclusive, duration_gps, lengthindex, err_dist, t_match, ids_point_edgeend, is_connected = \
318                self.match_trip_birgil(id_trip, ids_point, fstar, distancesmap[id_mode], accesslevelsmap[id_mode])
319
320            trips.set_matched_route(id_trip, route,
321                                    length_route,
322                                    length_route_mixed,
323                                    length_route_exclusive,
324                                    duration_gps,
325                                    lengthindex,
326                                    err_dist,
327                                    t_match,
328                                    is_connected=is_connected,
329                                    ids_point_edgeend=ids_point_edgeend)
330            n_trip_matched += 1
331
332            time_match_trace = time.time()-tick_before
333
334            time_match_trace_av = timeconst*time_match_trace_av + (1.0-timeconst)*time_match_trace
335            time_end_est = (n_trips-n_trip_matched)*time_match_trace_av
336
337            progress = 100.0*n_trip_matched/float(n_trips)
338            logger.w("Matched %d/%d traces, %.2f%% comleted. Avg match time = %.2fs, Terminated in %.1fh" %
339                     (n_trip_matched, n_trips, progress, time_match_trace_av, float(time_end_est)/3600.0), key='message')
340            logger.w(progress, key='progress')
341        return True
342
343    def get_edges(self):
344        return self.parent.get_scenario().net.edges
345
346    def match_trip_birgil(self, id_trip,  ids_point, fstar, costs, accesslevels):
347        # TODO: record time intervals
348        # calculate initial and final position on edge to get more precise
349        # triplength
350        print 79*'='
351        print 'match_trip_birgil', id_trip
352        tick = time.time()
353        routes = []
354        route = None
355        route_mindist = None
356        intervals_route = []
357        length_route = -1.0
358        length_bikeway = -1.0
359        length_mindist = -1.0
360        matchindex = -1.0
361        lengthindex = -1.0
362        err_dist = -1.0
363        err_dist_alg = -1.0
364        t_match = -1.0
365        duration_gps = -1.0
366        duration_est = -1.0
367
368        points = self.parent.points
369        #trips = self.parent.trips
370        #ids_point = trips.ids_points[id_trip]
371        #pointset = self.pointsets.get(id_trip)
372
373        # create a multi point object with points of traces
374        # should no longer be necessary
375        #tracepoints = MultiPoint(pointset.get_coords().tolist())
376
377        # make a array with time stamps of all points
378        pointtimes = points.timestamps[ids_point]
379        # segind_to_id_edge = self._segind_to_id_edge #<<<<<<<<<<<
380        coords = points.coords[ids_point][:, :2]
381
382        x1, y1, x2, y2 = self.parent.get_segvertices_xy()
383        edges = self.get_edges()
384        get_ids_edge_from_inds_seg = edges.get_ids_edge_from_inds_seg
385        get_dist_point_to_edge = edges.get_dist_point_to_edge
386        #edgehit_counts = np.zeros(len(self._net.getEdges()),int)
387        # segind_to_id_edge = self._segind_to_id_edge
388        # for p in pointset.get_coords():
389        #    print '  ',p
390        #    hitinds = get_dist_point_to_segs(p,self._x1,self._y1,self._x2,self._y2, is_ending=True)< self.width_buffer**2
391        #    edgehit_counts[segind_to_id_edge[hitinds]]+= 1
392
393        # ---------------------------------------------------------------------
394        # 1. initialization
395
396        # Set of initial edges
397
398        # create a list with lists  for each point
399        # Each list contans the edges, where the point is in the edge buffer
400        #edges_containing_point = np.zeros(len(ids_points), object)
401        #edgedists_to_point = np.zeros(len(ids_points), object)
402
403        ids_edge_initial = []
404        ids_edge_final = []
405        dists_initial = []
406        n_points = len(ids_point)
407        ind_point_initial = -1
408        ind_point_final = -1
409        t_point_initial = -1
410        t_point_final = -1
411
412        # print '  search initial edges'
413        ind_point = 0
414        for p in coords:
415            dists2 = get_dist_point_to_segs(p, x1, y1, x2, y2)
416            inds_hit = np.flatnonzero(dists2 < self.width_buffer_max**2)
417            # print '    id_point,inds_hit',ids_point[ind_point],inds_hit
418            #ids_edge = get_ids_edge_from_inds_seg(inds_hit)
419
420            if len(inds_hit) > 0:
421                if ind_point_initial < 0:
422                    # print '   inds_hit',ind_point,inds_hit
423                    ids_edge_initial = set(get_ids_edge_from_inds_seg(inds_hit))
424                    # print '    ids_edge_initial',ids_edge_initial
425                    #dists_initial = np.sqrt(dists2[inds_hit])
426                    ind_point_initial = ind_point
427                    t_point_initial = pointtimes[ind_point]
428                    # print '   ind_point, inds_hit, ind_point_initial',ind_point,inds_hit,ind_point_initial,ids_edge_initial
429                else:
430                    ids_edge_final = set(get_ids_edge_from_inds_seg(inds_hit))
431                    # print '    ids_edge_final',ids_edge_final
432                    ind_point_final = ind_point
433                    t_point_final = pointtimes[ind_point]
434                    # print '   ind_point, inds_hit, ind_point_final',ind_point,inds_hit,ind_point_initial,ids_edge_final
435
436            ind_point += 1
437
438        n_points_eff = ind_point_final - ind_point_initial
439
440        duration_gps = pointtimes[ind_point_final] - pointtimes[ind_point_initial]
441
442        # if self._logger:
443        #    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)) )
444        # print '  completed init:'
445        print '  ids_edge_initial', ids_edge_initial
446        print '  ids_edge_final', ids_edge_final
447
448        # print '  ind_point_initial,ind_point_final,n_points_eff',ind_point_initial,ind_point_final,n_points_eff
449        print '  id_point_initial=%d,id_point_final=%d,n_points_eff=%d' % (
450            ids_point[ind_point_initial], ids_point[ind_point_final], n_points_eff)
451        if (ind_point_initial < 0) | (ind_point_final < 0) | (n_points_eff < self.n_points_min):
452            print 'ABOARD: insufficient valid points'
453            return [], 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, [], False
454
455        # print '  coords',coords
456        # create initial routeset
457        routelist = []
458
459        #id_edge_unique = []
460        for id_edge in ids_edge_initial:
461
462            cost0 = get_dist_point_to_edge(coords[ind_point_initial], id_edge)
463
464            # print '  coords of initial point:',coords[ind_point_initial]
465            # print '  cost0, id_edge',cost0, id_edge
466
467            #get_ind_seg_from_id_edge(self, id_edge)
468
469            length_edge = edges.lengths[id_edge]
470            accesslevel = accesslevels[id_edge]
471            cost_tot = cost0+self.weight_cumlength * length_edge - self.weight_access * accesslevel
472            routelist.append([cost_tot, cost0, 0.0, length_edge, accesslevel,
473                              length_edge, [id_edge], [ids_point[ind_point_initial]]])
474
475        # print '  initial routelist',routelist
476
477        # ---------------------------------------------------------------------
478        # 2. main loop through rest of the points
479
480        # for i in xrange(ind_point+1, n_points):#coords[ind_point+1:]:
481        #is_connected = True
482        ind_point = ind_point_initial+1
483        length_gps = 0.0
484        while (ind_point < ind_point_final):  # & is_connected:
485            point = coords[ind_point]
486            delta_point = point-coords[ind_point-1]
487            dist_interpoint = np.sqrt(np.sum(delta_point**2))
488            phi_point = np.arctan2(delta_point[1], delta_point[0])
489            length_gps += dist_interpoint
490
491            # print 79*'_'
492            # 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)
493
494            routelist_new = []
495            ind_route = 0
496            for routeinfo in routelist:
497                costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = routeinfo
498
499                if len(ids_edge) == 0:
500                    break
501
502                length_partial += dist_interpoint
503                id_edge_last = ids_edge[-1]
504                # minimum distance point-edge
505                dist_point_edge = get_dist_point_to_edge(point, id_edge_last,
506                                                         is_detect_initial=False,
507                                                         is_detect_final=True,)
508                #dist_point_edge = self.get_dist_point_edge(coords[ind_point], id_edge_last, is_detect_final = True)
509                # if dist_point_edge is not a number (nan) the point is outside projection of edge
510
511                # print 79*'-'
512                # print '      route ',ind_route,'costs_tot',costs_tot,'dist_point_edge',dist_point_edge, len(ids_edge),id_edge_last
513                # print '        cost= %.3f, length_cum=%.3f'%(cost,length_cum,)
514                # print '        Xost= %.3f, Xength_cum=%.3f'%(cost,self.weight_cumlength *length_cum,)
515                # print '        ids_edge',ids_edge
516                # if length_partial > self.alpha * length_edge:
517                if (np.isnan(dist_point_edge)) | (dist_point_edge > self.width_buffer_max):
518                    # point is beyond edge
519
520                    # has reached end of edge
521                    length_partial = length_partial-length_edge
522
523                    # get FSTAR
524                    # print '        fstar',id_edge_last,fstar[id_edge_last]
525                    ids_edge_next_all = fstar[id_edge_last]
526
527                    # filter allowed edges, by verifying positivness of
528                    # travel costs
529                    ids_edge_next = ids_edge_next_all[costs[ids_edge_next_all] >= 0]
530                    # print '        parse ids_edge_next',ids_edge_next
531
532                    if len(ids_edge_next) == 0:
533                        # route not connected
534                        cost_edge = np.Inf  # punish route with infinite costs
535                        length_edge = np.Inf
536                        length_cum = np.Inf
537                        accesslevel = -1
538                        ids_edge_new = []
539                        ids_point_edgeend_new = []
540                    else:
541
542                        id_edge = ids_edge_next[0]
543                        accesslevel = accesslevels[id_edge]
544                        length_edge = edges.lengths[id_edge]
545                        length_cum += length_edge
546                        cost_edge = self._get_cost_birgil(point, id_edge, phi_point,
547                                                          accesslevel, get_dist_point_to_edge)
548                        #self.get_dist_point_edge(coords[ind_point], ind_edge)
549                        ids_edge_new = ids_edge + [id_edge]
550                        ids_point_edgeend_new = ids_point_edgeend + [ids_point[ind_point]]
551                        # print '      add to route id_edge',id_edge,ids_edge_new
552
553                    # save updated data of current route
554                    cost0 = cost+cost_edge
555                    cost_tot = cost0+self.weight_cumlength * length_cum
556                    routeinfo[:] = cost_tot, cost0, length_partial, length_cum, accesslevel, length_edge, ids_edge_new, ids_point_edgeend_new
557
558                    # add new children if forward star greater 1
559                    if len(ids_edge_next) > 1:
560                        for id_edge in ids_edge_next[1:]:
561                            #ind_edge = self._edge_to_id_edge[edge]
562                            length_edge = edges.lengths[id_edge]
563                            length_cum += length_edge
564                            accesslevel = accesslevels[id_edge]
565                            cost_edge = self._get_cost_birgil(
566                                point, id_edge, phi_point, accesslevel, get_dist_point_to_edge)
567                            ids_edge_new = ids_edge + [id_edge]
568                            ids_point_edgeend_new = ids_point_edgeend + [ids_point[ind_point]]
569                            # print '      new route id_edge',id_edge,ids_edge_new
570                            cost0 = cost+cost_edge
571                            cost_tot = cost0+self.weight_cumlength * length_cum
572                            routelist_new.append([cost_tot, cost0, length_partial, length_cum,
573                                                  accesslevel, length_edge, ids_edge_new, ids_point_edgeend_new])
574
575                else:
576                    # has not reached end of current edge
577                    # save updated data of current route
578                    if len(ids_edge) > 0:
579                        #dist += self.get_dist_point_edge(coords[ind_point], ids_edge[-1])
580                        cost += self._get_cost_birgil(point, id_edge_last, phi_point,
581                                                      accesslevel, get_dist_point_to_edge)
582                    cost_tot = cost+self.weight_cumlength * length_cum
583                    routeinfo[:] = cost_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend
584
585                ind_route += 1
586
587            routelist += routelist_new
588            # print '  routelist',routelist
589            routelist.sort()
590
591            # cut list to self.n_routes_follow
592            if len(routelist) > self.n_routes_follow+1:
593                routelist = routelist[:self.n_routes_follow+1]
594
595            ind_point += 1
596
597        # recover final edge objects of winner route!
598        costs_tot, cost, length_partial, length_cum, accesslevel, length_edge, ids_edge, ids_point_edgeend = routelist[
599            0]
600
601        # print '  ids_edge[-1]',ids_edge[-1],ids_edge[-1] in ids_edge_final
602        # print '  ids_edge_final',ids_edge_final
603        t_match = time.time() - tick
604
605        # --------------------------------------------------------------------
606        # post matching analisis
607        print '\n'+79*'-'
608        print '  matched route:', ids_edge
609        # print '  ids_point_edgeend',ids_point_edgeend
610        route = ids_edge
611        if len(route) == 0:
612            print 'ABOARD: route contains no edges ids_edge=', ids_edge
613            return [], 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, [], False
614
615        length_route = np.sum(costs[ids_edge])
616        length_route_mixed = 0.0
617        length_route_exclusive = 0.0
618        for id_edge, length in zip(ids_edge, costs[ids_edge]):
619            accesslevel = accesslevels[id_edge]
620            if accesslevel == 1:
621                length_route_mixed += length
622            elif accesslevel == 2:
623                length_route_exclusive += length
624
625        if length_gps > 0:
626            lengthindex = length_route/length_gps
627        else:
628            lengthindex = -1.0
629
630        if ids_edge[-1] in ids_edge_final:  # dist!=np.Inf: DID WE ARRIVE?
631            print 'SUCCESS: target', ids_edge[-1], 'reached'
632            is_connected = True
633        else:
634            print 'DISCONNECTED: last matched edge', ids_edge[-1], ' did not reach final edges', ids_edge_final
635            is_connected = False
636
637        # check dist error
638        inds_point = xrange(ind_point_initial, ind_point_final)
639        n_points = len(inds_point)
640
641        if (n_points >= 2) & (len(route) > 0):
642            # print '  initialize distance error measurement',len(route)
643            dist_points_tot = 0.0
644            routestring = get_routelinestring(route, edges)
645            # print '  routestring =',routestring
646
647            # print '  coords',coords[inds_point].tolist()
648            tracepoints = MultiPoint(coords[inds_point].tolist())
649
650            # measure precision
651            #routeset = set(route)
652            #n_points_boundary = 0
653            ind_tracepoint = 0
654            for ind_point in inds_point:  # xrange(n_points):
655                d = routestring.distance(tracepoints[ind_tracepoint])
656                dist_points_tot += d
657                # print '   v distance measurement',d,tracepoints[ind_tracepoint],coords[ind_point]#,n_points#,n_points_eff
658
659                ind_tracepoint += 1
660
661            err_dist = dist_points_tot/float(n_points)
662
663        # print '  check',(lengthindex<self.lengthindex_max),(lengthindex >= self.lengthindex_min),(duration_gps>self.duration_min),(length_route>self.dist_min),(len(route)>0)
664        # print '   twice',(lengthindex<self.lengthindex_max)&(lengthindex >= self.lengthindex_min)&(duration_gps>self.duration_min)&(length_route>self.dist_min)&(len(route)>0)
665
666        return route, length_route, length_route_mixed, length_route_exclusive, duration_gps, lengthindex, err_dist, t_match, ids_point_edgeend, is_connected
667
668    def _get_cost_birgil(self, p, id_edge, phi_point, accesslevel, get_dist_point_to_edge):
669        # print '_get_cost_birgil p, id_edge',p, id_edge
670
671        dist, segment = get_dist_point_to_edge(p, id_edge,
672                                               is_return_segment=True)
673        x1, y1, x2, y2 = segment
674
675        # print '  x1,y1',x1,y1
676        # print '  p',p[0],p[1]
677        # print '  x2,y2',x2,y2
678        # print '  dist',dist
679        #seginds = self._id_edge_to_seginds[ind_edge]
680        #dists2 = get_dist_point_to_segs( p, self._x1[seginds], self._y1[seginds], self._x2[seginds], self._y2[seginds])
681
682        #ind_segind = np.argmin(dists2)
683        #segind = seginds[ind_segind]
684        #x1,y1,x2,y2 = (self._x1[segind], self._y1[segind], self._x2[segind], self._y2[segind])
685        phi_seg = np.arctan2(y2-y1, x2-x1)
686        #dist_ps = max(dist,  self.dist_min)
687        # print '  x1,y1,x2,y2',x1,y1,x2,y2
688        # 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,(phi_point-phi_seg)/np.pi*180,np.clip(phi_point-phi_seg,-np.pi/2,np.pi/2)/np.pi*180)
689        #cost = dist_ps*np.abs(np.sin(np.abs(phi_point-phi_seg)))
690        cost_angle = np.abs(np.sin(np.clip(phi_point-phi_seg, -np.pi/2, np.pi/2)))
691        cost = dist + self.weight_angle * cost_angle - self.weight_access * accesslevel
692
693        # print '     cost_birgil=%.3f, dist=%.3f,  cost_angle=%.3f, accesslevel=%d'%(cost,dist,cost_angle,accesslevel)
694        # print '     cost_birgil=%.3f, Xist=%.3f,  Xost_angle=%.3f, Xccesslevel=%.3f'%(cost,dist,self.weight_angle *cost_angle,self.weight_access*accesslevel)
695        # print '  cost_birgil=',cost
696        return cost
697
698    def get_dist_point_edge(self, p, ind_edge, is_detect_final=False):
699        seginds = self._id_edge_to_seginds[ind_edge]
700        dists2 = get_dist_point_to_segs(p, self._x1[seginds], self._y1[seginds], self._x2[seginds],
701                                        self._y2[seginds], is_ending=True, is_detect_final=is_detect_final)
702        if is_detect_final:
703            is_final = np.isnan(dists2)
704            if np.all(is_final):  # point outside final segction of edge
705                return np.nan
706            else:
707                return np.sqrt(np.min(dists2[~is_final]))
708
709        return np.sqrt(np.min(dists2))
710
711    def get_edge_info(self, id_trip, log=None):
712        """
713        NOT IN USE!!??
714        """
715        # print 'get_edge_info for trace %s with c_length=%.6f'%(id_trip,self.c_length)
716        pointset = self.pointsets.get(id_trip)
717
718        # create a multi point object with points of traces
719        # should no longer be necessary
720        #tracepoints = MultiPoint(pointset.get_coords().tolist())
721
722        # make a array with time stamps of all points
723        pointtimes = pointset.get_times()
724        ids_points = pointset.get_ids()
725        segind_to_id_edge = self._segind_to_id_edge
726        #edgehit_counts = np.zeros(len(self._net.getEdges()),int)
727        # segind_to_id_edge = self._segind_to_id_edge
728        # for p in pointset.get_coords():
729        #    print '  ',p
730        #    hitinds = get_dist_point_to_segs(p,self._x1,self._y1,self._x2,self._y2, is_ending=True)< self.width_buffer**2
731        #    edgehit_counts[segind_to_id_edge[hitinds]]+= 1
732
733        # create a list with lists  for each point
734        # Each list contans the edges, where the point is in the edge buffer
735        edges_containing_point = np.zeros(len(ids_points), object)
736        edgedists_to_point = np.zeros(len(ids_points), object)
737        ind_point = 0
738        for p in pointset.get_coords():
739            dists2 = get_dist_point_to_segs(p, self._x1, self._y1, self._x2, self._y2, is_ending=True)
740            inds_hit = np.nonzero(dists2 < self.width_buffer**2)
741            edges_containing_point[ind_point] = self._edges[segind_to_id_edge[inds_hit]]
742            edgedists_to_point[ind_point] = np.sqrt(dists2[inds_hit])
743
744            # print '  ind_point,p,inds_hit',ind_point,p,len(np.nonzero(inds_hit)[0])#,edges_containing_point[ind_point]
745            ind_point += 1
746
747        # this is a dictionary with edge instance as key and
748        # the weight as value.
749        occurrences = {}
750        for edge in self._edges:
751            occurrences[edge] = 0.0
752
753        id_mode = self.id_mode
754
755        # 2. determine part of weight inversely proportional to the number
756        # of GPS points contained in the buffer
757        ind = 0
758        time_start = +10**10
759        time_end = -10**10
760
761        ind_point_start = -1
762        ind_point_end = -1
763        intervals = {}
764        for pointedges in edges_containing_point:
765            n_edges = len(pointedges)
766            if n_edges > 0:
767                t_point = pointtimes[ind]
768                if t_point < time_start:
769                    time_start = t_point
770                    ind_point_start = ind
771
772                elif t_point > time_end:
773                    time_end = t_point
774                    ind_point_end = ind
775
776                # compute partial weight for containing GPS points in edge buffer
777                wp = 1.0/n_edges
778
779                for edge in pointedges:
780                    # assign weight component to edges
781                    occurrences[edge] += wp
782
783                    # determine intervals per edge
784                    if intervals.has_key(edge):
785                        t_edge_start, t_edge_end = intervals[edge]
786                        if t_point < t_edge_start:
787                            t_edge_start = t_point
788                        elif t_point > t_edge_end:
789                            t_edge_end = t_point
790                        intervals[edge] = (t_edge_start, t_edge_end)
791
792                    else:
793                        intervals[edge] = (t_point, t_point)
794
795            ind += 1
796
797        edges_start = set(edges_containing_point[ind_point_start])
798        edges_end = set(edges_containing_point[ind_point_end])
799
800        # 1. calculate length proportional edge weight
801        c_length = float(self.c_length)
802        c_bike = float(self.c_bike)
803        weights = {}
804
805        for edge in self._edges:
806            len_edge = edge.getLength()
807            p_empty = c_length*len_edge
808
809            l = len_edge
810            allowed = edge.getLanes()[0].getAllowed()
811            if len(allowed) > 0:
812                if id_mode in allowed:
813                    l *= c_bike  # weight for dedicated lanes
814
815            if occurrences[edge] > 0.0:
816                # compute edge length proportional weight
817                weights[edge] = l/occurrences[edge]
818
819            else:
820                weights[edge] = l/p_empty
821
822        # if self._logger:
823        #    self._logger.w( '    New: duration in network %d s from %ds to %ds'%(time_end-time_start,time_start,time_end))
824
825            #log.w( '    Check dimensions len(weights)=%d,len(intervals)=%d'%(len(weights), len(intervals)))
826            # print '  intervals',intervals
827            #log.w( '    Possible start edges')
828            # for edge in edges_start:
829            #    log.w( '      %s'%edge.getID())
830            #log.w(  '    Possible end edges')
831            # for edge in edges_end:
832            #    log.w( '      %s'%edge.getID())
833            #
834            #log.w('get_edge_info done.')
835
836        #self.intervals.set(id_trip, [ time_start,time_end])
837        return weights, edges_start, edges_end, intervals, edges_containing_point, ind_point_start, ind_point_end
838
839
840def get_colvalue(val, default=0.0):
841
842    if (len(val) > 0) & (val != 'NULL'):
843        return float(val)
844    else:
845        return default
846
847
848class FilterMixin(Process):
849    def _init_filter_preview(self):
850        attrsman = self.get_attrsman()
851        trips = self.parent.trips
852        self.filterresults = attrsman.add(cm.FuncConf('filterresults',
853                                                      'filterpreview',  # function attribute of object
854                                                      '%d/%d' % (len(trips.get_ids_selected()), len(trips)),
855                                                      name='Preview selected trips',
856                                                      groupnames=['options', 'results'],
857                                                      ))
858
859    def filterpreview(self):
860        """
861        Previews selected trips after filtering.
862        """
863        trips = self.parent.trips
864        n_trips = len(trips)
865        n_sel_current = len(trips.get_ids_selected())
866        n_sel_eliminate = len(self.filter_ids())
867        n_sel_after = n_sel_current-n_sel_eliminate
868        return '%d/%d (currently %d/%d)' % (n_sel_after, n_trips, n_sel_current, n_trips)
869
870    def _init_traceoptions(self, **kwargs):
871        attrsman = self.get_attrsman()
872        self.dist_trip_min = attrsman.add(cm.AttrConf('dist_trip_min', kwargs.get('dist_trip_min', 100.0),
873                                                      groupnames=['options'],
874                                                      perm='rw',
875                                                      name='Min. trip distance',
876                                                      unit='m',
877                                                      info='Minimum distance of one trip. Shorter trips will not be selected.',
878                                                      ))
879
880        self.dist_trip_max = attrsman.add(cm.AttrConf('dist_trip_max', kwargs.get('dist_trip_max', 25000.0),
881                                                      groupnames=['options'],
882                                                      perm='rw',
883                                                      name='Max. trip distance',
884                                                      unit='m',
885                                                      info='Maximum distance of one trip. Shorter trips will not be selected.',
886                                                      ))
887
888        self.duration_trip_min = attrsman.add(cm.AttrConf('duration_trip_min', kwargs.get('duration_trip_min', 30.0),
889                                                          groupnames=['options'],
890                                                          perm='rw',
891                                                          name='Min. trip duration',
892                                                          unit='s',
893                                                          info='Minimum duration of one trip. Trips with shorter duration will not be selected.',
894                                                          ))
895
896        self.duration_trip_max = attrsman.add(cm.AttrConf('duration_trip_max', kwargs.get('duration_trip_max', 999999.0),
897                                                          groupnames=['options'],
898                                                          perm='rw',
899                                                          name='Max. trip duration',
900                                                          unit='s',
901                                                          info='Maximum duration of one trip. Trips with longer duration will not be selected.',
902                                                          ))
903
904        self.speed_trip_min = attrsman.add(cm.AttrConf('speed_trip_min', kwargs.get('speed_trip_min', 1.0),
905                                                       groupnames=['options'],
906                                                       perm='rw',
907                                                       name='Min. av. trip speed',
908                                                       unit='m/s',
909                                                       info='Minimum average trip speed. Trips with lower average speed will not be selected.',
910                                                       ))
911
912        self.speed_trip_max = attrsman.add(cm.AttrConf('speed_trip_max', kwargs.get('speed_trip_max', 14.0),
913                                                       groupnames=['options'],
914                                                       perm='rw',
915                                                       name='Max. av. trip speed',
916                                                       unit='m/s',
917                                                       info='Maximum average trip speed. Trips with higher average speed will not be selected.',
918                                                       ))
919
920    def _init_filter_time(self, **kwargs):
921        attrsman = self.get_attrsman()
922
923        self.hour_from_morning = attrsman.add(cm.AttrConf('hour_from_morning', kwargs.get('hour_from_morning', 0),
924                                                          groupnames=['options'],
925                                                          perm='rw',
926                                                          name='From morning hour',
927                                                          unit='h',
928                                                          info='Keep only morning trips which start after this hour.',
929                                                          ))
930
931        self.hour_to_morning = attrsman.add(cm.AttrConf('hour_to_morning', kwargs.get('hour_to_morning', 12),
932                                                        groupnames=['options'],
933                                                        perm='rw',
934                                                        name='To morning hour',
935                                                        unit='h',
936                                                        info='Keep only morning trips which start before this hour.',
937                                                        ))
938
939        self.hour_from_evening = attrsman.add(cm.AttrConf('hour_from_evening', kwargs.get('hour_from_evening', 12),
940                                                          groupnames=['options'],
941                                                          perm='rw',
942                                                          name='From evening hour',
943                                                          unit='h',
944                                                          info='Keep only evening trips which start after this hour.',
945                                                          ))
946
947        self.hour_to_evening = attrsman.add(cm.AttrConf('hour_to_evening', kwargs.get('hour_to_evening', 24),
948                                                        groupnames=['options'],
949                                                        perm='rw',
950                                                        name='To evening hour',
951                                                        unit='h',
952                                                        info='Keep only evening trips which start before this hour.',
953                                                        ))
954
955        self.weekdays = attrsman.add(cm.AttrConf('weekdays', kwargs.get('weekdays', WEEKDAYCHOICES['all']),
956                                                 groupnames=['options'],
957                                                 name='Weekdays',
958                                                 choices=WEEKDAYCHOICES,
959                                                 info='Keep only trips at the given weekdays.',
960                                                 ))
961
962    def filter_time(self, timestamps):
963        """
964        timestamps is an array with timestamps.
965        Function returns a binary array, with a True value for each
966        time stamp that does NOT SATISFY the specified time constrants.
967        """
968        # print 'filter_time'
969        # print '  self.hour_from_morning,self.hour_to_morning',self.hour_from_morning,self.hour_to_morning
970        localtime = time.localtime
971        inds_elim = np.zeros(len(timestamps), dtype=np.bool)
972        i = 0
973        for timestamp in timestamps:
974
975            dt = localtime(timestamp)
976            is_keep = dt.tm_wday in self.weekdays
977            # print '   dt.tm_wday,self.weekdays',dt.tm_wday,self.weekdays
978            h = dt.tm_hour
979            is_keep &= (h > self.hour_from_morning) & (h < self.hour_to_morning)\
980                | (h > self.hour_from_evening) & (h < self.hour_to_evening)
981
982            # print '  is_keep,w,h=',is_keep,dt.tm_wday,h
983            inds_elim[i] = not is_keep
984            i += 1
985
986        return inds_elim
987
988    def is_timestamp_ok(self, timestamp):
989        # if is_daytimesaving:
990        dt = time.localtime(timestamp)
991        # else:
992        #    dt = time.gmtime(timestamp)
993        is_ok = dt.tm_wday in self.weekdays
994        h = dt.tm_hour
995        # print 'is_timestamp_ok h,dt.tm_wday',h,dt.tm_wday,is_ok,
996
997        is_ok &= (h >= self.hour_from_morning) & (h < self.hour_to_morning)\
998            | (h >= self.hour_from_evening) & (h < self.hour_to_evening)
999
1000        # print is_ok
1001        return is_ok
1002
1003    def filter_ids(self):
1004        """
1005        Returns an array of ids to be eliminated or deselected.
1006        To be overridden
1007        """
1008        return []
1009
1010
1011class PostMatchfilter(FilterMixin):
1012    def __init__(self,  mapmatching, logger=None, **kwargs):
1013        print 'PostMatchfilter.__init__'
1014        self._init_common('postmatchfilter',
1015                          parent=mapmatching,
1016                          name='Post matchfilter',
1017                          logger=logger,
1018                          info='Removes matched tripe with defined characteristics from current selection.',
1019                          )
1020
1021        attrsman = self.set_attrsman(cm.Attrsman(self))
1022
1023        info_lengthindex = "Length index is length of matched route divided by length of line interpolated GPS points in percent."
1024        self.lengthindex_min = attrsman.add(cm.AttrConf('lengthindex_min', kwargs.get('lengthindex_min', 80.0),
1025                                                        groupnames=['options'],
1026                                                        name='Min. length index',
1027                                                        unit='%',
1028                                                        info='Minimum allowed length index.'+info_lengthindex,
1029                                                        ))
1030        self.lengthindex_max = attrsman.add(cm.AttrConf('lengthindex_max', kwargs.get('lengthindex_max', 110.0),
1031                                                        groupnames=['options'],
1032                                                        name='Min. length index',
1033                                                        unit='%',
1034                                                        info='Maximum allowed length index '+info_lengthindex,
1035                                                        ))
1036
1037        info_error_dist = 'The distance error is the average distance between the GPS points and the matched route.'
1038        self.error_dist_max = attrsman.add(cm.AttrConf('error_dist_max', kwargs.get('error_dist_max', 10000.0),
1039                                                       groupnames=['options'],
1040                                                       name='Max. distance err.',
1041                                                       unit='mm',
1042                                                       info='Maximum allowed distance error. '+info_error_dist,
1043                                                       ))
1044
1045        self._init_filter_time(**kwargs)
1046        # self._init_traceoptions(**kwargs)
1047        self.speed_trip_min = attrsman.add(cm.AttrConf('speed_trip_min', kwargs.get('speed_trip_min', 1.0),
1048                                                       groupnames=['options'],
1049                                                       perm='rw',
1050                                                       name='Min. av. trip speed',
1051                                                       unit='m/s',
1052                                                       info='Minimum average trip speed. Trips with lower average speed will not be selected.',
1053                                                       ))
1054
1055        self.speed_trip_max = attrsman.add(cm.AttrConf('speed_trip_max', kwargs.get('speed_trip_max', 14.0),
1056                                                       groupnames=['options'],
1057                                                       perm='rw',
1058                                                       name='Max. av. trip speed',
1059                                                       unit='m/s',
1060                                                       info='Maximum average trip speed. Trips with higher average speed will not be selected.',
1061                                                       ))
1062
1063        self._init_filter_preview()
1064
1065    def filter_ids(self):
1066        """
1067        Returns an array of ids to be eliminated or deselected.
1068        """
1069        # print 'filter_ids'
1070        trips = self.parent.trips
1071        ids_selected = trips.get_ids_selected()
1072        inds_eliminate = np.logical_or(
1073            trips.lengthindexes[ids_selected] < self.lengthindex_min,
1074            trips.lengthindexes[ids_selected] > self.lengthindex_max,
1075            trips.errors_dist[ids_selected] > self.error_dist_max,
1076            # (trips.lengths_route_matched[ids_selected]<1.0), # too many args??
1077        )
1078
1079        inds_eliminate = np.logical_or(inds_eliminate,
1080                                       trips.lengths_route_matched[ids_selected] < 1.0
1081                                       )
1082
1083        inds_eliminate = np.logical_or(inds_eliminate,
1084                                       trips.speeds_average[ids_selected] < self.speed_trip_min,
1085                                       trips.speeds_average[ids_selected] > self.speed_trip_max
1086                                       )
1087
1088        # dist > self.dist_trip_min)\
1089        #           & (dist < self.dist_trip_max)\
1090        #           & (duration > self.duration_trip_min)\
1091        #           & (speed_av > self.speed_trip_min)\
1092        #           & (speed_av < self.speed_trip_max)
1093
1094        # print '  lengths_route_matched',trips.lengths_route_matched[ids_selected]
1095        # print '  lengthindexes',self.lengthindex_min,self.lengthindex_max,trips.lengthindexes[ids_selected]
1096        # print '  errors_dist',self.error_dist_max,trips.errors_dist[ids_selected]
1097        # print '  lengthindex_min',trips.lengthindexes[ids_selected]<self.lengthindex_min
1098        # print '  lengthindex_max',trips.lengthindexes[ids_selected]>self.lengthindex_max
1099        # print '  lengths_route_matched',trips.errors_dist[ids_selected]>self.error_dist_max
1100        # print '  inds_eliminate',inds_eliminate
1101
1102        inds_eliminate = np.logical_or(inds_eliminate, self.filter_time(trips.timestamps[ids_selected]))
1103        # print '  inds_eliminate',inds_eliminate
1104        return ids_selected[inds_eliminate]
1105
1106    def do(self):
1107
1108        # execute filtering
1109        self.parent.trips.are_selected[self.filter_ids()] = False
1110
1111        return True
1112
1113
1114class TripGeomfilter(FilterMixin):
1115    def __init__(self,  mapmatching, logger=None, **kwargs):
1116        print 'TripGeomfilter.__init__'
1117        self._init_common('tripGeomfilter',
1118                          parent=mapmatching,
1119                          name='Geometry trip filter',
1120                          logger=logger,
1121                          info='Removes trips with defined geometric characteristics from current selection.',
1122                          )
1123
1124        attrsman = self.set_attrsman(cm.Attrsman(self))
1125
1126        self.dist_point_max = attrsman.add(cm.AttrConf('dist_point_max', kwargs.get('dist_point_max', 1000.0),
1127                                                       groupnames=['options'],
1128                                                       perm='rw',
1129                                                       name='Max. point dist.',
1130                                                       unit='m',
1131                                                       info='Keep only traces where the distance between all successive points is below this maximal distance.',
1132                                                       ))
1133
1134        self.duration_point_max = attrsman.add(cm.AttrConf('duration_point_max', kwargs.get('duration_point_max', 300.0),
1135                                                           groupnames=['options'],
1136                                                           perm='rw',
1137                                                           name='Max. point duration.',
1138                                                           unit='s',
1139                                                           info='Keep only traces where the duration between all successive points is below this maximal duration.',
1140                                                           ))
1141
1142        self.const_return_max = attrsman.add(cm.AttrConf('const_return_max', kwargs.get('const_return_max', 0.3),
1143                                                         groupnames=['options'],
1144                                                         perm='rw',
1145                                                         name='Max. return const',
1146                                                         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.',
1147                                                         ))
1148
1149        self.is_eliminate_close_points = attrsman.add(cm.AttrConf('is_eliminate_close_points', kwargs.get('is_eliminate_close_points', False),
1150                                                                  groupnames=['options'],
1151                                                                  perm='rw',
1152                                                                  name='Eliminate close points',
1153                                                                  info='Eliminate points with a distance less then the minimum distance.',
1154                                                                  ))
1155
1156        self.dist_point_min_extr = attrsman.add(cm.AttrConf('dist_point_min_extr', kwargs.get('dist_point_min_extr', 9.0),
1157                                                            groupnames=['options'],
1158                                                            perm='rw',
1159                                                            name='Min. extr. point dist.',
1160                                                            unit='m',
1161                                                            info='Keep only the point where the distance to the point at both extremities is more than a minimum distance.',
1162                                                            ))
1163
1164        self.dist_point_min_inter = attrsman.add(cm.AttrConf('dist_point_min_inter', kwargs.get('dist_point_min_inter', 2.0),
1165                                                             groupnames=['options'],
1166                                                             perm='rw',
1167                                                             name='Min. int. point dist.',
1168                                                             unit='m',
1169                                                             info='Keep only the point where the distance to the previous point is more than a minimum distance.',
1170                                                             ))
1171
1172        self.speed_max = attrsman.add(cm.AttrConf('speed_max', kwargs.get('speed_max', 50.0/3.6),
1173                                                  groupnames=['options'],
1174                                                  perm='rw',
1175                                                  name='Max. speed',
1176                                                  unit='m',
1177                                                  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.',
1178                                                  ))
1179
1180        self.n_overspeed_max = attrsman.add(cm.AttrConf('n_overspeed_max', kwargs.get('n_overspeed_max', 3),
1181                                                        groupnames=['options'],
1182                                                        perm='rw',
1183                                                        name='Max. overspeed points',
1184                                                        info='Trace gets eliminated if this consecutive number of points have speeds over the set maximum speed.',
1185                                                        ))
1186
1187        self._init_filter_preview()
1188
1189    def do(self):
1190
1191        # execute filtering
1192        self.parent.trips.are_selected[self.filter_ids()] = False
1193        return True
1194
1195    def filter_ids(self):
1196        """
1197        Returns an array of ids to be eliminated or deselected.
1198        """
1199        c_cutoff = 1.0 - self.const_return_max
1200        print 'TripGeomfilter.do c_cutoff', c_cutoff
1201        dist_point_max = self.dist_point_max
1202        dist_point_min = self.dist_point_min_extr
1203        dist_point_min_inter = self.dist_point_min_inter
1204        duration_point_max = self.duration_point_max
1205        trips = self.parent.trips
1206        points = self.parent.points
1207
1208        ids_trips = trips.get_ids_selected()
1209
1210        ids_points = trips.ids_points[ids_trips]
1211        # print '  ids_trips',ids_trips
1212        # print '  ids_points',ids_points
1213
1214        intersects_boundaries = self.parent.get_scenario().net.intersects_boundaries
1215
1216        inds_elim = np.zeros(len(ids_trips), dtype=np.bool)
1217        j = 0
1218        ids_point_elim_perm = []
1219        for id_trip, ids_point in zip(ids_trips, ids_points):
1220
1221            coords = points.coords[ids_point]
1222            times = points.timestamps[ids_point]
1223            # print 79*'-'
1224            # print '  check id_trip ',id_trip
1225            # print '  ids_point', ids_point
1226            # print '  times',times
1227            # print '  coords', coords
1228            # print '  duration_point_max',duration_point_max
1229            #dist = pointset.get_distance()
1230
1231            if ids_point is None:
1232                # this happens if the points of a trip in the workout file
1233                # have not been imported for some reason
1234                is_eliminate = True
1235
1236            elif not intersects_boundaries(get_boundary(coords)):
1237                is_eliminate = True
1238
1239            elif np.any(times < 0):
1240                is_eliminate = True
1241            else:
1242                dist_max = 0.0
1243                is_eliminate = False
1244                i = 0
1245                n = len(times)
1246                x, y, z = coords[0]
1247                ids_point_elim = []
1248                coord_last = coords[0]
1249                coord_start = coords[0]
1250                coord_end = coords[-1]
1251
1252                while (not is_eliminate) & (i < (n-1)):
1253                    i += 1
1254
1255                    dist_point = np.sqrt((coords[i, 0]-coords[i-1, 0])**2
1256                                         + (coords[i, 1]-coords[i-1, 1])**2)
1257                    # print '  ',times[i]-times[i-1],dist_point,coords[i,:2],coords[i-1,:2]
1258                    if self.is_eliminate_close_points:
1259
1260                        dist_start = np.sqrt((coords[i, 0]-coord_start[0])**2
1261                                             + (coords[i, 1]-coord_start[1])**2)
1262
1263                        if dist_start < dist_point_min:
1264                            # print '  eliminate',ids_point[i], dist_check
1265                            ids_point_elim.append(ids_point[i])
1266                        else:
1267                            dist_end = np.sqrt((coords[i, 0]-coord_end[0])**2
1268                                               + (coords[i, 1]-coord_end[1])**2)
1269
1270                            if dist_end < dist_point_min:
1271                                # print '  eliminate',ids_point[i], dist_check
1272                                ids_point_elim.append(ids_point[i])
1273                            else:
1274
1275                                dist_check = np.sqrt((coords[i, 0]-coord_last[0])**2
1276                                                     + (coords[i, 1]-coord_last[1])**2)
1277                                if dist_check < dist_point_min_inter:
1278                                    ids_point_elim.append(ids_point[i])
1279                                else:
1280                                    coord_last = coords[i]
1281
1282                    if dist_point > dist_point_max:
1283                        is_eliminate = True
1284
1285                    # print '   times[i]-times[i-1]',times[i]-times[i-1]
1286                    time_delta = times[i]-times[i-1]
1287                    if time_delta > duration_point_max:
1288                        is_eliminate = True
1289
1290                    # test for return distance
1291                    d = np.sqrt((x-coords[i, 0])**2 + (y-coords[i, 1])**2)
1292                    if d > dist_max:
1293                        dist_max = d
1294
1295                    elif d < c_cutoff*dist_max:
1296                        is_eliminate = True
1297
1298                if self.is_eliminate_close_points:
1299                    if len(ids_point_elim) > 0:
1300
1301                        ids_point2 = list(ids_point)
1302                        # print '  before elim ids_point',ids_point2
1303                        # print '  eliminate',ids_point_elim
1304                        for id_point in ids_point_elim:
1305                            ids_point2.remove(id_point)
1306
1307                        trips.ids_points[id_trip] = ids_point2
1308                        ids_point_elim_perm += ids_point_elim
1309                        # print '  after elim ids_point',trips.ids_points[id_trip]
1310
1311            inds_elim[j] = is_eliminate
1312            j += 1
1313
1314        if len(ids_point_elim_perm) > 0:
1315            print '  permanently eliminate %d GPS points' % (len(ids_point_elim_perm))
1316            points.del_rows(ids_point_elim_perm)
1317
1318        return ids_trips[inds_elim]
1319
1320
1321class EccTracesImporter(FilterMixin):
1322    def __init__(self,  mapmatching, logger=None, **kwargs):
1323        print 'EccTracesImporter.__init__', mapmatching.get_ident()
1324        self._init_common('traceimporter',
1325                          parent=mapmatching,
1326                          name='ECC Trace Importer',
1327                          logger=logger,
1328                          info='Import workouts and GPS points of a European cycling challange.',
1329                          )
1330
1331        attrsman = self.set_attrsman(cm.Attrsman(self))
1332
1333        scenario = mapmatching.get_scenario()
1334        rootfilepath = scenario.get_rootfilepath()
1335
1336        # here we ged classes not vehicle type
1337        # specific vehicle type within a class will be generated later
1338        modechoices = scenario.net.modes.names.get_indexmap()
1339
1340        # print '  modechoices',modechoices
1341        self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'],
1342                                                groupnames=['options'],
1343                                                choices=modechoices,
1344                                                name='Mode',
1345                                                info='Transport mode to be matched.',
1346                                                ))
1347
1348        self.workoutsfilepath = attrsman.add(
1349            cm.AttrConf('workoutsfilepath', kwargs.get('workoutsfilepath', rootfilepath+'.workouts.csv'),
1350                        groupnames=['options'],
1351                        perm='rw',
1352                        name='Workout file',
1353                        wildcards='CSV file (*.csv)|*.csv',
1354                        metatype='filepath',
1355                        info="""CSV text file with workout database.""",
1356                        ))
1357
1358        self.pointsfilepath = attrsman.add(cm.AttrConf('pointsfilepath', kwargs.get('pointsfilepath', rootfilepath+'.points.csv'),
1359                                                       groupnames=['options'],
1360                                                       perm='rw',
1361                                                       name='Points file',
1362                                                       wildcards='CSV file (*.csv)|*.csv',
1363                                                       metatype='filepath',
1364                                                       info="CSV text file with GPS point database.",
1365                                                       ))
1366
1367        self.year = attrsman.add(cm.AttrConf('year', kwargs.get('year', 2014),
1368                                             groupnames=['options'],
1369                                             choices={'2013': 2013, '2014': 2014, '2015': 2015, '2016': 2016},
1370                                             perm='rw',
1371                                             name='Year of challange',
1372                                             info='Year of challange is used to identify the correct database format.',
1373                                             ))
1374
1375        self._init_traceoptions(**kwargs)
1376
1377        # self.sep_column_workout = attrsman.add(cm.AttrConf( 'sep_column_workout',kwargs.get('sep_column_workout',','),
1378        #                    groupnames = ['options'],
1379        #                    perm='rw',
1380        #                    name = 'Workoutdata seperator',
1381        #                    info = 'Workout column seperator of CSV file',
1382        #                    ))
1383
1384        # self.sep_column_points = attrsman.add(cm.AttrConf( 'sep_column_points',kwargs.get('sep_column_points',','),
1385        #                    groupnames = ['options'],
1386        #                    perm='rw',
1387        #                    name = 'Point data seperator',
1388        #                    info = 'Pointdata column seperator of CSV file',
1389        #                    ))
1390
1391        self._init_filter_time(**kwargs)
1392
1393        self._proj = None
1394        self._offset = None
1395
1396    def project(self, lons, lats):
1397        if self._proj is None:
1398            self._proj, self._offset = self.parent.get_proj_and_offset()
1399        x, y = self._proj(lons, lats)
1400        return np.transpose(np.concatenate(([x+self._offset[0]], [y+self._offset[1]]), axis=0))
1401
1402    def validate_trip(self, dist, duration, speed_av=-1):
1403        if speed_av < 0:
1404            if duration > 1.0:
1405                speed_av = dist/duration
1406            else:
1407                speed_av = 0.0
1408
1409        return (dist > self.dist_trip_min)\
1410            & (dist < self.dist_trip_max)\
1411            & (duration > self.duration_trip_min)\
1412            & (speed_av > self.speed_trip_min)\
1413            & (speed_av < self.speed_trip_max)
1414
1415    def do(self):
1416        print 'TraceImporter.do'
1417        if self.year == 2014:
1418            self.import_workouts_2014()
1419            self.import_points_2014()
1420
1421        if self.year == 2015:
1422            self.import_workouts_2015()
1423            self.import_points_2015()
1424
1425        if self.year == 2016:
1426            self.import_workouts_2016()
1427            self.import_points_2016()
1428
1429        if self.year == 2013:
1430            self.import_points_2013()
1431
1432        self.parent.points.project()
1433        return True
1434
1435    def import_workouts_2016(self):
1436        # UserID	                    TripID	                    TimeStamp	Start DT	                Distance	 ECC	 AvgSpeed	 TrackType	 Sex	 Year	 Profession	 Frequent User	 ZIP	 Source	  TypeOfBike	 TipeOfTrip	 Max Spd
1437        # 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
1438
1439        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 = range(
1440            17)
1441        n_cols = 17
1442        null = 'NULL'
1443        trips = self.parent.trips
1444        persons = self.parent.persons
1445        #exist_id_person_sumo = persons.ids_sumo.has_index
1446        ids_person_sumo = {}
1447
1448        #ids_trips = []
1449        scenario = self.parent.get_scenario()
1450
1451        get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
1452        id_type = get_vtype_for_mode(self.id_mode)
1453
1454        f = open(self.workoutsfilepath, 'r')
1455        if self._logger:
1456            self._logger.w('import_workouts_2016 %s' % os.path.basename(self.workoutsfilepath))
1457        i_line = 0
1458        sep = ';'
1459        i_line = 0
1460        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
1461        for line in f.readlines()[1:]:
1462            # if True:#i_line>1:
1463            cols = line.split(sep)
1464            # print '    len(cols)',len(cols),n_cols
1465            # print '    cols',cols
1466            if len(cols) >= n_cols:
1467                # row is complete
1468                # print '  id_trip_sumo',cols[j_id_trip]
1469                # if cols[j_dist] != null:
1470                dist = get_colvalue(cols[j_dist])*1000.0
1471                speed_av = get_colvalue(cols[j_speed_av])/3.6
1472                if speed_av > 0:
1473                    duration = dist/speed_av
1474                else:
1475                    duration = 0.0
1476
1477                if self.validate_trip(dist, duration, speed_av):
1478                    timestamp = get_colvalue(cols[j_timestamp])
1479
1480                    if timestamp is not None:
1481                        # print '    valid time stamp',timestamp,self.is_timestamp_ok(timestamp)
1482
1483                        if self.is_timestamp_ok(timestamp):
1484                            id_trip = trips.make(id_sumo=cols[j_id_trip],
1485                                                 id_vtype=id_type,
1486                                                 timestamp=timestamp,
1487                                                 distance_gps=dist,
1488                                                 duration_gps=duration,
1489                                                 speed_average=speed_av,
1490                                                 speed_max=get_colvalue(cols[j_speedmax])/3.6,
1491                                                 purpose=cols[j_purpose].strip(),
1492                                                 device=cols[j_device].strip(),
1493                                                 )
1494
1495                            # ids_trips.append(id_trip)
1496                            #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
1497
1498                            zip = cols[j_zip].strip()
1499                            if zip == 'undefined':
1500                                zip = ''
1501
1502                            year = cols[j_year]
1503                            if year.isdigit():
1504                                year_birth = int(year)
1505                            else:
1506                                year_birth = -1
1507
1508                            id_pers = persons.make(id_sumo=cols[j_id_user].strip(),
1509                                                   id_trip=id_trip,
1510                                                   gender=cols[j_sex].strip(),
1511                                                   year_birth=year_birth,
1512                                                   occupation=cols[j_profession].strip(),
1513                                                   is_frequent_user=cols[j_frequent].strip().lower() == 'yes',
1514                                                   zip=zip,
1515                                                   )
1516                            # print '  id_trip,id_trip_sumo,id_pers',id_trip,cols[j_id_trip], id_pers
1517                            # print
1518            else:
1519                print 'WARNING: inconsistent number of columns (%d) in line %d, file %s' % (
1520                    len(cols), i_line, self.workoutsfilepath)
1521                print '  cols =', cols
1522
1523            i_line += 1
1524
1525    def import_points_2016(self):
1526        print 'import_points_2016'
1527        #    0          1    2            3         4          5         6    7
1528        # TripID, TimeStamp,Latitude, Longitude, Altitude, Distance, Speed, Type
1529        # 574e98c988c5378163a3e11f,1462347278,44.52606,11.27617,78,0.027255420500625783,5,<start|mid|end>,
1530        # 5741cdd388c537f10192ee97, 1463926725,44.50842,11.3604,101.0417,0.01615021486623964,3.483146,mid
1531
1532        ind_id_path = 0
1533        ind_time = 1
1534        ind_lat = 2
1535        ind_lon = 3
1536        ind_alt = 4
1537        ind_dist = 5
1538        ind_speed = 6
1539        ind_type = 7
1540
1541        n_cols = 8
1542        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
1543
1544        trips = self.parent.trips
1545        points = self.parent.points
1546
1547        exist_id_trip_sumo = trips.ids_sumo.has_index
1548        get_id_trip = trips.ids_sumo.get_id_from_index
1549
1550        sep = ','  # self.sep_column_points
1551
1552        f = open(self.pointsfilepath, 'r')
1553        if self._logger:
1554            self._logger.w('import_points_2016 %s' % os.path.basename(self.pointsfilepath))
1555        i_line = 0
1556        id_trip_sumo = None
1557        id_trip = -1
1558        is_valid_trip = False
1559
1560        n_points_imported = 0
1561        for line in f.readlines():
1562
1563            cols = line.split(sep)
1564            # print '    len(cols)',len(cols),n_cols
1565            if len(cols) == n_cols:
1566                id_trip_sumo_current = cols[ind_id_path]
1567                # print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
1568                if id_trip_sumo_current != id_trip_sumo:
1569                    # this point is part of new trip
1570
1571                    if is_valid_trip:
1572                        # print '  store past points for valid trip',id_trip
1573                        ids_point = points.add_rows(
1574                            timestamps=timestamps,
1575                            ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
1576                            longitudes=longitudes,
1577                            latitudes=latitudes,
1578                            altitudes=altitudes,
1579                        )
1580
1581                        trips.set_points(id_trip, ids_point)
1582                        # print '    timestamps',timestamps
1583                        if len(timestamps) > 1:
1584                            trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
1585                            # print '    durations_gps',timestamps[-1]-timestamps[0]
1586                    # check if new trip is valid
1587                    if exist_id_trip_sumo(id_trip_sumo_current):
1588
1589                        is_valid_trip = True  # start recording
1590                        id_trip = get_id_trip(id_trip_sumo_current)
1591                        # print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
1592
1593                        # ids_point_sumo = [] # useless?
1594                        timestamps = []
1595                        ids_trip = []
1596                        longitudes = []
1597                        latitudes = []
1598                        altitudes = []
1599
1600                    else:
1601                        # print '    trip',id_trip_sumo_current,'does not exisit'
1602                        is_valid_trip = False
1603                        id_trip = -1
1604
1605                    id_trip_sumo = id_trip_sumo_current
1606
1607                if is_valid_trip:
1608                    # print '    store point timestamp',cols[ind_time]
1609                    # current point belongs to a valid trip
1610                    # ids_point_sumo.append(cols[ind_id_point])
1611                    timestamps.append(get_colvalue(cols[ind_time]))
1612                    # ids_trip.append(id_trip)
1613                    longitudes.append(get_colvalue(cols[ind_lon]))
1614                    latitudes.append(get_colvalue(cols[ind_lat]))
1615                    altitudes.append(get_colvalue(cols[ind_alt]))
1616
1617            else:
1618                print 'WARNING: inconsistent number of columns (%d) in line %d, file %s' % (
1619                    len(cols), i_line, self.pointsfilepath)
1620                print '  cols =', cols
1621
1622            i_line += 1
1623
1624        # register points of last trip
1625        if is_valid_trip:
1626            # store past points for valid trip
1627            ids_point = points.add_rows(
1628                timestamps=timestamps,
1629                ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
1630                longitudes=longitudes,
1631                latitudes=latitudes,
1632                altitudes=altitudes,
1633            )
1634
1635            trips.set_points(id_trip, ids_point)
1636
1637            if len(timestamps) > 1:
1638                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
1639
1640        # self.odtab.print_rows()
1641
1642        f.close()
1643
1644    def import_workouts_2015(self):
1645
1646        # 2015 csv
1647        # workouts
1648        # UserID	 TripID	 TimeStamp	 Start DT	  Distance	 AvgSpeed	 TrackType	 Sex	 Year	 Profession	 Frequent User	 ZIP
1649        # 54eb068de71f393530a9a74d	54eb0737e71f394c2fa9a74d	1424692504	Mon, 23 Feb 2015 11:55:04 GMT	0	0	urban bicycle	M	1987	Developer	no
1650        # 54eb9374e71f39f02fa9a750	5505cb04e71f39542e25e2d4	1426442994	Sun, 15 Mar 2015 18:09:54 GMT	0	0.7	urban bicycle	M	1974	Worker	yes	40128
1651
1652        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 = range(
1653            12)
1654        n_cols = 12
1655        null = 'NULL'
1656        trips = self.parent.trips
1657        persons = self.parent.persons
1658        #exist_id_person_sumo = persons.ids_sumo.has_index
1659        ids_person_sumo = {}
1660
1661        #ids_trips = []
1662        scenario = self.parent.get_scenario()
1663
1664        get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
1665        id_type = get_vtype_for_mode(self.id_mode)
1666
1667        f = open(self.workoutsfilepath, 'r')
1668        if self._logger:
1669            self._logger.w('import_workouts_2015 %s' % os.path.basename(self.workoutsfilepath))
1670        i_line = 0
1671        sep = ';'
1672        i_line = 1
1673        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
1674        for line in f.readlines()[1:]:
1675            # if True:#i_line>1:
1676            cols = line.split(sep)
1677            # print '\n    len(cols)',len(cols),n_cols
1678            # print '    cols',cols
1679            if len(cols) >= n_cols:
1680                # row is complete
1681                # print '  id_trip_sumo "%s"'%cols[j_id_trip]
1682                # if cols[j_dist] != null:
1683                dist = get_colvalue(cols[j_dist])*1000.0
1684                speed_av = get_colvalue(cols[j_speed_av])/3.6
1685                #duration = get_colvalue(cols[j_duration])
1686                # if duration>0:
1687                #    speed_av = dist/duration
1688                # else:
1689                #    speed_av = 0.0
1690                # print '  dist',dist,'speed_av',speed_av
1691                if speed_av > 0:
1692                    duration = dist/speed_av
1693                else:
1694                    duration = 0.0
1695
1696                if self.validate_trip(dist, duration, speed_av):
1697                    # print  '    parametric conditions verified'
1698                    timestamp = get_colvalue(cols[j_timestamp])
1699                    if timestamp is not None:
1700                        # print '    valid time stamp',timestamp#,self.is_timestamp_ok(timestamp)
1701                        if self.is_timestamp_ok(timestamp):
1702                            id_trip = trips.make(id_sumo=cols[j_id_trip],
1703                                                 id_vtype=id_type,
1704                                                 timestamp=timestamp,
1705                                                 distance_gps=dist,
1706                                                 duration_gps=duration,
1707                                                 speed_average=speed_av,
1708                                                 )
1709                            # ids_trips.append(id_trip)
1710                            #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
1711
1712                            zip = cols[j_zip].strip()
1713                            if zip == 'undefined':
1714                                zip = ''
1715
1716                            year = cols[j_year]
1717                            if year.isdigit():
1718                                year_birth = int(year)
1719                            else:
1720                                year_birth = -1
1721
1722                            persons.make(id_sumo=cols[j_id_user].strip(),
1723                                         id_trip=id_trip,
1724                                         gender=cols[j_sex].strip(),
1725                                         year_birth=year_birth,
1726                                         occupation=cols[j_profession].strip(),
1727                                         is_frequent_user=cols[j_frequent].strip() == 'yes',
1728                                         zip=zip,
1729                                         )
1730            else:
1731                print 'WARNING: inconsistent number of columns (%d) in line %d, file %s' % (
1732                    len(cols), i_line, self.workoutsfilepath)
1733                # print '  cols =',cols
1734
1735            i_line += 1
1736
1737    def import_points_2015(self):
1738        print 'import_points_2015'
1739        #    0          1    2   3         4          5         6         7       8
1740        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
1741        # 54eb0737e71f394c2fa9a74d,1424692509,"Mon, 23 Feb 2015 11:55:09 GMT",44.499096,11.361185,49.419395,0,0.000815,start
1742
1743        ind_id_path = 0
1744        ind_time = 1
1745        ind_day = 2
1746        ind_date = 3
1747        ind_lat = 4
1748        ind_lon = 5
1749        ind_alt = 6
1750        ind_dist = 7
1751        ind_speed = 8
1752        ind_type = 9
1753
1754        n_cols = 10
1755        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
1756
1757        trips = self.parent.trips
1758        points = self.parent.points
1759
1760        exist_id_trip_sumo = trips.ids_sumo.has_index
1761        get_id_trip = trips.ids_sumo.get_id_from_index
1762
1763        sep = ','  # self.sep_column_points
1764
1765        f = open(self.pointsfilepath, 'r')
1766        if self._logger:
1767            self._logger.w('import_points_2015 %s' % os.path.basename(self.pointsfilepath))
1768        i_line = 0
1769        id_trip_sumo = None
1770        id_trip = -1
1771        is_valid_trip = False
1772
1773        n_points_imported = 0
1774        for line in f.readlines():
1775
1776            cols = line.split(sep)
1777            # print '    len(cols)',len(cols),n_cols
1778            if len(cols) == n_cols:
1779                id_trip_sumo_current = cols[ind_id_path]
1780                # print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
1781                if id_trip_sumo_current != id_trip_sumo:
1782                    # this point is part of new trip
1783
1784                    if is_valid_trip:
1785                        # print '  store past points for valid trip',id_trip
1786                        ids_point = points.add_rows(
1787                            timestamps=timestamps,
1788                            ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
1789                            longitudes=longitudes,
1790                            latitudes=latitudes,
1791                            altitudes=altitudes,
1792                        )
1793
1794                        trips.set_points(id_trip, ids_point)
1795                        # print '    timestamps',timestamps
1796                        if len(timestamps) > 1:
1797                            trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
1798                            # print '    durations_gps',timestamps[-1]-timestamps[0]
1799                    # check if new trip is valid
1800                    if exist_id_trip_sumo(id_trip_sumo_current):
1801
1802                        is_valid_trip = True  # start recording
1803                        id_trip = get_id_trip(id_trip_sumo_current)
1804                        # print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
1805
1806                        # ids_point_sumo = [] # useless?
1807                        timestamps = []
1808                        ids_trip = []
1809                        longitudes = []
1810                        latitudes = []
1811                        altitudes = []
1812
1813                    else:
1814                        # print '    trip',id_trip_sumo_current,'does not exisit'
1815                        is_valid_trip = False
1816
1817                    id_trip_sumo = id_trip_sumo_current
1818
1819                if is_valid_trip:
1820                    # print '    store point timestamp',cols[ind_time]
1821                    # current point belongs to a valid trip
1822                    # ids_point_sumo.append(cols[ind_id_point])
1823                    timestamps.append(get_colvalue(cols[ind_time]))
1824                    # ids_trip.append(id_trip)
1825                    longitudes.append(get_colvalue(cols[ind_lon]))
1826                    latitudes.append(get_colvalue(cols[ind_lat]))
1827                    altitudes.append(get_colvalue(cols[ind_alt]))
1828
1829            else:
1830                print 'WARNING: inconsistent number of columns (%d) in line %d, file %s' % (
1831                    len(cols), i_line, self.pointsfilepath)
1832                # print '  cols =',cols
1833
1834            i_line += 1
1835
1836        # register points of last trip
1837        if is_valid_trip:
1838            # store past points for valid trip
1839            ids_point = points.add_rows(
1840                timestamps=timestamps,
1841                ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
1842                longitudes=longitudes,
1843                latitudes=latitudes,
1844                altitudes=altitudes,
1845            )
1846
1847            trips.set_points(id_trip, ids_point)
1848
1849            if len(timestamps) > 1:
1850                trips.durations_gps[id_trip] = timestamps[-1]-timestamps[0]
1851
1852        f.close()
1853
1854    def import_workouts_2014(self):
1855        print 'import_workouts_2014'
1856        # 2014 ecomondo workouts
1857        # id	        pointDBNode	pointPathId	startTime	        distance	duration	sport	calories	maxSpeed	altitudeMin	altitudeMax	metersAscent	metersDescent
1858        # 329308466	7	        37073516	2014-05-01 19:00:00	    26	    15600	    1	    1182.64	    NULL	    NULL	    NULL	    NULL	        NULL
1859        # 0         1           2            3                       4          5       6          7        8           9          10          11                   12
1860
1861        j_id, j_node, j_id_trip, j_time, j_dist, j_duration = range(6)
1862        j_v_max = 8
1863        n_cols = 13
1864        null = 'NULL'
1865        trips = self.parent.trips
1866        # persons = self.parent.persons # no person data in 2014 :(
1867        ids_person_sumo = {}
1868
1869        ids_trips = []
1870        scenario = self.parent.get_scenario()
1871
1872        #get_vtype_for_mode = scenario.demand.vtypes.get_vtype_for_mode
1873        id_vtype = scenario.demand.vtypes.get_vtype_for_mode(self.id_mode)
1874
1875        f = open(self.workoutsfilepath, 'r')
1876        #if self._logger: self._logger.w('import_workouts_2014 %s'%os.path.basename(self.workoutsfilepath))
1877        i_line = 0
1878        sep = ','  # self.sep_column_workout
1879
1880        #self.get_logger().w(100.0*self.simtime/self.duration, key ='progress')
1881        for line in f.readlines()[1:]:
1882            # if True:#i_line>1:
1883            cols = line.split(sep)
1884            # print '    len(cols)',len(cols),n_cols
1885            if len(cols) == n_cols:
1886                # row is complete
1887
1888                # if cols[j_dist] != null:
1889                dist = get_colvalue(cols[j_dist])*1000.0
1890                duration = get_colvalue(cols[j_duration])
1891                if duration > 0:
1892                    speed_av = dist/duration
1893                else:
1894                    speed_av = 0.0
1895
1896                if self.validate_trip(dist, duration, speed_av):
1897                    # print  'parametric conditions verified'
1898                    timestamp = calc_seconds(cols[j_time])
1899                    if timestamp is not None:
1900                        # print '  valid time stamp',timestamp
1901                        if self.is_timestamp_ok(timestamp):
1902                            id_trip = trips.make(id_sumo=cols[j_id_trip],
1903                                                 id_vtype=id_vtype,
1904                                                 timestamp=timestamp,
1905                                                 distance_gps=dist,
1906                                                 duration_gps=duration,
1907                                                 speed_average=speed_av,
1908                                                 speed_max=get_colvalue(cols[j_v_max])/3.6,
1909                                                 )
1910                            ids_trips.append(id_trip)
1911
1912    def import_points_2014(self):
1913        print 'import_points_2014'
1914        # csv2014
1915        # pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
1916        # 4,61565791,23648171762,2013-05-01 06:33:58,44.501085,11.372906,NULL,0,NULL,2,NULL
1917
1918        # pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
1919        # 7,37073516,15138524460,NULL,44.51579,11.36257,NULL,NULL,NULL,NULL,NULL
1920
1921        # pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
1922        # 7,37073516,15138524460,NULL,44.51579,11.36257,NULL,NULL,NULL,NULL,NULL
1923
1924        # Endomondo export format
1925        # pointDBNode,pointPathId,id,timestamp,latitude,longitude,altitude,distance,heartRate,instruction,speed
1926        #    0          1         2   3         4          5         6       7       8          9          10
1927        ind_id_path = 1
1928        ind_id_point = 2
1929        ind_time = 3
1930        ind_lat = 4
1931        ind_lon = 5
1932        ind_alt = 6
1933        ind_dist = 7
1934        ind_speed = 10
1935
1936        n_cols = 11
1937        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
1938
1939        trips = self.parent.trips
1940        points = self.parent.points
1941
1942        exist_id_trip_sumo = trips.ids_sumo.has_index
1943        get_id_trip = trips.ids_sumo.get_id_from_index
1944
1945        sep = ','  # self.sep_column_points
1946
1947        f = open(self.pointsfilepath, 'r')
1948        if self._logger:
1949            self._logger.w('import_points_2014 %s' % os.path.basename(self.pointsfilepath))
1950        i_line = 0
1951        id_trip_sumo = None
1952        id_trip = -1
1953        is_valid_trip = False
1954
1955        n_points_imported = 0
1956        for line in f.readlines():
1957
1958            cols = line.split(sep)
1959            # print '    len(cols)',len(cols),n_cols
1960            if len(cols) == n_cols:
1961                id_trip_sumo_current = cols[ind_id_path]
1962                # print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
1963                if id_trip_sumo_current != id_trip_sumo:
1964                    # this point is part of new trip
1965
1966                    if is_valid_trip:
1967                        # print '  store past points for valid trip',id_trip
1968                        ids_point = points.add_rows(
1969                            timestamps=timestamps,
1970                            ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
1971                            longitudes=longitudes,
1972                            latitudes=latitudes,
1973                            altitudes=altitudes,
1974                        )
1975
1976                        trips.set_points(id_trip, ids_point)
1977
1978                    # check if new trip is valid
1979                    if exist_id_trip_sumo(id_trip_sumo_current):
1980
1981                        is_valid_trip = True  # start recording
1982                        id_trip = get_id_trip(id_trip_sumo_current)
1983                        # print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
1984
1985                        # ids_point_sumo = [] # useless?
1986                        timestamps = []
1987                        ids_trip = []
1988                        longitudes = []
1989                        latitudes = []
1990                        altitudes = []
1991
1992                    else:
1993                        # print '    trip',id_trip_sumo_current,'does not exisit'
1994                        is_valid_trip = False
1995
1996                    id_trip_sumo = id_trip_sumo_current
1997
1998                if is_valid_trip:
1999                    # print '    store point timestamp',cols[ind_time]
2000                    # current point belongs to a valid trip
2001                    # ids_point_sumo.append(cols[ind_id_point])
2002                    timestamps.append(calc_seconds(cols[ind_time]))
2003                    # ids_trip.append(id_trip)
2004                    longitudes.append(get_colvalue(cols[ind_lon]))
2005                    latitudes.append(get_colvalue(cols[ind_lat]))
2006                    altitudes.append(get_colvalue(cols[ind_alt]))
2007
2008            else:
2009                print 'WARNING: inconsistent columns in line %d, file %s' % (
2010                    i_line, os.path.basename(self.pointsfilepath))
2011
2012            i_line += 1
2013
2014        # register points of last trip
2015        if is_valid_trip:
2016            # store past points for valid trip
2017            ids_point = points.add_rows(
2018                timestamps=timestamps,
2019                ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
2020                longitudes=longitudes,
2021                latitudes=latitudes,
2022                altitudes=altitudes,
2023            )
2024
2025            trips.set_points(id_trip, ids_point)
2026
2027        # self.odtab.print_rows()
2028
2029        f.close()
2030
2031    def import_points_2013(self):
2032        print 'import_points_2013'
2033        #pointDBNode,   pointPathId,    id,     timestamp,          latitude,   longitude,  altitude,distance,  heartRate,instruction,speed
2034        # 4,             61565791,   23648171762,2013-05-01 06:33:58,44.501085,  11.372906,  NULL,       0,      NULL,       2,          NULL
2035        # 0                  1         2          3                      4          5            6       7       8           9           10
2036        ind_id_path = 1
2037        ind_id_point = 2
2038        ind_time = 3
2039        ind_lat = 4
2040        ind_lon = 5
2041        ind_alt = 6
2042        ind_dist = 7
2043        ind_speed = 10
2044
2045        n_cols = 11
2046        #TripID, TimeStamp,Date, Latitude, Longitude, Altitude, Distance, Speed, Type
2047
2048        trips = self.parent.trips
2049        points = self.parent.points
2050
2051        exist_id_trip_sumo = trips.ids_sumo.has_index
2052        get_id_trip = trips.ids_sumo.get_id_from_index
2053        id_vtype = self.parent.get_scenario().demand.vtypes.get_vtype_for_mode(self.id_mode)
2054        sep = ','  # self.sep_column_points
2055
2056        f = open(self.pointsfilepath, 'r')
2057        if self._logger:
2058            self._logger.w('import_points_2013 %s' % os.path.basename(self.pointsfilepath))
2059        i_line = 0
2060        id_trip_sumo = None
2061        id_trip = -1
2062        is_valid_trip = False
2063        timestamp_last = -1
2064        n_points_imported = 0
2065        timestamps = []
2066
2067        for line in f.readlines():
2068
2069            cols = line.split(sep)
2070            # print '    len(cols)',len(cols),n_cols
2071            if (len(cols) == n_cols) & (i_line > 0):
2072                id_trip_sumo_current = cols[ind_id_path]
2073                if timestamp_last < 0:
2074                    timestamp_last = calc_seconds(cols[ind_time])
2075
2076                # print '    id_trip_sumo_current,id_trip_sumo',id_trip_sumo_current,id_trip_sumo,is_valid_trip
2077                if id_trip_sumo_current != id_trip_sumo:
2078                    # this point is part of new trip
2079
2080                    # validate trip data
2081                    if len(timestamps) == 0:
2082                        is_valid_trip = False
2083
2084                    else:
2085                        coords = self.project(latitudes, longitudes)
2086                        distance = np.sum(np.sqrt(np.sum((coords[1:, :]-coords[:-1, :])**2, 1)))
2087                        duration = timestamps[-1]-timestamps[0]
2088                        if duration > 0:
2089                            speed_av = distance/duration
2090                        else:
2091                            speed_av = 0.0
2092                        is_valid_trip = self.validate_trip(distance, duration, speed_av)
2093
2094                    if is_valid_trip:
2095                        # print '  store past points for valid trip',id_trip
2096                        id_trip = trips.make(id_sumo=id_trip_sumo_current,
2097                                             id_vtype=id_vtype,
2098                                             timestamp=timestamp_last,
2099                                             distance_gps=distance,
2100                                             duration_gps=duration,
2101                                             speed_average=speed_av,
2102                                             )
2103
2104                        ids_point = points.add_rows(
2105                            timestamps=timestamps,
2106                            ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
2107                            longitudes=longitudes,
2108                            latitudes=latitudes,
2109                            altitudes=altitudes,
2110                        )
2111
2112                        trips.set_points(id_trip, ids_point)
2113
2114                    # check if new trip is valid
2115                    # if exist_id_trip_sumo(id_trip_sumo_current):
2116
2117                    is_valid_trip = True  # start recording
2118                    #id_trip = get_id_trip(id_trip_sumo_current)
2119                    # print '    found trip',id_trip,id_trip_sumo_current,' exisits-> record'
2120
2121                    # ids_point_sumo = [] # useless?
2122                    timestamp_last = calc_seconds(cols[ind_time])
2123                    timestamps = []
2124                    ids_trip = []
2125                    longitudes = []
2126                    latitudes = []
2127                    altitudes = []
2128
2129                    # else:
2130                    #    #print '    trip',id_trip_sumo_current,'does not exisit'
2131                    #    is_valid_trip  = False
2132
2133                    id_trip_sumo = id_trip_sumo_current
2134
2135                if is_valid_trip:
2136                    # print '    store point timestamp',cols[ind_time]
2137                    # current point belongs to a valid trip
2138                    # ids_point_sumo.append(cols[ind_id_point])
2139                    timestamps.append(calc_seconds(cols[ind_time]))
2140                    # ids_trip.append(id_trip)
2141                    longitudes.append(get_colvalue(cols[ind_lon]))
2142                    latitudes.append(get_colvalue(cols[ind_lat]))
2143                    altitudes.append(get_colvalue(cols[ind_alt]))
2144
2145            else:
2146                print 'WARNING: inconsistent columns in line %d, file %s' % (
2147                    i_line, os.path.basename(self.pointsfilepath))
2148
2149            i_line += 1
2150
2151        # register points of last trip
2152        if is_valid_trip:
2153            # store past points for valid trip
2154            ids_point = points.add_rows(
2155                timestamps=timestamps,
2156                ids_trip=id_trip*np.ones(len(timestamps), dtype=np.int32),
2157                longitudes=longitudes,
2158                latitudes=latitudes,
2159                altitudes=altitudes,
2160            )
2161
2162            trips.set_points(id_trip, ids_point)
2163
2164        # self.odtab.print_rows()
2165
2166        f.close()
2167
2168
2169class GpxImporter(FilterMixin):
2170    def __init__(self,  mapmatching, logger=None, **kwargs):
2171        print 'GpxImporter.__init__', mapmatching.get_ident()
2172        self._init_common('gpximporter',
2173                          parent=mapmatching,
2174                          name='GPX Importer',
2175                          logger=logger,
2176                          info='Import GPS traces from GPX files.',
2177
2178                          )
2179
2180        attrsman = self.set_attrsman(cm.Attrsman(self))
2181
2182        scenario = mapmatching.get_scenario()
2183        rootfilepath = scenario.get_rootfilepath()
2184
2185        # here we ged classes not vehicle type
2186        # specific vehicle type within a class will be generated later
2187        modechoices = scenario.net.modes.names.get_indexmap()
2188
2189        # print '  modechoices',modechoices
2190        self.id_mode = attrsman.add(am.AttrConf('id_mode',  modechoices['bicycle'],
2191                                                groupnames=['options'],
2192                                                choices=modechoices,
2193                                                name='Mode',
2194                                                info='Transport mode to be matched.',
2195                                                ))
2196
2197        self.filepaths = attrsman.add(
2198            cm.AttrConf('filepaths', kwargs.get('filepath', rootfilepath+'.gpx'),
2199                        groupnames=['options'],
2200                        perm='rw',
2201                        name='filenames',
2202                        wildcards='GPX file (*.gpx)|*.gpx|XML file (*.xml)|*.xml',
2203                        metatype='filepaths',
2204                        info="""Paths and file name of GPX file with GPS traces in XML format.""",
2205                        ))
2206        self._init_traceoptions(**kwargs)
2207
2208    def do(self):
2209        """
2210        Reads endomondo gpx xml file and stores point data in traces table.
2211        If there is no traces table, one will be initialized and returned
2212        """
2213        print 'GpxImporter.do', self.filepaths
2214        mapmatching = self.parent
2215        #scenario = mapmatching.get_scenario()
2216        logger = self.get_logger()
2217
2218        get_vtype_for_mode = mapmatching.get_scenario().demand.vtypes.get_vtype_for_mode
2219
2220        parser = GpxParser(mapmatching.trips, mapmatching.points,
2221                           logger=logger,
2222                           id_vtype=get_vtype_for_mode(self.id_mode),
2223                           dist_trip_min=self.dist_trip_min,
2224                           dist_trip_max=self.dist_trip_max,
2225                           duration_trip_min=self.speed_trip_min,
2226                           duration_trip_max=self.duration_trip_max,
2227                           speed_trip_min=self.speed_trip_min,
2228                           speed_trip_max=self.speed_trip_max,
2229                           )
2230        for filepath in self.filepaths.split(','):
2231            print '  parse gpx file',    filepath
2232            parse(filepath.strip(), parser)
2233
2234        ids_trips = parser.get_ids_trip()
2235        if logger:
2236            logger.w('imported %d traces, project coordinates...' % len(ids_trips))
2237
2238        # recalculate projection for network in scenario
2239        # for id_trace in ids_traces:
2240        #    traces.pointsets.get(id_trace).project( traces._proj, traces.offset)
2241
2242        mapmatching.points.project()
2243
2244        if logger:
2245            logger.w('imported %d trips done.' % len(ids_trips))
2246        return True
2247
2248
2249class GpxParser(handler.ContentHandler):
2250    """Reads endomondo gpx xml file and parses lat,lon, ele and time.
2251    """
2252
2253    def __init__(self, trips, points, logger=None,
2254                 id_vtype=0,
2255                 dist_trip_min=10.0,
2256                 dist_trip_max=50000.0,
2257                 duration_trip_min=0.5,
2258                 duration_trip_max=999999.0,
2259                 speed_trip_min=0.1,
2260                 speed_trip_max=100.0,
2261                 ):
2262
2263        self._logger = logger
2264        self._points = points
2265        self._trips = trips
2266        self._id_vtype = id_vtype
2267        self._dist_trip_min = dist_trip_min
2268        self._dist_trip_max = dist_trip_max
2269        self._duration_trip_min = speed_trip_min
2270        self._duration_trip_max = duration_trip_max
2271        self._speed_trip_min = speed_trip_min
2272        self._speed_trip_max = speed_trip_max
2273
2274        self.reset_trip()
2275
2276    def reset_trip(self):
2277        self._lons = []
2278        self._lats = []
2279        self._eles = []
2280        self._times = []
2281        self._is_record_segment = False
2282        self._is_record_point = False
2283        self._is_time = False
2284        self._is_ele = False
2285
2286        self._ids_trip = []
2287        self._ids_point = []
2288
2289    def get_ids_trip(self):
2290        return self._ids_trip
2291
2292    def get_ids_point(self):
2293        return self._ids_point
2294
2295    def startElement(self, name, attrs):
2296        # if self._pointset  is None:
2297
2298        if name == 'trkpt':
2299            self._is_record_point = True
2300            self._lons.append(float(attrs['lon']))
2301            self._lats.append(float(attrs['lat']))
2302
2303        if name == 'time':
2304            self._is_time = True
2305
2306        if name == 'ele':
2307            self._is_ele = True
2308
2309        if name == 'trkseg':
2310            self._is_record_segment = True
2311
2312        if name == 'trk':
2313            self._is_record_trip = True
2314
2315    def characters(self, content):
2316
2317        if self._is_time:
2318            # print '  got time',content[:-1]
2319
2320            if self._is_record_point:
2321                # 2013-05-30T16:42:33Z 2014-01-26T12:32:21Z
2322                self._times.append(calc_seconds(content[:-1],
2323                                                sep_date_clock='T',
2324                                                sep_date='-',
2325                                                sep_clock=':',
2326                                                is_float=False))
2327
2328        if self._is_ele:
2329            # print 'characters content',content
2330            self._eles.append(float(content))
2331
2332    def endElement(self, name):
2333        if name == 'ele':
2334            self._is_ele = False
2335
2336        if name == 'time':
2337            self._is_time = False
2338
2339        if name == 'trkpt':
2340            # print 'endElement: point',self._lon,self._lat,self._ele,self._time
2341            #self._is_ele = False
2342            #self._is_time = False
2343            self._is_record_point = False
2344
2345        if name == 'trk':
2346            # here we could stop recording a segment
2347            # but currently we join all segments together
2348            pass
2349
2350        if name == 'trk':
2351            # print 'endElement',len(self._lons)
2352
2353            # print '  self._lons',self._lons
2354            # print '  self._lats',self._lats
2355            # print '  self._times',self._times
2356
2357            # trip recording ends
2358            n_points = len(self._lons)
2359
2360            if (n_points > 0):
2361                timestamp = self._times[0]
2362                duration = self._times[-1]-timestamp
2363                # print '    timestamp',timestamp
2364                # print '    duration',duration,self._duration_trip_min,self._duration_trip_max
2365                # TODO: make this filter a functin of filtermixin
2366                if (duration > self._duration_trip_min)\
2367                        & (duration < self._duration_trip_max):
2368
2369                    id_trip = self._trips.make(id_vtype=self._id_vtype,
2370                                               timestamp=timestamp,
2371                                               #distance_gps = dist,
2372                                               duration_gps=duration
2373                                               #speed_average = speed_av,
2374                                               )
2375                    self._ids_trip.append(id_trip)
2376
2377                    if len(self._eles) == n_points:
2378                        altitudes = self._eles
2379                    else:
2380                        altitudes = np.zeros(n_points, dtype=np.float32)
2381
2382                    ids_point = self._points.add_rows(
2383                        timestamps=self._times,
2384                        ids_trip=id_trip*np.ones(n_points, dtype=np.int32),
2385                        longitudes=self._lons,
2386                        latitudes=self._lats,
2387                        altitudes=altitudes,
2388                    )
2389
2390                    self._trips.set_points(id_trip, ids_point)
2391                    self._trips.ids_sumo[id_trip] = str(id_trip)
2392
2393            self.reset_trip()
2394
2395
2396class GpsTrips(Trips):
2397    def __init__(self, ident, mapmatching,  **kwargs):
2398        # print 'Trips.__init__'
2399        self._init_objman(ident=ident,
2400                          parent=mapmatching,
2401                          name='Trips',
2402                          info='Table with GPS trips, matched routes and alternative routes.',
2403                          xmltag=('trips', 'trip', 'ids_sumo'),
2404                          version=0.0,
2405                          **kwargs)
2406
2407        self._init_attributes()
2408        self._init_constants()
2409
2410    def _init_attributes(self):
2411
2412        self.add_col(SumoIdsConf('GPS Trip', xmltag='id', info='GPS trip data.'))
2413
2414        self.add_col(am.ArrayConf('are_selected', default=True,
2415                                  dtype=np.bool,
2416                                  groupnames=['parameters', ],
2417                                  name='selected',
2418                                  symbol='Sel.',
2419                                  info='Selected for being processed (example mapmatching, export, etc).',
2420                                  ))
2421
2422        self.add_col(am.ArrayConf('timestamps', default=0,
2423                                  dtype=np.int,
2424                                  perm='r',
2425                                  groupnames=['parameters', 'gps'],
2426                                  name='timestamp',
2427                                  unit='s',
2428                                  metatype='datetime',
2429                                  info='Timestamp when trip started in seconds after 01 January 1970.',
2430                                  ))
2431
2432        self.timestamps.metatype = 'datetime'
2433        self.timestamps.set_perm('r')
2434
2435        self.add_col(am.ArrayConf('durations_gps', default=0.0,
2436                                  dtype=np.float32,
2437                                  groupnames=['parameters', 'gps'],
2438                                  name='GPS duration',
2439                                  unit='s',
2440                                  info='Time duration measure with GPS points.',
2441                                  ))
2442
2443        self.add_col(am.ArrayConf('distances_gps', default=0.0,
2444                                  dtype=np.float32,
2445                                  groupnames=['parameters', 'gps'],
2446                                  name='GPS distance',
2447                                  unit='m',
2448                                  info='Distance measure with GPS points.',
2449                                  ))
2450
2451        self.add_col(am.ArrayConf('speeds_average', default=0.0,
2452                                  dtype=np.float32,
2453                                  groupnames=['parameters', 'gps'],
2454                                  name='Av. speed',
2455                                  unit='m/s',
2456                                  info='Average speed based on GPS info.',
2457                                  ))
2458        self.add_col(am.ArrayConf('speeds_max', default=0.0,
2459                                  dtype=np.float32,
2460                                  groupnames=['parameters', 'gps'],
2461                                  name='Max. speed',
2462                                  unit='m/s',
2463                                  info='Maximum speed based on GPS info.',
2464                                  ))
2465
2466        # Trips._init_attributes(self)
2467        self.add_col(am.IdsArrayConf('ids_vtype', self.get_obj_vtypes(),
2468                                     groupnames=['state'],
2469                                     name='Type',
2470                                     info='Vehicle type.',
2471                                     xmltag='type',
2472                                     ))
2473
2474        self.add_col(am.ArrayConf('ids_purpose', default=TRIPPUROPSES['unknown'],
2475                                  dtype=np.int32,
2476                                  groupnames=['parameters', 'gps'],
2477                                  choices=TRIPPUROPSES,
2478                                  name='Purpose',
2479                                  info='Trip purpose ID',
2480                                  ))
2481
2482        self.add_col(am.ArrayConf('ids_device', default=DEVICES['unknown'],
2483                                  dtype=np.int32,
2484                                  groupnames=['parameters', 'gps'],
2485                                  choices=DEVICES,
2486                                  name='Devices',
2487                                  info='Device ID',
2488                                  ))
2489        self.add(cm.ObjConf(Routes('routes', self, self.parent.get_scenario().net)))
2490
2491        self.add_col(am.IdsArrayConf('ids_route_matched', self.get_routes(),
2492                                     groupnames=['results'],
2493                                     name='ID matched route',
2494                                     info='Route ID of mached route.',
2495                                     ))
2496
2497        self.add_col(am.IdsArrayConf('ids_route_shortest', self.get_routes(),
2498                                     groupnames=['results'],
2499                                     name='ID shortest route',
2500                                     info='Route ID of shortest route.',
2501                                     ))
2502
2503        self.add_col(am.IdsArrayConf('ids_route_fastest', self.get_routes(),
2504                                     groupnames=['results'],
2505                                     name='ID fastest route',
2506                                     info='Route ID of fastest route.',
2507                                     ))
2508
2509        self.add_col(am.ArrayConf('lengths_gpsroute_matched', default=-1.0,
2510                                  dtype=np.float32,
2511                                  groupnames=['results'],
2512                                  name='Matched length',
2513                                  symbol='L match GPS',
2514                                  unit='m',
2515                                  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.',
2516                                  ))
2517
2518        self.add_col(am.ArrayConf('lengths_route_matched', default=-1.0,
2519                                  dtype=np.float32,
2520                                  groupnames=['results'],
2521                                  name='Matched length',
2522                                  symbol='L match',
2523                                  unit='m',
2524                                  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.',
2525                                  ))
2526
2527        self.add_col(am.ArrayConf('durations_route_matched', default=-1.0,
2528                                  dtype=np.float32,
2529                                  groupnames=['results'],
2530                                  name='Matched duration',
2531                                  symbol='T match',
2532                                  unit='s',
2533                                  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.',
2534                                  ))
2535
2536        self.add_col(am.ArrayConf('lengths_route_shortest', default=-1.0,
2537                                  dtype=np.float32,
2538                                  groupnames=['results'],
2539                                  name='Shortest length',
2540                                  symbol='L short',
2541                                  unit='m',
2542                                  info='Length of the shortest route.  Shortest route is connecting the first matched edge and the final matched edge.',
2543                                  ))
2544
2545        self.add_col(am.ArrayConf('durations_route_fastest', default=-1.0,
2546                                  dtype=np.float32,
2547                                  groupnames=['results'],
2548                                  name='Fastest duration',
2549                                  symbol='T fast',
2550                                  unit='s',
2551                                  info='Durations of the fastest route.  Fastest route is connecting the first matched edge and the final matched edge.',
2552                                  ))
2553
2554        self.add_col(am.ArrayConf('timelosses_route_fastest', default=-1.0,
2555                                  dtype=np.float32,
2556                                  groupnames=['results'],
2557                                  name='Fastest timeloss',
2558                                  symbol='dT fast',
2559                                  unit='s',
2560                                  info="""Time loss of matched route with respect to fastest route.  Fastest route is connecting the first matched edge and the final matched edge.
2561                                    Note that in order to be comparable, this time loss is calculated for matched and fastest route with the same edge travel times.
2562                                    These edge travel times are an approximation and are not necessarily identical with the experienced edge travel times.
2563                                    """,
2564                                  ))
2565
2566        self.add_col(am.ArrayConf('lengths_route_fastest', default=-1.0,
2567                                  dtype=np.float32,
2568                                  groupnames=['results'],
2569                                  name='Shortest length',
2570                                  symbol='L fast',
2571                                  unit='m',
2572                                  info='Length of the fastest route.  Fastest route is connecting the first matched edge and the final matched edge.',
2573                                  ))
2574
2575        self.add_col(am.ArrayConf('lengths_route_matched_mixed', default=-1.0,
2576                                  dtype=np.float32,
2577                                  groupnames=['results'],
2578                                  name='Matched length mixed access',
2579                                  symbol='L match mix',
2580                                  unit='m',
2581                                  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.',
2582                                  ))
2583
2584        self.add_col(am.ArrayConf('lengths_route_matched_exclusive', default=-1.0,
2585                                  dtype=np.float32,
2586                                  groupnames=['results'],
2587                                  name='Matched length exclusive access',
2588                                  symbol='L match excl',
2589                                  unit='m',
2590                                  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.',
2591                                  ))
2592
2593        self.add_col(am.ArrayConf('lengthindexes', default=-1.0,
2594                                  dtype=np.float32,
2595                                  groupnames=['results'],
2596                                  name='Length index',
2597                                  unit='%',
2598                                  info='Length index is the length of the matched route divided by length of line-interpolated GPS points.',
2599                                  ))
2600
2601        self.add_col(am.ArrayConf('errors_dist', default=-1.0,
2602                                  dtype=np.float32,
2603                                  groupnames=['results'],
2604                                  name='Distance error',
2605                                  unit='mm',
2606                                  info='The distance error is the average distance between the GPS points and the matched route.',
2607                                  ))
2608
2609        self.add_col(am.ArrayConf('times_computation', default=-1.0,
2610                                  dtype=np.float32,
2611                                  groupnames=['results'],
2612                                  name='Computation time',
2613                                  unit='ms',
2614                                  info='Computation time of the match algorithm.',
2615                                  ))
2616
2617        self.add_col(am.ArrayConf('are_match_connected', default=False,
2618                                  dtype=np.bool,
2619                                  groupnames=['results', ],
2620                                  name='Match connected',
2621                                  #symbol = 'Match conn.',
2622                                  info='The matched route connects first and last network edge where GPS points have been detected.',
2623                                  ))
2624
2625        self.add_col(am.IdlistsArrayConf('ids_points', self.parent.points,
2626                                         #groupnames = ['_private'],
2627                                         name='Point IDs',
2628                                         info="GPS point IDs.",
2629                                         ))
2630
2631        self.add_col(am.IdlistsArrayConf('ids_points_edgeend', self.parent.points,
2632                                         groupnames=['results', '_private'],
2633                                         name='Edge endpoint IDs',
2634                                         info="This is a list of GPS point IDs which represent the last point associated with each matched edge.",
2635                                         ))
2636        # update
2637        self.ids_points_edgeend.add_groupnames(['_private'])
2638
2639    def get_obj_vtypes(self):
2640        return self.parent.get_scenario().demand.vtypes
2641
2642    def get_routes(self):
2643        return self.routes.get_value()
2644
2645    def clear_routes(self):
2646        self.get_routes().clear()
2647        for attrconf in self.get_group('results'):
2648            attrconf.reset()
2649
2650    def select_all(self):
2651        self.are_selected.get_value()[:] = True
2652
2653    def unselect_all(self):
2654        self.are_selected.get_value()[:] = False
2655
2656    def invert_selection(self):
2657        self.are_selected.get_value()[:] = np.logical_not(self.are_selected.get_value()[:])
2658
2659    def set_matched_route(self, id_trip, route,
2660                          length_matched=0.0,
2661                          length_route_mixed=0.0,
2662                          length_route_exclusive=0.0,
2663                          duration_matched=0.0,
2664                          lengthindex=-1.0,
2665                          error_dist=-1.0,
2666                          comptime=0.0,
2667                          is_connected=False,
2668                          ids_point_edgeend=[],
2669                          color=COLOR_MATCHED_ROUTE.copy(),
2670                          ):
2671
2672        if len(route) > 0:
2673            id_route = self.ids_route_matched[id_trip]
2674            if id_route >= 0:
2675                # already a matched route existant
2676                self.get_routes().ids_edges[id_route] = route
2677                self.get_routes().colors[id_route] = color
2678                # self.get_routes().set_row(  id_route,
2679                #                            ids_edges = route,
2680                #                            colors = COLOR_MATCHED_ROUTE,
2681                #                            )
2682
2683                # self.get_routes().set_row(  id_route,
2684                #                            ids_trip = id_trip,
2685                #                            ids_edges = route,
2686                #                            #costs = duration_matched,
2687                #                            #probabilities = 1.0,
2688                #                        )
2689            else:
2690                id_route = self.get_routes().add_row(ids_trip=id_trip,
2691                                                     ids_edges=route,
2692                                                     #costs = duration_matched,
2693                                                     #probabilities = 1.0,
2694                                                     colors=color,
2695                                                     )
2696        else:
2697            id_route = -1
2698
2699        # print 'set_matched_route id_trip', id_trip,'id_route', id_route
2700        # print '  ids_point_edgeend',ids_point_edgeend
2701        #self.ids_route_matched[id_trip] = id_route
2702
2703        self.set_row(id_trip,
2704                     ids_route_matched=id_route,
2705                     lengths_gpsroute_matched=length_matched/lengthindex,
2706                     durations_route_matched=duration_matched,
2707                     lengths_route_matched=length_matched,
2708                     lengths_route_matched_mixed=length_route_mixed,
2709                     lengths_route_matched_exclusive=length_route_exclusive,
2710                     lengthindexes=100*lengthindex,
2711                     errors_dist=1000 * error_dist,
2712                     times_computation=1000*comptime,
2713                     are_match_connected=is_connected
2714                     )
2715        # TODO:  do this extra! this is a bug!!
2716        # if included in set_row, only first value in list is taken!!!
2717        self.ids_points_edgeend[id_trip] = ids_point_edgeend
2718
2719        # print '  ids_points_edgeend[id_trip]',self.ids_points_edgeend[id_trip]
2720
2721        return id_route
2722
2723    def make(self,  **kwargs):
2724        # if self.ids_sumo.has_index(id_sumo):
2725        #    id_trip = self.ids_sumo.get_id_from_index(id_sumo)
2726        #    #self.set_row(id_sumo, **kwargs)
2727        #    return id_trip
2728        # else:
2729
2730        #purpose = cols[j_speed_av].strip(),
2731        #device = cols[j_device].strip(),
2732        device = kwargs.get('device', None)
2733        if device in DEVICES:
2734            id_device = DEVICES[device]
2735        else:
2736            id_device = DEVICES['unknown']
2737
2738        purpose = kwargs.get('purpose', None)
2739        if purpose in TRIPPUROPSES:
2740            id_purpose = TRIPPUROPSES[purpose]
2741        else:
2742            id_purpose = TRIPPUROPSES['unknown']
2743
2744        id_trip = self.add_row(ids_sumo=kwargs.get('id_sumo', None),
2745                               ids_vtype=kwargs.get('id_vtype', None),
2746                               timestamps=kwargs.get('timestamp', None),
2747                               distances_gps=kwargs.get('distance_gps', None),
2748                               durations_gps=kwargs.get('duration_gps', None),
2749                               speeds_average=kwargs.get('speed_average', None),
2750                               speeds_max=kwargs.get('speed_max', None),
2751                               ids_purpose=id_purpose,
2752                               ids_device=id_device,
2753                               ids_points=kwargs.get('ids_point', None),
2754                               )
2755        return id_trip
2756
2757    def set_points(self, id_trip, ids_point):
2758        self.ids_points[id_trip] = ids_point
2759
2760    def get_ids_selected(self):
2761        return self.select_ids(self.are_selected.get_value())
2762
2763    def route_fastest_with_waits(self, time_modespecific=3.0, c_modespecific=0.9,
2764                                 is_ignor_connections=False, times_wait_nodes=None,
2765                                 speeds_in_motion=None, dist_min_modespecific=15.0,
2766                                 color_route=COLOR_FASTEST_ROUTE.copy()):
2767        """
2768        Shortest fastest routing.
2769        """
2770        print 'route_fastest_with_waits', time_modespecific, c_modespecific
2771        # TODO: if too mant vtypes, better go through id_modes
2772        exectime_start = time.clock()
2773        scenario = self.parent.get_scenario()
2774        net = scenario.net
2775        edges = net.edges
2776        vtypes = scenario.demand.vtypes
2777        routes = self.get_routes()
2778        #ids_edges = []
2779        #ids_trip = []
2780        #costs = []
2781
2782        distancesmap = self.parent.get_distancesmap()
2783        accesslevelsmap = self.parent.get_accesslevelsmap()
2784
2785        # delete current
2786        #ids_with_shortes = self.select_ids(np.logical_and(self.are_selected.get_value(), self.ids_route_shortest.get_value()>=0))
2787        # routes.del_rows(self.ids_route_shortest[ids_with_shortes])
2788        #self.ids_route_shortest[ids_with_shortes] = -1
2789
2790        fstar = edges.get_fstar(is_ignor_connections=is_ignor_connections)
2791        for id_vtype in self.get_vtypes():
2792            id_mode = vtypes.ids_mode[id_vtype]
2793
2794            # no routing for pedestrians
2795            if id_mode != net.modes.get_id_mode('pedestrian'):
2796                dists_orig = distancesmap[id_mode].copy()
2797                times = np.zeros(len(dists_orig), dtype=np.float32)
2798                weights = np.zeros(len(dists_orig), dtype=np.float32)
2799
2800                # this will subtract some meters dependent on
2801                # access-level of the edge
2802                accesslevels = accesslevelsmap[id_mode]
2803                ids_edge = edges.get_ids()
2804                are_valid = dists_orig > dist_min_modespecific
2805
2806                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value(
2807                ) == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value() >= 0))
2808                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
2809                # print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
2810                # print '  weights',weights
2811
2812                edgewaittimes = times_wait_nodes[edges.ids_tonode[ids_edge]]
2813                specifictimes = time_modespecific * are_valid[ids_edge] * accesslevels[ids_edge]
2814                specificfactors = 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge] == 2)
2815
2816                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
2817                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
2818
2819                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]):
2820                    route_matched = routes.ids_edges[id_route]
2821                    # print '  id_trip,speed_inmotion',id_trip,speed_inmotion
2822                    times[ids_edge] = dists_orig[ids_edge]/speed_inmotion + edgewaittimes
2823                    weights[ids_edge] = (times[ids_edge] - specifictimes)*specificfactors
2824                    # weights[ids_edge] -=
2825                    # weights[ids_edge] *=
2826
2827                    # compute time wit speed in motion and add waiting time of tonode
2828                    cost, route = routing.get_mincostroute_edge2edge(route_matched[0],
2829                                                                     route_matched[-1],
2830                                                                     weights=weights,
2831                                                                     fstar=fstar)
2832                    if len(route) > 0:
2833                        # ids_edges.append(route)
2834                        # ids_trip.append(id_trip)
2835                        # costs.append(cost)
2836
2837                        id_route = self.ids_route_shortest[id_trip]
2838                        if id_route >= 0:
2839                            # there is already a previous shortest route
2840                            routes.set_row(id_route,
2841                                           #ids_edges = list(route),
2842                                           costs=cost,
2843                                           colors=color_route,
2844                                           )
2845                            # TODO!!! this assigment does not work in set_row!!!!
2846                            # tales only first edge !! why??
2847                            routes.ids_edges[id_route] = route
2848                            # print '  old route',id_route,type(route)
2849                        else:
2850                            # create new route
2851                            id_route = routes.add_row(ids_trip=id_trip,
2852                                                      ids_edges=route,
2853                                                      costs=cost,
2854                                                      colors=color_route,
2855                                                      )
2856                            self.ids_route_fastest[id_trip] = id_route
2857                            # print '  new route',type(route)
2858
2859                        self.lengths_route_fastest[id_trip] = np.sum(dists_orig[route])
2860                        self.durations_route_fastest[id_trip] = np.sum(times[route])
2861                        self.timelosses_route_fastest[id_trip] = np.sum(
2862                            times[route_matched])-self.durations_route_fastest[id_trip]
2863                        # print '   route', route
2864                        # print '   times',times[route]
2865                        # print '  routes.ids_edges' ,routes.ids_edges[id_route]
2866
2867        print '  exectime', time.clock()-exectime_start
2868
2869    def route_fastest(self, time_modespecific=3.0, c_modespecific=0.9,
2870                      is_ignor_connections=False, color_route=COLOR_FASTEST_ROUTE.copy()):
2871        """
2872        Shortest fastest routing.
2873        """
2874        print 'route_fastest', time_modespecific, c_modespecific
2875        # TODO: if too mant vtypes, better go through id_modes
2876        exectime_start = time.clock()
2877        scenario = self.parent.get_scenario()
2878        net = scenario.net
2879        edges = net.edges
2880        vtypes = scenario.demand.vtypes
2881        routes = self.get_routes()
2882
2883        timesmap = self.parent.get_timesmap()
2884        #distancesmap = self.parent.get_distancesmap()
2885        accesslevelsmap = self.parent.get_accesslevelsmap()
2886
2887        fstar = edges.get_fstar(is_ignor_connections=is_ignor_connections)
2888        for id_vtype in self.get_vtypes():
2889            id_mode = vtypes.ids_mode[id_vtype]
2890
2891            # no routing for pedestrians
2892            if id_mode != net.modes.get_id_mode('pedestrian'):
2893                #dists = distancesmap[id_mode]
2894                times_orig = timesmap[id_mode].copy()
2895
2896                # TODO: needs to be improved with default junction waits
2897                weights = times_orig.copy()
2898
2899                # this will subtract some meters dependent on
2900
2901                accesslevels = accesslevelsmap[id_mode]
2902                ids_edge = edges.get_ids()
2903                are_valid = weights > 3*time_modespecific
2904
2905                weights[ids_edge] -= time_modespecific * are_valid[ids_edge] * accesslevels[ids_edge]
2906                weights[ids_edge] *= 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge] == 2)
2907
2908                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value(
2909                ) == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value() >= 0))
2910                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
2911                # print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
2912                # print '  weights',weights
2913
2914                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
2915                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
2916
2917                for id_trip, id_route in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype]):
2918                    route_matched = routes.ids_edges[id_route]
2919
2920                    cost, route = routing.get_mincostroute_edge2edge(route_matched[0],
2921                                                                     route_matched[-1],
2922                                                                     weights=weights,
2923                                                                     fstar=fstar,
2924                                                                     )
2925
2926                    if len(route) > 0:
2927                        # ids_edges.append(route)
2928                        # ids_trip.append(id_trip)
2929                        # costs.append(cost)
2930
2931                        id_route = self.ids_route_fastest[id_trip]
2932                        if id_route >= 0:
2933                            # there is already a previous shortest route
2934                            routes.set_row(id_route,
2935                                           #ids_edges = list(route),
2936                                           costs=cost,
2937                                           colors=color_route,
2938                                           )
2939                            # TODO!!! this assigment does not work in set_row!!!!
2940                            # tales only first edge !! why??
2941                            routes.ids_edges[id_route] = route
2942                            # print '  old route',id_route,type(route)
2943                        else:
2944                            # create new route
2945                            id_route = routes.add_row(ids_trip=id_trip,
2946                                                      ids_edges=route,
2947                                                      costs=cost,
2948                                                      colors=color_route,
2949                                                      )
2950                            self.ids_route_fastest[id_trip] = id_route
2951                            # print '  new route',type(route)
2952
2953                        self.durations_route_fastest[id_trip] = np.sum(times_orig[route])
2954                        self.timelosses_route_fastest[id_trip] = np.sum(
2955                            times_orig[route_matched])-self.durations_route_fastest[id_trip]
2956                        print '  route', route
2957                        print '  routes.ids_edges', routes.ids_edges[id_route]
2958
2959        print '  exectime', time.clock()-exectime_start
2960
2961    def route_shortest(self, dist_modespecific=5.0, c_modespecific=0.9,
2962                       is_ignor_connections=False, dist_min_modespecific=15.0,
2963                       color_route=None):
2964        """
2965        Shortest path routing.
2966        """
2967        print 'route_shortest', dist_modespecific, c_modespecific
2968        # TODO: if too mant vtypes, better go through id_modes
2969        exectime_start = time.clock()
2970        scenario = self.parent.get_scenario()
2971        net = scenario.net
2972        edges = net.edges
2973        vtypes = scenario.demand.vtypes
2974        routes = self.get_routes()
2975        #ids_edges = []
2976        #ids_trip = []
2977        #costs = []
2978
2979        distancesmap = self.parent.get_distancesmap()
2980        accesslevelsmap = self.parent.get_accesslevelsmap()
2981
2982        # delete current
2983        #ids_with_shortes = self.select_ids(np.logical_and(self.are_selected.get_value(), self.ids_route_shortest.get_value()>=0))
2984        # routes.del_rows(self.ids_route_shortest[ids_with_shortes])
2985        #self.ids_route_shortest[ids_with_shortes] = -1
2986
2987        fstar = edges.get_fstar(is_ignor_connections=is_ignor_connections)
2988        for id_vtype in self.get_vtypes():
2989            id_mode = vtypes.ids_mode[id_vtype]
2990
2991            # no routing for pedestrians
2992            if id_mode != net.modes.get_id_mode('pedestrian'):
2993                dists_orig = distancesmap[id_mode].copy()
2994                weights = dists_orig.copy()
2995
2996                # this will subtract some meters dependent on
2997                # access-level of the edge
2998                accesslevels = accesslevelsmap[id_mode]
2999                ids_edge = edges.get_ids()
3000                are_valid = weights > dist_min_modespecific
3001                weights[ids_edge] -= dist_modespecific * are_valid[ids_edge] * accesslevels[ids_edge]
3002                weights[ids_edge] *= 1 - (1-c_modespecific) * are_valid[ids_edge] * (accesslevels[ids_edge] == 2)
3003
3004                ids_trip_vtype = self.select_ids(np.logical_and(self.ids_vtype.get_value(
3005                ) == id_vtype, self.are_selected.get_value(), self.ids_route_matched.get_value() >= 0))
3006                #ids_trip_vtype = self.get_trips_for_vtype(id_vtype)
3007                # print '  id_vtype,id_mode',id_vtype,id_mode#,ids_trip_vtype
3008                # print '  weights',weights
3009
3010                #ids_edge_depart = self.ids_edge_depart[ids_trip_vtype]
3011                #ids_edge_arrival = self.ids_edge_arrival[ids_trip_vtype]
3012
3013                for id_trip, id_route in zip(ids_trip_vtype, self.ids_route_matched[ids_trip_vtype]):
3014                    route_matched = routes.ids_edges[id_route]
3015
3016                    cost, route = routing.get_mincostroute_edge2edge(route_matched[0],
3017                                                                     route_matched[-1],
3018                                                                     weights=weights,
3019                                                                     fstar=fstar)
3020                    if len(route) > 0:
3021                        # ids_edges.append(route)
3022                        # ids_trip.append(id_trip)
3023                        # costs.append(cost)
3024
3025                        id_route = self.ids_route_shortest[id_trip]
3026                        if id_route >= 0:
3027                            # there is already a previous shortest route
3028                            routes.set_row(id_route,
3029                                           #ids_edges = list(route),
3030                                           costs=cost,
3031                                           colors=color_route,
3032                                           )
3033                            # TODO!!! this assigment does not work in set_row!!!!
3034                            # tales only first edge !! why??
3035                            routes.ids_edges[id_route] = route
3036                            # print '  old route',id_route,type(route)
3037                        else:
3038                            # create new route
3039                            id_route = routes.add_row(ids_trip=id_trip,
3040                                                      ids_edges=route,
3041                                                      costs=cost,
3042                                                      colors=color_route,
3043                                                      )
3044                            self.ids_route_shortest[id_trip] = id_route
3045                            # print '  new route',type(route)
3046
3047                        self.lengths_route_shortest[id_trip] = np.sum(dists_orig[route])
3048                        # print '  route', route
3049                        # print '  routes.ids_edges' ,routes.ids_edges[id_route]
3050
3051        print '  exectime', time.clock()-exectime_start
3052
3053    def get_ids_edge_matched(self, id_trip):
3054        return self.get_routes().ids_edges[self.ids_route_matched[id_trip]]
3055
3056    def get_speedprofile(self, id_trip):
3057        """
3058        NOT USED!!??
3059        """
3060
3061        points = self.parent.points
3062        ids_point = self.ids_points[id_trip]
3063        ids_point_edgeend = self.ids_points_edgeend[id_trip]
3064        ids_edge = self.get_routes().ids_edges[self.ids_route_matched[id_trip]]
3065        id_edge_current = -1
3066        n_edges = len(ids_edge)
3067
3068        positions_gps = []
3069        times_gps = []
3070        ids_edges_profile = []
3071        ids_nodes_profile = []
3072
3073        for id_point, coord, timestamp in zip(ids_point, points.coords[ids_point], points.timestamps[ids_point]):
3074            #get_pos_from_coord(id_edge_current, coord)
3075
3076            if id_point in ids_point_edgeend:
3077                ind = ids_point_edgeend.index(id_point)
3078                if ind == n_edges-1:
3079                    id_edge_current = -1
3080                else:
3081                    id_edge_current = ids_edge[ind+1]
3082
3083    def get_flows(self, is_shortest_path=False):
3084        """
3085        Determine the total number of vehicles for each edge.
3086        returns ids_edge and flows
3087        """
3088        ids_edges = self.get_routes.ids_edges
3089        counts = np.zeros(np.max(self.get_net().edges.get_ids())+1, int)
3090
3091        ids_trip = self.get_ids_selected()
3092        if not is_shortest_path:
3093            ids_route = self.ids_route_matched[ids_trip]
3094        else:
3095            ids_route = self.ids_route_shortest[ids_trip]
3096        inds_valid = np.flatnonzero(ids_route > 0)
3097        for id_trip, id_route in zip(ids_trip[inds_valid], ids_route[inds_valid]):
3098            counts[ids_edges[id_route][:]] += 1
3099
3100        ids_edge = np.flatnonzero(counts)
3101
3102        return ids_edge, counts[ids_edge].copy()
3103
3104    def get_ids_route_selected(self):
3105        # TODO: here we could append direct routes
3106        print 'get_ids_route_selected'
3107        ids_route_matched = self.ids_route_matched[self.get_ids_selected()]
3108        ids_route_shortest = self.ids_route_shortest[self.get_ids_selected()]
3109        ids_route_fastest = self.ids_route_fastest[self.get_ids_selected()]
3110        # print '  ids_route_matched.dtype',ids_route_matched.dtype
3111        # print '  ids_route_shortest.dtype',ids_route_shortest.dtype
3112        # print '  ids_route_matched[ids_route_matched >= 0] ',ids_route_matched[ids_route_matched >= 0]
3113        return np.concatenate([ids_route_matched[ids_route_matched >= 0],
3114                               ids_route_shortest[ids_route_shortest >= 0],
3115                               ids_route_fastest[ids_route_fastest >= 0]])
3116        # return  ids_route_matched[ids_route_matched >= 0]
3117
3118
3119class GpsPoints(am.ArrayObjman):
3120    """
3121    Contains data of points of a single trace.
3122    """
3123
3124    def __init__(self, ident, mapmatching, **kwargs):
3125        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
3126        self._init_objman(ident=ident,
3127                          parent=mapmatching,
3128                          name='GPS Points',
3129                          info='GPS points database.',
3130                          version=0.0,
3131                          **kwargs)
3132
3133        self._init_attributes()
3134        self._init_constants()
3135
3136    def _init_attributes(self):
3137        scenario = self.get_scenario()
3138
3139        # ident is the path id of the trace
3140
3141        # the actual numpy arrays are stored in .cols
3142        self.add_col(am.ArrayConf('longitudes',     default=0.0,
3143                                  dtype=np.float32,
3144                                  groupnames=['parameters', ],
3145                                  perm='rw',
3146                                  name='Longitude',
3147                                  symbol='Lon',
3148                                  unit='deg',
3149                                  info='Longitude  of point',
3150                                  ))
3151
3152        self.add_col(am.ArrayConf('latitudes',   default=0.0,
3153                                  groupnames=['parameters', ],
3154                                  dtype=np.float32,
3155                                  perm='rw',
3156                                  name='Latitude',
3157                                  symbol='Lat',
3158                                  unit='deg',
3159                                  info='Latitude of point',
3160                                  ))
3161
3162        self.add_col(am.ArrayConf('altitudes',   default=-100000.0,
3163                                  groupnames=['parameters', ],
3164                                  dtype=np.float32,
3165                                  perm='rw',
3166                                  name='Altitude',
3167                                  symbol='Alt',
3168                                  unit='m',
3169                                  info='Altitude of point',
3170                                  ))
3171
3172        self.add_col(am.ArrayConf('radii',  10.0,
3173                                  dtype=np.float32,
3174                                  groupnames=['parameters', ],
3175                                  perm='rw',
3176                                  name='Radius',
3177                                  unit='m',
3178                                  info='Point radius, representing the imprecision of the point, which depends on the recording device ane the environment.',
3179                                  ))
3180
3181        self.add_col(am.ArrayConf('coords',    default=[0.0, 0.0, 0.0],
3182                                  groupnames=['parameters', ],
3183                                  dtype=np.float32,
3184                                  perm='rw',
3185                                  name='Coordinate',
3186                                  symbol='x,y,z',
3187                                  unit='m',
3188                                  info='Local 3D coordinate  of point',
3189                                  ))
3190
3191        self.add_col(am.ArrayConf('timestamps',  default=0.0,
3192                                  dtype=np.float,
3193                                  groupnames=['parameters', ],
3194                                  perm='r',
3195                                  name='timestamp',
3196                                  symbol='t',
3197                                  metatype='datetime',
3198                                  unit='s',
3199                                  digits_fraction=2,
3200                                  info='Time stamp of point in seconds after 01 January 1970.',
3201                                  ))
3202        # test:
3203        self.timestamps.metatype = 'datetime'
3204        self.timestamps.set_perm('r')
3205
3206    def set_trips(self, trips):
3207        self.add_col(am.IdsArrayConf('ids_trip', trips,
3208                                     groupnames=['state'],
3209                                     name='Trip ID',
3210                                     info='ID of trips to which this point belongs to.',
3211                                     ))
3212        # put in geometry filter
3213        # self.add_col(am.ArrayConf( 'are_inside_boundary',  default =False,
3214        #                                groupnames = ['parameters',],
3215        #                                perm='r',
3216        #                                name = 'in boundary',
3217        #                                info = 'True if this the data point is within the boundaries of the road network.',
3218        #                                ))
3219
3220    def get_ids_selected(self):
3221        """
3222        Returns point ids of selected traces
3223        """
3224        # print 'GpsPoints.get_ids_selected'
3225        # print '  ??ids_points = ',self.select_ids(self.parent.trips.are_selected[self.ids_trip.get_value()] )
3226        # TODO: why is this working??? do we need trips.ids_points????
3227        return self.select_ids(self.parent.trips.are_selected[self.ids_trip.get_value()])
3228        # return self.get_ids()#self.select_ids(self.get_ids()
3229        # return self.select_ids(
3230
3231    def get_scenario(self):
3232        return self.parent.get_scenario()
3233
3234    def get_coords(self):
3235        """
3236        Returns an array of x,y coordinates of all points.
3237        """
3238        return self.coords.get_value()
3239
3240    def get_times(self):
3241        """
3242        Returns an array of time stamps of all points.
3243        """
3244        return self.timestamp.get_value()
3245
3246    def get_interval(self):
3247        if len(self) == 0:
3248            return [0.0, 0.0]
3249        else:
3250            timestamps = self.timestamps.get_value()
3251            return [timestamps.min(), timestamps.max()]
3252
3253    def get_duration(self):
3254        ts, te = self.get_interval()
3255        return te-ts
3256
3257    def get_distance(self):
3258        v = self.get_coords()
3259        # return np.linalg.norm( self.cols.coords[0:-1] - self.cols.coords[1:] )
3260        return np.sum(np.sqrt((v[1:, 0]-v[0:-1, 0])**2 + (v[1:, 1]-v[0:-1, 1])**2))
3261
3262    def get_boundary(self):
3263        if len(self) == 0:
3264            return [0, 0, 0, 0]
3265        else:
3266            x_min, y_min = self.get_coords().min(0)
3267            x_max, y_max = self.get_coords().max(0)
3268
3269            return [x_min, y_min, x_max, y_max]
3270
3271    def project(self, proj=None, offset=None):
3272        if proj is None:
3273            proj, offset = self.parent.get_proj_and_offset()
3274        x, y = proj(self.longitudes.get_value(), self.latitudes.get_value())
3275        self.get_coords()[:] = np.transpose(np.concatenate(
3276            ([x+offset[0]], [y+offset[1]], [self.altitudes.get_value()]), axis=0))
3277
3278
3279class GpsPersons(am.ArrayObjman):
3280
3281    def __init__(self, ident, mapmatching, **kwargs):
3282        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
3283        self._init_objman(ident=ident,
3284                          parent=mapmatching,
3285                          name='GPS Persons',
3286                          info='GPS person database.',
3287                          version=0.2,
3288                          **kwargs)
3289
3290        self._init_attributes()
3291
3292    def _init_attributes(self):
3293        trips = self.parent.trips
3294
3295        # TODO: add/update vtypes here
3296        self.add_col(SumoIdsConf('User', xmltag='id'))
3297
3298        self.add_col(am.ArrayConf('ids_gender', default=-1,
3299                                  dtype=np.int32,
3300                                  groupnames=['parameters'],
3301                                  choices=GENDERS,
3302                                  name='Gender',
3303                                  info='Gender of person.',
3304                                  ))
3305
3306        self.add_col(am.ArrayConf('years_birth', default=-1,
3307                                  dtype=np.int32,
3308                                  groupnames=['parameters'],
3309                                  name='Birth year',
3310                                  info='Year when person has been born.',
3311                                  ))
3312
3313        self.add_col(am.ArrayConf('ids_occupation', default=OCCUPATIONS['unknown'],
3314                                  dtype=np.int32,
3315                                  choices=OCCUPATIONS,
3316                                  groupnames=['parameters'],
3317                                  name='occupation',
3318                                  info='Tupe of occupation',
3319                                  ))
3320
3321        self.add_col(am.ArrayConf('are_frequent_user', False,
3322                                  dtype=np.bool,
3323                                  groupnames=['parameters'],
3324                                  name='frequent user',
3325                                  info='If true, this person is a frequent user of the recorded transport mode.',
3326                                  ))
3327
3328        # change from int ti string
3329        print 'GpsPersons.versio,', self.get_version(), self.get_version() < 0.2
3330
3331        if hasattr(self, 'zips'):
3332                # print '  zips',self.zips.get_value().dtype,self.zips.get_value()
3333            if self.zips.get_value().dtype in [np.dtype(np.int32), np.dtype(np.int64)]:
3334                print 'WARNING: delete old person.zips'
3335                self.delete('zips')
3336
3337        # if self.get_version()<0.2:
3338        #    #if hasattr(self,'zips'):
3339        #   self.delete('zips')
3340
3341        self.add_col(am.ArrayConf('zips', '',
3342                                  dtype=np.object,
3343                                  groupnames=['parameters'],
3344                                  name='ZIP',
3345                                  info="ZIP code of person's home.",
3346                                  ))
3347
3348        self.add_col(am.IdlistsArrayConf('ids_trips', trips,
3349                                         #groupnames = ['_private'],
3350                                         name='Trip IDs',
3351                                         info="IDs of trips made by this vehicle. This is a collection of recorded trips associated with this person.",
3352                                         ))
3353
3354        self.add_col(am.ArrayConf('numbers_tot_trip_gps', 0,
3355                                  dtype=np.int32,
3356                                  groupnames=['results'],
3357                                  name='tot. trips',
3358                                  symbol='N tot GPS',
3359                                  info='Total number of recorded GPS traces.',
3360                                  ))
3361
3362        self.add_col(am.ArrayConf('lengths_tot_route_gps', 0.0,
3363                                  dtype=np.float32,
3364                                  groupnames=['results'],
3365                                  name='tot. GPS length',
3366                                  symbol='L tot GPS',
3367                                  unit='m',
3368                                  info='Total distances of recorded GPS traces.',
3369                                  ))
3370        self.add_col(am.ArrayConf('times_tot_route_gps', 0.0,
3371                                  dtype=np.float32,
3372                                  groupnames=['results'],
3373                                  name='Tot. GPS time',
3374                                  symbol='T tot GPS',
3375                                  unit='s',
3376                                  info='Total trip times of recorded GPS traces.',
3377                                  ))
3378
3379        self.add_col(am.ArrayConf('speeds_av_gps', 0.0,
3380                                  dtype=np.float32,
3381                                  groupnames=['results'],
3382                                  name='Tot. GPS time',
3383                                  symbol='V avg GPS',
3384                                  unit='m/s',
3385                                  info='Average speed of recorded GPS traces.',
3386                                  ))
3387
3388        self.add_col(am.ArrayConf('numbers_tot_trip_mached', 0,
3389                                  dtype=np.int32,
3390                                  groupnames=['results'],
3391                                  name='tot. trips',
3392                                  symbol='N tot match',
3393                                  info='Total number of recorded GPS traces.',
3394                                  ))
3395
3396        self.add_col(am.ArrayConf('lengths_tot_route_matched', default=-1.0,
3397                                  dtype=np.float32,
3398                                  groupnames=['results'],
3399                                  name='Tot. matched length',
3400                                  symbol='L tot match',
3401                                  unit='m',
3402                                  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.',
3403                                  ))
3404
3405        # self.add_col(am.ArrayConf('times_tot_route_matched', default = -1.0,
3406        #                            dtype = np.float32,
3407        #                            groupnames = ['results'],
3408        #                            name = 'Tot. matched duration',
3409        #                            symbol = 'T tot match',
3410        #                            unit = 's',
3411        #                            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.',
3412        #                            ))
3413
3414        self.add_col(am.ArrayConf('lengths_tot_route_shortest', default=-1.0,
3415                                  dtype=np.float32,
3416                                  groupnames=['results'],
3417                                  name='Tot. shortest length',
3418                                  symbol='L tot short',
3419                                  unit='m',
3420                                  info='Total length of the shortest routes.  Shortest route is connecting the first matched edge and the final matched edge.',
3421                                  ))
3422
3423        self.add_col(am.ArrayConf('lengths_tot_route_matched_mixed', default=-1.0,
3424                                  dtype=np.float32,
3425                                  groupnames=['results'],
3426                                  name='Tot. matched length mixed access',
3427                                  symbol='L tot match mix',
3428                                  unit='m',
3429                                  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.',
3430                                  ))
3431
3432        self.add_col(am.ArrayConf('lengths_tot_route_matched_exclusive', default=-1.0,
3433                                  dtype=np.float32,
3434                                  groupnames=['results'],
3435                                  name='Matched length exclusive access',
3436                                  symbol='L tot match excl',
3437                                  unit='m',
3438                                  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.',
3439                                  ))
3440
3441        # upgrade
3442        if hasattr(self, 'ids_genders'):
3443            self.delete('ids_genders')
3444
3445    def analyze(self):
3446        print 'Persons.analyze'
3447        # ids_person = self.select_ids(self.)
3448        trips = self.parent.trips
3449        points = self.parent.points
3450        npsum = np.sum
3451
3452        ids_person = self.get_ids()
3453        n_pers = len(ids_person)
3454
3455        numbers_tot_trip_gps = np.zeros(n_pers, dtype=np.int32)
3456        lengths_tot_route_gps = np.zeros(n_pers, dtype=np.float32)
3457        times_tot_route_gps = np.zeros(n_pers, dtype=np.float32)
3458        speeds_av_gps = np.zeros(n_pers, dtype=np.float32)
3459        numbers_tot_trip_mached = np.zeros(n_pers, dtype=np.int32)
3460        lengths_tot_route_matched = np.zeros(n_pers, dtype=np.float32)
3461        times_tot_route_matched = np.zeros(n_pers, dtype=np.float32)
3462        lengths_tot_route_shortest = np.zeros(n_pers, dtype=np.float32)
3463        lengths_tot_route_matched_mixed = np.zeros(n_pers, dtype=np.float32)
3464        lengths_tot_route_matched_exclusive = np.zeros(n_pers, dtype=np.float32)
3465
3466        timebudgets = -1*np.ones(n_pers, dtype=np.float32)
3467
3468        i = 0
3469        for id_person, ids_trip_all in zip(ids_person, self.ids_trips[ids_person]):
3470            # get selected trips only
3471            ids_trip = np.array(ids_trip_all)[trips.are_selected[ids_trip_all]]
3472
3473            numbers_tot_trip_gps[i] = len(ids_trip)
3474            # print '  id_person, ids_trip',id_person, ids_trip
3475            if len(ids_trip) > 0:
3476
3477                length_tot = npsum(trips.distances_gps[ids_trip])
3478                time_tot = npsum(trips.durations_gps[ids_trip])
3479
3480                lengths_tot_route_gps[i] = length_tot
3481                times_tot_route_gps[i] = time_tot
3482                if time_tot > 0:
3483                    speeds_av_gps[i] = length_tot/time_tot
3484
3485                # do statistics on matched trips
3486                # print '    ids_route_shortest',trips.ids_route_shortest[ids_trip]
3487                # print '    inds',trips.ids_route_shortest[ids_trip]>=0
3488                ids_trip_matched = ids_trip[trips.ids_route_shortest[ids_trip] >= 0]
3489                if len(ids_trip_matched) > 0:
3490                    numbers_tot_trip_mached[i] = len(ids_trip_matched)
3491                    lengths_tot_route_matched[i] = npsum(trips.lengths_route_matched[ids_trip_matched])
3492                    #times_tot_route_matched[i] = npsum(trips.lengths_route_matched[ids_trip])
3493                    lengths_tot_route_shortest[i] = npsum(trips.lengths_route_shortest[ids_trip_matched])
3494                    lengths_tot_route_matched_mixed[i] = npsum(trips.lengths_route_matched_mixed[ids_trip_matched])
3495                    lengths_tot_route_matched_exclusive[i] = npsum(
3496                        trips.lengths_route_matched_exclusive[ids_trip_matched])
3497
3498                # do statistics time budget
3499                # coord0 = None#points.coords[ids_points[ids_trip[0]]]
3500                # date = None# points.timestamps[ids_points[ids_trip[0]]]
3501                # for ids_point in trips.ids_points[ids_trip]:
3502                #    coord0 = None#points.coords[ids_points[ids_trip[0]]]
3503                #    date = None# points.timestamps[ids_points[ids_trip[0]]]
3504
3505            i += 1
3506
3507        self.set_rows(ids_person,
3508                      numbers_tot_trip_gps=numbers_tot_trip_gps,
3509                      lengths_tot_route_gps=lengths_tot_route_gps,
3510                      times_tot_route_gps=times_tot_route_gps,
3511                      speeds_av_gps=speeds_av_gps,
3512                      numbers_tot_trip_mached=numbers_tot_trip_mached,
3513                      lengths_tot_route_matched=lengths_tot_route_matched,
3514                      times_tot_route_matched=times_tot_route_matched,
3515                      lengths_tot_route_shortest=lengths_tot_route_shortest,
3516                      lengths_tot_route_matched_mixed=lengths_tot_route_matched_mixed,
3517                      lengths_tot_route_matched_exclusive=lengths_tot_route_matched_exclusive,
3518                      )
3519
3520    def make(self, id_sumo, **kwargs):
3521        print 'make id_pers_sumo', id_sumo
3522
3523        id_trip = kwargs.get('id_trip', -1)
3524        if self.ids_sumo.has_index(id_sumo):
3525            # person exisis
3526            id_pers = self.ids_sumo.get_id_from_index(id_sumo)
3527
3528            if id_trip >= 0:
3529                self.ids_trips[id_pers].append(id_trip)
3530
3531            # debug
3532            # print '  exists id_pers',id_pers,self.ids_sumo[id_pers]
3533            #trips = self.parent.trips
3534            # for id_trip in self.ids_trips[id_pers]:
3535            #    print '    id_trip',id_trip,trips.ids_sumo[id_trip]
3536
3537            #self.set_row(id_pers, **kwargs)
3538            return id_pers
3539
3540        else:
3541            # print 'make new person',kwargs
3542            # add new person
3543            # if id_trip >= 0:
3544            #    ids_trip = [id_trip]
3545            # else:
3546            #    ids_trip = []
3547
3548            gender = kwargs.get('gender', '').lower()
3549            if gender == 'm':
3550                # print '  m gender=*%s*'%gender
3551                id_gender = GENDERS['male']
3552            elif gender == 'f':
3553                # print '  f gender=*%s*'%gender
3554                id_gender = GENDERS['female']
3555            else:
3556                # print '  u gender=*%s*'%gender
3557                id_gender = GENDERS['unknown']
3558
3559            occupation = kwargs.get('occupation', '')
3560            occupation = occupation.lower()
3561            if occupation in ['None', '', 'unknown']:
3562                id_occupation = OCCUPATIONS['unknown']
3563            elif occupation in OCCUPATIONS:
3564                id_occupation = OCCUPATIONS[occupation]
3565            else:
3566                id_occupation = OCCUPATIONS['other']
3567
3568            id_pers = self.add_row(ids_sumo=id_sumo,
3569                                   ids_gender=id_gender,
3570                                   years_birth=kwargs.get('year_birth', None),
3571                                   ids_occupation=id_occupation,
3572                                   are_frequent_user=kwargs.get('is_frequent_user', None),
3573                                   zips=kwargs.get('zip', None),
3574                                   )
3575            if id_trip >= 0:
3576                self.ids_trips[id_pers] = [id_trip]
3577            else:
3578                self.ids_trips[id_pers] = []
3579
3580            # debug
3581            # print '  made id_pers',id_pers, self.ids_sumo[id_pers]
3582            #trips = self.parent.trips
3583            # for id_trip in self.ids_trips[id_pers]:
3584            #    print '    id_trip',id_trip,trips.ids_sumo[id_trip]
3585
3586            return id_pers
3587
3588
3589class Mapmatching(DemandobjMixin, cm.BaseObjman):
3590    def __init__(self, ident, demand=None,
3591                 name='Mapmatching', info='Mapmatching functionality.',
3592                 **kwargs):
3593
3594        self._init_objman(ident=ident, parent=demand,
3595                          name=name, info=info, **kwargs)
3596
3597        attrsman = self.set_attrsman(cm.Attrsman(self))
3598
3599        self._init_attributes()
3600        self._init_constants()
3601
3602    def get_scenario(self):
3603        return self.parent.parent
3604
3605    def clear_all(self):
3606        self.trips.clear()
3607        self.points.clear()
3608        self.persons.clear()
3609        self._init_constants()
3610
3611    def _init_attributes(self):
3612        print 'Mapmatching._init_attributes'
3613        attrsman = self.get_attrsman()
3614
3615        self.points = attrsman.add(cm.ObjConf(GpsPoints('points', self)))
3616        self.trips = attrsman.add(cm.ObjConf(GpsTrips('trips', self)))
3617        self.points.set_trips(self.trips)
3618        self.persons = attrsman.add(cm.ObjConf(GpsPersons('persons', self)))
3619
3620    def _init_constants(self):
3621        self._proj = None
3622        self._segvertices_xy = None
3623        self._distancesmap = None
3624        self._timesmap = None
3625        self._accesslevelsmap = None
3626
3627        attrsman = self.get_attrsman()
3628        attrsman.do_not_save_attrs(['_segvertices_xy', '_proj', '_distancesmap', '_timesmap', '_accesslevelsmap'])
3629
3630    def clear_routes(self):
3631        self.trips.clear_routes()
3632
3633    def delete_unselected_trips(self):
3634        trips = self.trips
3635        points = self.points
3636        persons = self.persons
3637
3638        if len(persons) == 0:
3639                # no persons, just trips
3640            ids_del = trips.select_ids(np.logical_not(trips.are_selected.get_value()))
3641
3642            for ids_point in trips.ids_points[ids_del]:
3643                if ids_point is not None:
3644                    points.del_rows(ids_point)
3645            trips.del_rows(ids_del)
3646        else:
3647            for ids_trip in persons.ids_trips[persons.get_ids()]:
3648                if ids_trip is not None:
3649                    ids_del = np.array(ids_trip, dtype=np.int32)[np.logical_not(trips.are_selected[ids_trip])]
3650                    for id_del, ids_point in zip(ids_del, trips.ids_points[ids_del]):
3651                        ids_trip.remove(id_del)
3652                        if ids_point is not None:
3653                            points.del_rows(ids_point)
3654
3655                    trips.del_rows(ids_del)
3656
3657    def get_proj_and_offset(self):
3658        if self._proj is None:
3659            net = self.get_scenario().net
3660            proj_params = str(net.get_projparams())
3661            # try:
3662            self._proj = pyproj.Proj(proj_params)
3663            # except:
3664            #    proj_params ="+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
3665            #    self._proj = pyproj.Proj(self.proj_params)
3666
3667            self._offset = net.get_offset()
3668
3669        return self._proj, self._offset
3670
3671    def get_segvertices_xy(self):
3672        if self._segvertices_xy is None:
3673            self._segvertices_xy = self.get_scenario().net.edges.get_segvertices_xy()
3674
3675        return self._segvertices_xy
3676
3677    def get_timesmap(self, is_check_lanes=False):
3678        """
3679        Returns a dictionary where key is id_mode and
3680        value is a distance-lookup table, mapping id_edge to edge distance
3681        """
3682        # print 'get_timesmap',self._distancesmap is None
3683
3684        if self._timesmap is None:
3685
3686            vtypes = self.get_scenario().demand.vtypes
3687            edges = self.get_scenario().net.edges
3688            ids_vtype = self.trips.ids_vtype[self.trips.get_ids_selected()]
3689            ids_mode = vtypes.ids_mode[ids_vtype]
3690            # print '    ids_mode',ids_mode
3691
3692            self._timesmap = {}
3693            for id_mode in set(ids_mode):
3694                #ids_vtype_mode = vtypes.select_by_mode(id_mode)
3695
3696                self._timesmap[id_mode] = edges.get_times(id_mode=id_mode,
3697                                                          is_check_lanes=is_check_lanes,
3698                                                          )
3699
3700        # print '  len(self._distancesmap)',len(self._distancesmap)
3701        return self._timesmap
3702
3703    def get_distancesmap(self, is_check_lanes=False):
3704        """
3705        Returns a dictionary where key is id_mode and
3706        value is a distance-lookup table, mapping id_edge to edge distance
3707        """
3708        # print 'get_distancesmap',self._distancesmap is None
3709
3710        if self._distancesmap is None:
3711
3712            vtypes = self.get_scenario().demand.vtypes
3713            edges = self.get_scenario().net.edges
3714            ids_vtype = self.trips.ids_vtype[self.trips.get_ids_selected()]
3715            ids_mode = vtypes.ids_mode[ids_vtype]
3716            # print '    ids_mode',ids_mode
3717
3718            self._distancesmap = {}
3719            for id_mode in set(ids_mode):
3720                #ids_vtype_mode = vtypes.select_by_mode(id_mode)
3721
3722                self._distancesmap[id_mode] = edges.get_distances(id_mode=id_mode,
3723                                                                  is_check_lanes=is_check_lanes,
3724                                                                  )
3725
3726        # print '  len(self._distancesmap)',len(self._distancesmap)
3727        return self._distancesmap
3728
3729    def get_accesslevelsmap(self):
3730        """
3731        Returns a dictionary where key is id_mode and
3732        value is a distance-lookup table, mapping id_edge to edge distance
3733        """
3734
3735        if self._accesslevelsmap is None:
3736            vtypes = self.get_scenario().demand.vtypes
3737            edges = self.get_scenario().net.edges
3738            ids_vtype = self.trips.ids_vtype[self.trips.get_ids_selected()]
3739            ids_mode = vtypes.ids_mode[ids_vtype]
3740
3741            self._accesslevelsmap = {}
3742            for id_mode in set(ids_mode):
3743                #ids_vtype_mode = vtypes.select_by_mode(id_mode)
3744                self._accesslevelsmap[id_mode] = edges.get_accesslevels(id_mode)
3745        return self._accesslevelsmap
3746
3747
3748class Matchresults(cm.BaseObjman):
3749    def __init__(self, ident, mapmatching,
3750                 name='Mapmatching results',
3751                 info='Results of mapmatching analysis.',
3752                 **kwargs):
3753
3754        # make results a child of process or of wxgui
3755        # use these objects to access matched trips
3756
3757        self._init_objman(ident, parent=mapmatching, name=name,
3758                          info=info, **kwargs)
3759        attrsman = self.set_attrsman(cm.Attrsman(self))
3760
3761        self._init_attributes()
3762
3763    def _init_attributes(self):
3764        attrsman = self.get_attrsman()
3765        mapmatching = self.parent
3766        # self.routesresults = attrsman.add(cm.ObjConf( Routesresults('routesresults',
3767        #                                                        self, mapmatching.trips.routes),
3768        #                                            groupnames = ['Route results'],
3769        #                                            ))
3770
3771        # add trip results from all demand objects
3772        # print 'Matchresults._init_attributes'
3773
3774    def config(self, resultobj, **kwargs):
3775        # attention: need to check whether already set
3776        # because setattr is set explicitely after add
3777        if not hasattr(self, resultobj.get_ident()):
3778            if kwargs.has_key('groupnames'):
3779                kwargs['groupnames'].append('Results')
3780            else:
3781                kwargs['groupnames'] = ['Results']
3782            attrsman = self.get_attrsman()
3783            attrsman.add(cm.ObjConf(resultobj, **kwargs))
3784            setattr(self, resultobj.get_ident(), resultobj)
3785
3786    def get_scenario(self):
3787        return self.parent.get_scenario()
3788
3789    def clear_all(self):
3790        self.clear()
3791
3792    def save(self, filepath=None, is_not_save_parent=True):
3793        if filepath is None:
3794            self.get_scenario().get_rootfilepath()+'.mmatch.obj'
3795        # parent will not be saved because in no_save set
3796        cm.save_obj(self, filepath, is_not_save_parent=is_not_save_parent)
3797
3798
3799class Nodesresults(am.ArrayObjman):
3800    def __init__(self, ident, parent,
3801                 name='Node results',
3802                 info='Table with data from matched traces on different node types.',
3803                 **kwargs):
3804
3805        self._init_objman(ident=ident,
3806                          parent=parent,  # main results object
3807                          info=info,
3808                          name=name,
3809                          **kwargs)
3810
3811        self.add_col(am.IdsArrayConf('ids_node', parent.get_scenario().net.nodes,
3812                                     groupnames=['state'],
3813                                     is_index=True,
3814                                     name='Node ID',
3815                                     info='ID of network node.',
3816                                     ))
3817
3818        # rdundant
3819        # self.add_col(am.ArrayConf('types', default = 0.0,
3820        #                            dtype = np.int32,
3821        #                            choices =  parent.get_scenario().net.nodes.types.choices.copy() ,
3822        #                            groupnames = ['results'],
3823        #                            name = 'Node type',
3824        #                            is_index = True,
3825        #                            info = 'Node type.',
3826        #                            ))
3827
3828        self._init_attributes()
3829
3830    def _init_attributes(self):
3831
3832        self.add_col(am.ArrayConf('times_wait', default=0.0,
3833                                  dtype=np.float32,
3834                                  groupnames=['results'],
3835                                  name='wait time',
3836                                  unit='s',
3837                                  info='Average wait times at this node.',
3838                                  ))
3839        self.add_col(am.ArrayConf('numbers_tot_matched', default=0,
3840                                  dtype=np.int32,
3841                                  groupnames=['results'],
3842                                  name='number matched',
3843                                  info='Total number of matched routes crossing this node.',
3844                                  ))
3845
3846    def get_nodes_significant(self, ids_node_raw=None,
3847                              n_lane_in_min=2, n_lane_out_min=2):
3848        """
3849        Returns an array with significant nodes where wait times
3850        can be expected.
3851        """
3852        nodes = self.ids_node.get_linktab()
3853        net = nodes.parent
3854        lanes = net.lanes
3855        edges = net.edges
3856        if ids_node_raw is None:
3857            ids_node_raw = nodes.get_ids()
3858
3859        ids_node = []
3860        id_mode_ped = net.modes.get_id_mode('pedestrian')
3861        id_ped_allow = [id_mode_ped]
3862        for id_node, type, ids_incoming, ids_outgoing, id_tls in zip(
3863                ids_node_raw,
3864                nodes.types[ids_node_raw],
3865                nodes.ids_incoming[ids_node_raw],
3866                nodes.ids_outgoing[ids_node_raw],
3867                nodes.ids_tls[ids_node_raw]):
3868
3869            if (ids_incoming is not None) & (ids_outgoing is not None):
3870                n_in = len(ids_incoming)
3871                n_out = len(ids_outgoing)
3872                for ids_lane in edges.ids_lanes[ids_incoming]:
3873                    if len(ids_lane) == 1:
3874                        if lanes.ids_modes_allow[ids_lane[0]] == id_ped_allow:
3875                            n_in -= 1
3876                for ids_lane in edges.ids_lanes[ids_outgoing]:
3877                    if len(ids_lane) == 1:
3878                        if lanes.ids_modes_allow[ids_lane[0]] == id_ped_allow:
3879                            n_out -= 1
3880                if (n_in >= n_lane_in_min) & (n_out >= n_lane_out_min):
3881                    ids_node.append(id_node)
3882        return ids_node
3883
3884    def get_times_wait_est(self):
3885        """
3886        Estimate wait times at all nodes.
3887        Returns an array times_wait_est such that
3888        the average wait tie at node id_node is
3889        times_wait_est[id_node]
3890        """
3891        print 'get_times_wait_est'
3892        nodes = self.ids_node.get_linktab()
3893        ids_node = nodes.get_ids()
3894        ids_node_signif = self.get_nodes_significant()
3895        #nodetypes = nodes.types[ids_node_signif]
3896        map_nodetype_to_times_wait = self.get_map_nodetype_to_times_wait()
3897
3898        print '  map_nodetype_to_times_wait', map_nodetype_to_times_wait
3899
3900        times_wait = np.zeros(max(ids_node)+1, dtype=np.int32)
3901        print '  ', np.min(nodes.types[ids_node_signif]), np.max(nodes.types[ids_node_signif])
3902
3903        times_wait[ids_node_signif] = map_nodetype_to_times_wait[nodes.types[ids_node_signif]]
3904
3905        # for nodetype, time_wait  in map_nodetype_to_times_wait.iteritems():
3906        #    times_wait[ids_node_signif[nodetypes == nodetype]] = time_wait
3907        return times_wait
3908
3909    def get_map_nodetype_to_times_wait(self):
3910        print 'get_map_nodetype_to_times_wait'
3911        nodes = self.ids_node.get_linktab()
3912        ids_res = self.get_ids()
3913        nodetypes = nodes.types[self.ids_node[ids_res]]
3914        nodetypeset = nodes.types.choices.values()
3915        #map_type_to_typename = get_inversemap(nodes.types.choices)
3916        #map_type_to_times_wait = {}
3917        map_type_to_times_wait = np.zeros(max(nodetypeset)+1, dtype=np.float32)
3918        for thistype in nodetypeset:
3919            inds_res = np.flatnonzero(nodetypes == thistype)
3920            if len(inds_res) > 0:
3921                map_type_to_times_wait[thistype] = np.mean(self.times_wait[ids_res[inds_res]])
3922
3923        return map_type_to_times_wait
3924
3925
3926class Edgesresults(am.ArrayObjman):
3927    def __init__(self, ident, parent,
3928                 name='Edges results',
3929                 info='Table with results from edges that are part of matched routes or alternative routes.',
3930                 **kwargs):
3931
3932        self._init_objman(ident=ident,
3933                          parent=parent,  # main results object
3934                          info=info,
3935                          name=name,
3936                          **kwargs)
3937
3938        # self.add(cm.AttrConf(  'datapathkey',datapathkey,
3939        #                        groupnames = ['_private'],
3940        #                        name = 'data pathkey',
3941        #                        info = "key of data path",
3942        #                        ))
3943
3944        self.add_col(am.IdsArrayConf('ids_edge', parent.get_scenario().net.edges,
3945                                     groupnames=['state'],
3946                                     is_index=True,
3947                                     name='Edge ID',
3948                                     info='ID of network edge.',
3949                                     ))
3950        self._init_attributes()
3951
3952    def _init_attributes(self):
3953
3954        self.add_col(am.ArrayConf('speeds_average', default=0.0,
3955                                  dtype=np.float32,
3956                                  groupnames=['results'],
3957                                  name='Avg. speed',
3958                                  unit='m/s',
3959                                  info='Average speed on this edge.',
3960                                  ))
3961
3962        self.add_col(am.ArrayConf('speeds_inmotion', default=0.0,
3963                                  dtype=np.float32,
3964                                  groupnames=['results'],
3965                                  name='Avg. speed in mot.',
3966                                  unit='m/s',
3967                                  info='Average speed in motion this edge.',
3968                                  ))
3969
3970        self.add_col(am.ArrayConf('durations_tot_matched', default=0.0,
3971                                  dtype=np.float32,
3972                                  groupnames=['results'],
3973                                  name='Tot. duration',
3974                                  unit='s',
3975                                  info='Total time of matched routes spent on this edge.',
3976                                  ))
3977
3978        self.add_col(am.ArrayConf('numbers_tot_matched', default=0,
3979                                  dtype=np.int32,
3980                                  groupnames=['results'],
3981                                  name='number matched',
3982                                  info='Total number of matched routes crossing this edge.',
3983                                  ))
3984
3985        self.add_col(am.ArrayConf('numbers_tot_shortest', default=0,
3986                                  dtype=np.int32,
3987                                  groupnames=['results'],
3988                                  name='number shortest',
3989                                  info='Total number of shortest routes crossing this edge.',
3990                                  ))
3991
3992        self.add_col(am.ArrayConf('numbers_tot_fastest', default=0,
3993                                  dtype=np.int32,
3994                                  groupnames=['results'],
3995                                  name='number fastest',
3996                                  info='Total number of fastest routes crossing this edge.',
3997                                  ))
3998
3999        self.add_col(am.ArrayConf('differences_dist_tot_shortest', default=0.0,
4000                                  dtype=np.float32,
4001                                  groupnames=['results'],
4002                                  name='Tot. short. length difference',
4003                                  unit='m',
4004                                  info='Sum of length differences between matched route length and the corrisponding shortest route using this edge.',
4005                                  ))
4006
4007        self.add_col(am.ArrayConf('differences_dist_tot_fastest', default=0.0,
4008                                  dtype=np.float32,
4009                                  groupnames=['results'],
4010                                  name='Tot. fast. length difference',
4011                                  unit='m',
4012                                  info='Sum of length differences between matched route length and the corrisponding fastest route using this edge.',
4013                                  ))
4014
4015        self.add_col(am.ArrayConf('probabilities_tot_matched', default=0.0,
4016                                  dtype=np.float32,
4017                                  groupnames=['results'],
4018                                  name='trip probab.',
4019                                  info='The probability that the edge has been used by the totality of all trips.',
4020                                  ))
4021
4022        self.add_col(am.ArrayConf('flows_est', default=0.0,
4023                                  dtype=np.float32,
4024                                  groupnames=['results'],
4025                                  name='Estim. Flows',
4026                                  unit='1/h',
4027                                  info='Estimated vehicle flows as a resukt of all trips.',
4028                                  ))
4029
4030        self.add_col(am.ArrayConf('times_wait', default=0.0,
4031                                  dtype=np.float32,
4032                                  groupnames=['results'],
4033                                  name='wait time',
4034                                  unit='s',
4035                                  info='Average  wait times per vehicle passed on this edge, including stops at junctions or TLS.',
4036                                  ))
4037
4038        self.add_col(am.ArrayConf('times_wait_junc', default=0.0,
4039                                  dtype=np.float32,
4040                                  groupnames=['results'],
4041                                  name='wait time at junction',
4042                                  unit='s',
4043                                  info='Average wait times per vehicle at junction at the end of this edge.',
4044                                  ))
4045
4046        self.add_col(am.ArrayConf('times_wait_tls', default=0.0,
4047                                  dtype=np.float32,
4048                                  groupnames=['results'],
4049                                  name='wait time at TLS',
4050                                  unit='s',
4051                                  info='Average wait times per vehicle at a traffic light systems at the end of this edge.',
4052                                  ))
4053
4054    def init_for_routes(self, routes):
4055        """
4056        Initializes a row for each edge ID in routes
4057        """
4058        ids_edge_init = set()
4059        for ids_edge in routes.ids_edges.get_value():
4060            if ids_edge is not None:
4061                ids_edge_init.update(ids_edge)
4062        ids_edge_init = list(ids_edge_init)
4063        # return result ids and respective edge ids
4064        ids_edgeresmap = np.zeros(np.max(ids_edge_init)+1, dtype=np.int32)
4065        ids_edgeresmap[ids_edge_init] = self.add_rows(n=len(ids_edge_init), ids_edge=ids_edge_init)
4066
4067        return ids_edgeresmap
4068
4069
4070class Routesresults(am.ArrayObjman):
4071    def __init__(self, ident, parent,
4072                 name='Route results',
4073                 info='Table with results from analysis of each matched route.',
4074                 **kwargs):
4075
4076        self._init_objman(ident=ident,
4077                          parent=parent,  # main results object
4078                          info=info,
4079                          name=name,
4080                          **kwargs)
4081
4082        # self.add(cm.AttrConf(  'datapathkey',datapathkey,
4083        #                        groupnames = ['_private'],
4084        #                        name = 'data pathkey',
4085        #                        info = "key of data path",
4086        #                        ))
4087
4088        self.add_col(am.IdsArrayConf('ids_route', parent.parent.trips.get_routes(),
4089                                     groupnames=['state'],
4090                                     is_index=True,
4091                                     name='ID route',
4092                                     info='ID of route.',
4093                                     ))
4094        self._init_attributes()
4095
4096    def _init_attributes(self):
4097
4098        self.add_col(am.ArrayConf('distances', default=-1.0,
4099                                  dtype=np.float32,
4100                                  groupnames=['results'],
4101                                  name='distance',
4102                                  unit='m',
4103                                  info='Length of the route.',
4104                                  ))
4105
4106        self.add_col(am.ArrayConf('durations', default=0.0,
4107                                  dtype=np.float32,
4108                                  groupnames=['results'],
4109                                  name='duration',
4110                                  unit='s',
4111                                  info='Time duration of the route.',
4112                                  ))
4113
4114        self.add_col(am.ArrayConf('lengths_mixed', default=-1.0,
4115                                  dtype=np.float32,
4116                                  groupnames=['results'],
4117                                  name='Length mixed access',
4118                                  symbol='L mix',
4119                                  unit='m',
4120                                  info='Length of roads with mixed access.',
4121                                  ))
4122
4123        self.add_col(am.ArrayConf('lengths_exclusive', default=-1.0,
4124                                  dtype=np.float32,
4125                                  groupnames=['results'],
4126                                  name='Length exclusive access',
4127                                  symbol='L excl',
4128                                  unit='m',
4129                                  info='Length of roads with exclusive access.',
4130                                  ))
4131
4132        self.add_col(am.ArrayConf('lengths_low_priority', default=-1.0,
4133                                  dtype=np.float32,
4134                                  groupnames=['results'],
4135                                  name='Length low priority',
4136                                  symbol='L lowprio',
4137                                  unit='m',
4138                                  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.',
4139                                  ))
4140
4141        self.add_col(am.ArrayConf('lengths_overlap_matched', default=-1.0,
4142                                  dtype=np.float32,
4143                                  groupnames=['results'],
4144                                  name='Length overlap',
4145                                  symbol='L overl',
4146                                  unit='m',
4147                                  info='Length of overlap with matched route.',
4148                                  ))
4149
4150        self.add_col(am.ArrayConf('numbers_nodes', default=-1,
4151                                  dtype=np.int32,
4152                                  groupnames=['results'],
4153                                  name='number of nodes',
4154                                  symbol='N nodes',
4155                                  info='Total number of nodes.',
4156                                  ))
4157
4158        self.add_col(am.ArrayConf('numbers_nodes_tls', default=-1,
4159                                  dtype=np.int32,
4160                                  groupnames=['results'],
4161                                  name='number of TL nodes',
4162                                  symbol='N TL',
4163                                  info='Total number of traffic light controlled nodes.',
4164                                  ))
4165
4166        self.add_col(am.ArrayConf('numbers_prioritychange', default=-1,
4167                                  dtype=np.int32,
4168                                  groupnames=['results'],
4169                                  name='number of prio. change',
4170                                  symbol='N prio',
4171                                  info='Total number of change in road priority.',
4172                                  ))
4173
4174        self.add_col(am.ArrayConf('times_inmotion', default=0.0,
4175                                  dtype=np.float32,
4176                                  groupnames=['results'],
4177                                  name='Time in motion',
4178                                  unit='s',
4179                                  info='Times while moving during the matched trip.',
4180                                  ))
4181
4182        self.add_col(am.ArrayConf('times_wait', default=0.0,
4183                                  dtype=np.float32,
4184                                  groupnames=['results'],
4185                                  name='wait time',
4186                                  unit='s',
4187                                  info='Wait times or times of low speed during the matched trip.',
4188                                  ))
4189
4190        self.add_col(am.ArrayConf('times_wait_tls', default=0.0,
4191                                  dtype=np.float32,
4192                                  groupnames=['results'],
4193                                  name='wait time at TLS',
4194                                  unit='s',
4195                                  info='Wait times or times of low speed at traffic lights during the matched trip.',
4196                                  ))
4197
4198        self.add_col(am.ListArrayConf('pointsspeeds',
4199                                      groupnames=['_private'],
4200                                      perm='rw',
4201                                      name='Point speeds',
4202                                      unit='m/s',
4203                                      info='List of velocities associated with each matched GPS  of this route.',
4204                                      ))
4205
4206        self.add_col(am.ListArrayConf('pointspositions',
4207                                      groupnames=['_private'],
4208                                      perm='rw',
4209                                      name='Point positions',
4210                                      unit='m',
4211                                      info='List of point positions on edge associated with each matched GPS point of this route.',
4212                                      ))
4213
4214        self.add_col(am.ListArrayConf('pointstimes',
4215                                      groupnames=['_private'],
4216                                      perm='rw',
4217                                      name='Point times',
4218                                      unit='s',
4219                                      info='List of time points associated with each matched GPS point of this route. First point in routs has zero seconds.',
4220                                      ))
4221
4222        self.add_col(am.IdlistsArrayConf('ids_pointedges', self.parent.get_scenario().net.edges,
4223                                         groupnames=['results', '_private'],
4224                                         name='Point edge IDs',
4225                                         info="This is a list edge IDs corrisponding to each matched GPS point of this route.",
4226                                         ))
4227
4228    def get_speeds_inmotion(self, ids_routeres):
4229        return self.distances[ids_routeres]/self.times_inmotion[ids_routeres]
4230
4231    def get_speeds_inmotion_est(self):
4232        """
4233        Estimate speeds in motion.
4234        Returns an array speeds_inmotion_est such that
4235        the average speed in motion of trip id_trip is
4236        speeds_inmotion_est[id_trip]
4237        """
4238        routes = self.ids_route.get_linktab()
4239
4240        ids_routeres = self.get_ids()
4241        ids_trip = routes.ids_trip[self.ids_route[ids_routeres]]
4242
4243        speeds_inmotion_est = np.zeros(max(ids_trip)+1, dtype=np.float32)
4244
4245        #ids_trip_matched = self.ids_trip[ids_tripres]
4246        speeds_inmotion_est[ids_trip] = self.get_speeds_inmotion(ids_routeres)
4247        return speeds_inmotion_est
4248
4249
4250class Shortestrouter(Process):
4251    def __init__(self, ident, mapmatching,  logger=None, **kwargs):
4252        print 'Shortestrouter.__init__'
4253
4254        # TODO: let this be independent, link to it or child??
4255
4256        self._init_common(ident,
4257                          parent=mapmatching,
4258                          name='Shortest Router',
4259                          logger=logger,
4260                          info='Shortest path router.',
4261                          )
4262
4263        attrsman = self.set_attrsman(cm.Attrsman(self))
4264
4265        self.c_modespecific = attrsman.add(cm.AttrConf('c_modespecific', kwargs.get('c_modespecific', 0.9),
4266                                                       groupnames=['options'],
4267                                                       perm='rw',
4268                                                       name='Mode constant',
4269                                                       info='The Mode constant is multiplied with the the distance of each edge if vehicle has exclusive access.',
4270                                                       ))
4271
4272        self.dist_modespecific = attrsman.add(cm.AttrConf('dist_modespecific', kwargs.get('dist_modespecific', 0.0),
4273                                                          groupnames=['options'],
4274                                                          perm='rw',
4275                                                          name='Mode dist',
4276                                                          unit='m',
4277                                                          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.',
4278                                                          ))
4279
4280        self.dist_min_modespecific = attrsman.add(cm.AttrConf('dist_min_modespecific', kwargs.get('dist_min_modespecific', 15.0),
4281                                                              groupnames=['options'],
4282                                                              perm='rw',
4283                                                              name='Mode dist min',
4284                                                              unit='m',
4285                                                              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.',
4286                                                              ))
4287
4288        self.is_ignor_connections = attrsman.add(cm.AttrConf('is_ignor_connections', kwargs.get('is_ignor_connections', True),
4289                                                             groupnames=['options'],
4290                                                             perm='rw',
4291                                                             name='Ignore connections',
4292                                                             info='Ignore connections means that the vehicle can always make turns into all possible edges, whether they are allowed or not.',
4293                                                             ))
4294        self.color_route = attrsman.add(cm.AttrConf('color_route', kwargs.get('color_route', COLOR_SHORTEST_ROUTE.copy()),
4295                                                    groupnames=['options'],
4296                                                    perm='wr',
4297                                                    metatype='color',
4298                                                    name='Route color',
4299                                                    info='Color of matched routes used in various visualization tools.',
4300                                                    ))
4301
4302    def do(self):
4303        print 'Shortestrouter.do'
4304        # links
4305        mapmatching = self.parent
4306        trips = mapmatching.trips
4307        trips.route_shortest(dist_modespecific=self.dist_modespecific,
4308                             dist_min_modespecific=self.dist_min_modespecific,
4309                             c_modespecific=self.c_modespecific,
4310                             is_ignor_connections=self.is_ignor_connections,
4311                             color_route=self.color_route,
4312                             )
4313        return True
4314
4315
4316class Fastestrouter(Process):
4317    def __init__(self, ident, mapmatching,  logger=None, matchresults=None, **kwargs):
4318        print 'Fastestrouter.__init__'
4319
4320        # TODO: let this be independent, link to it or child??
4321
4322        self._init_common(ident,
4323                          parent=mapmatching,
4324                          name='Fastest Router',
4325                          logger=logger,
4326                          info='Fastest path router.',
4327                          )
4328
4329        attrsman = self.set_attrsman(cm.Attrsman(self))
4330
4331        self.matchresults = matchresults
4332
4333        self.c_modespecific = attrsman.add(cm.AttrConf('c_modespecific', kwargs.get('c_modespecific', 0.9),
4334                                                       groupnames=['options'],
4335                                                       perm='rw',
4336                                                       name='Mode constant',
4337                                                       info='The Mode constant is multiplied with the the distance of each edge if vehicle has exclusive access.',
4338                                                       ))
4339
4340        self.time_modespecific = attrsman.add(cm.AttrConf('time_modespecific', kwargs.get('time_modespecific', 0.0),
4341                                                          groupnames=['options'],
4342                                                          perm='rw',
4343                                                          name='Mode time',
4344                                                          unit='s',
4345                                                          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.',
4346                                                          ))
4347
4348        self.dist_min_modespecific = attrsman.add(cm.AttrConf('dist_min_modespecific', kwargs.get('dist_min_modespecific', 15.0),
4349                                                              groupnames=['options'],
4350                                                              perm='rw',
4351                                                              name='Mode dist min',
4352                                                              unit='m',
4353                                                              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.',
4354                                                              ))
4355
4356        self.is_ignor_connections = attrsman.add(cm.AttrConf('is_ignor_connections', kwargs.get('is_ignor_connections', True),
4357                                                             groupnames=['options'],
4358                                                             perm='rw',
4359                                                             name='Ignore connections',
4360                                                             info='Ignore connections means that the vehicle can always make turns into all possible edges, whether they are allowed or not.',
4361                                                             ))
4362        self.color_route = attrsman.add(cm.AttrConf('color_route', kwargs.get('color_route', COLOR_FASTEST_ROUTE.copy()),
4363                                                    groupnames=['options'],
4364                                                    perm='wr',
4365                                                    metatype='color',
4366                                                    name='Route color',
4367                                                    info='Color of fastest routes used in various visualization tools.',
4368                                                    ))
4369
4370        if (self.matchresults is not None):
4371            if hasattr(self.matchresults, 'nodesresults'):
4372                self.is_use_nodeswaittimes_est = attrsman.add(cm.AttrConf('is_use_nodeswaittimes_est', kwargs.get('is_use_nodeswaittimes_est', True),
4373                                                                          groupnames=['options'],
4374                                                                          perm='rw',
4375                                                                          name='Use estimated waittime at nodes',
4376                                                                          info='If True, the average waittime at different node types are used.',
4377                                                                          #is_enabled = lambda self: self.nodesresults is not None,
4378                                                                          ))
4379            else:
4380                self.is_use_nodeswaittimes_est = False
4381        else:
4382            self.is_use_nodeswaittimes_est = False
4383
4384    def do(self):
4385        print 'Shortestrouter.do'
4386        # links
4387        mapmatching = self.parent
4388        trips = mapmatching.trips
4389        # map_nodetype_to_times_wait = get_map_nodetype_to_times_wait
4390        if self.is_use_nodeswaittimes_est & (self.matchresults is not None):
4391            trips.route_fastest_with_waits(time_modespecific=self.time_modespecific,
4392                                           c_modespecific=self.c_modespecific,
4393                                           is_ignor_connections=self.is_ignor_connections,
4394                                           times_wait_nodes=self.matchresults.nodesresults.get_times_wait_est(),
4395                                           speeds_in_motion=self.matchresults.routesresults_matched.get_speeds_inmotion_est(),
4396                                           dist_min_modespecific=self.dist_min_modespecific,
4397                                           color_route=self.color_route,
4398                                           )
4399        else:
4400            trips.route_fastest(time_modespecific=self.time_modespecific,
4401                                c_modespecific=self.c_modespecific,
4402                                is_ignor_connections=self.is_ignor_connections,
4403                                color_route=self.color_route,
4404                                )
4405        return True
4406
4407
4408class Routesanalyzer(Process):
4409    def __init__(self, ident, mapmatching, results=None,  logger=None, **kwargs):
4410        print 'Routesanalyzer.__init__'
4411
4412        # TODO: let this be independent, link to it or child??
4413
4414        if results is None:
4415            self._results = Matchresults('matchresults', mapmatching)
4416        else:
4417            self._results = results
4418
4419        self._init_common(ident,
4420                          parent=mapmatching,
4421                          name='Routes Analyzer',
4422                          logger=logger,
4423                          info='Determines attributes of matched route and alternative routes.',
4424                          )
4425
4426        attrsman = self.set_attrsman(cm.Attrsman(self))
4427
4428        self.priority_max_low = attrsman.add(cm.AttrConf('priority_max_low', kwargs.get('priority_max_low', 6),
4429                                                         groupnames=['options'],
4430                                                         perm='rw',
4431                                                         name='Max. low priority',
4432                                                         info='Maximum priority of an edge, such that it is still considered low priority.',
4433                                                         ))
4434
4435        self.timeloss_intersection = attrsman.add(cm.AttrConf('timeloss_intersection', kwargs.get('timeloss_intersection', 5.0),
4436                                                              groupnames=['options'],
4437                                                              perm='rw',
4438                                                              name='Timeloss intersection',
4439                                                              info='Estimated timeloss at intersections due to waiting or reduction of speed. This value is used to estimate travel time of route alternatives.',
4440                                                              ))
4441
4442        self.timeloss_tl = attrsman.add(cm.AttrConf('timeloss_tl', kwargs.get('timeloss_tl', 15.0),
4443                                                    groupnames=['options'],
4444                                                    perm='rw',
4445                                                    name='Timeloss TL',
4446                                                    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.',
4447                                                    ))
4448
4449        self.edgedurations_tot_min = attrsman.add(cm.AttrConf('edgedurations_tot_min', kwargs.get('edgedurations_tot_min', 10.0),
4450                                                              groupnames=['options'],
4451                                                              perm='rw',
4452                                                              name='min tot. edge duration',
4453                                                              unit='s',
4454                                                              info='Minimum total duration of all users in an edge in order to calculate average speed. A too small value distors the results.',
4455                                                              ))
4456
4457        self.is_speedana = attrsman.add(cm.AttrConf('is_speedana', kwargs.get('is_speedana', False),
4458                                                    groupnames=['options'],
4459                                                    perm='rw',
4460                                                    name='Speed analysis?',
4461                                                    info='If True, speed analysis will be performed (can take some time and use a lot of memory).',
4462                                                    ))
4463
4464        self.edgespeed_max = attrsman.add(cm.AttrConf('edgespeed_max', kwargs.get('edgespeed_max', 14.0),
4465                                                      groupnames=['options'],
4466                                                      perm='rw',
4467                                                      name='Max edge speed',
4468                                                      unit='m/s',
4469                                                      info='Edge speed estimates will be clipped at this speed.',
4470                                                      ))
4471
4472        self.waitspeed = attrsman.add(cm.AttrConf('waitspeed', kwargs.get('waitspeed', 0.7),
4473                                                  groupnames=['options'],
4474                                                  perm='rw',
4475                                                  name='Wait speed',
4476                                                  unit='m/s',
4477                                                  info='Below this speed, the vehicle is considered waiting.',
4478                                                  ))
4479
4480        self.junctionrange = attrsman.add(cm.AttrConf('junctionrange', kwargs.get('junctionrange', 10.0),
4481                                                      groupnames=['options'],
4482                                                      perm='rw',
4483                                                      name='Junction range',
4484                                                      unit='m',
4485                                                      info='This is the range after an edge for in which waiting times are considered as part of the edge.',
4486                                                      ))
4487
4488        self.is_nodeana = attrsman.add(cm.AttrConf('is_nodeana', kwargs.get('is_nodeana', False),
4489                                                   groupnames=['options'],
4490                                                   perm='rw',
4491                                                   name='Node analysis?',
4492                                                   info='If True, Node analysis will be performed. This is mainly the calculation of the average waiting time at different types of junctions.',
4493                                                   ))
4494
4495        self.flowfactor = attrsman.add(cm.AttrConf('flowfactor', kwargs.get('flowfactor', 10.0),
4496                                                   groupnames=['options'],
4497                                                   perm='rw',
4498                                                   name='Flow factor',
4499                                                   info='This is the factor which will be multiplied with the edge probability to estimate the absolute vehicle flows.',
4500                                                   ))
4501
4502        self.is_oldversion = attrsman.add(cm.AttrConf('is_oldversion', kwargs.get('is_oldversion', False),
4503                                                      groupnames=['options'],
4504                                                      perm='rw',
4505                                                      name='Old mapmatch?',
4506                                                      info='If True, analyze for old mapmatch version.',
4507                                                      ))
4508
4509        results = self.get_results()
4510        results.config(Routesresults('routesresults_matched', results,
4511                                     name='Results of matched routes'), name='Results of matched routes')
4512        results.config(Routesresults('routesresults_shortest', results,
4513                                     name='Results of shortest routes'), name='Results of shortest routes')
4514        results.config(Routesresults('routesresults_fastest', results,
4515                                     name='Results of fastest routes'), name='Results of fastest routes')
4516
4517        results.config(Routesresults('routesresults_matched_nonoverlap', results, name='Results of matched, non-overlapping routes'),
4518                       name='Results of matched routes using only edges which do not overlap with the shortest route.')
4519        results.config(Routesresults('routesresults_shortest_nonoverlap', results, name='Results of shortest, non-overlapping routes'),
4520                       name='Results of shortest routes using only edges which do not overlap with the matched route.')
4521
4522        results.config(Edgesresults('edgesresults', results))
4523        results.config(Nodesresults('nodesresults', results))
4524
4525    def get_results(self):
4526        return self._results
4527
4528    def do(self):
4529        print 'Routesanalyzer.do'
4530        # results
4531        results = self.get_results()
4532        routesresults_shortest = results.routesresults_shortest
4533        routesresults_fastest = results.routesresults_fastest
4534        routesresults_matched = results.routesresults_matched
4535
4536        routesresults_shortest_nonoverlap = results.routesresults_shortest_nonoverlap
4537        routesresults_matched_nonoverlap = results.routesresults_matched_nonoverlap
4538
4539        edgesresults = results.edgesresults
4540        nodesresults = results.nodesresults
4541
4542        # links
4543        mapmatching = self.parent
4544        trips = mapmatching.trips
4545
4546        routes = trips.get_routes()
4547        scenario = mapmatching.get_scenario()
4548        edges = scenario.net.edges
4549        lanes = scenario.net.lanes
4550        nodes = scenario.net.nodes
4551        id_mode_ped = scenario.net.modes.get_id_mode('pedestrian')
4552        get_pos_from_coord = edges.get_pos_from_coord
4553
4554        vtypes = scenario.demand.vtypes
4555        points = mapmatching.points
4556        timestamps = points.timestamps
4557
4558        priority_max_low = self.priority_max_low
4559        timeloss_intersection = self.timeloss_intersection
4560        timeloss_tl = self.timeloss_tl
4561
4562        distancesmap = mapmatching.get_distancesmap()
4563        accesslevelsmap = mapmatching.get_accesslevelsmap()
4564        priorities = edges.priorities
4565        ids_tonode = edges.ids_tonode
4566        nodetypes = scenario.net.nodes.types
4567        tlstype = nodetypes.choices['traffic_light']
4568        ids_tls = scenario.net.nodes.ids_tls
4569
4570        ids_trip_sel = trips.get_ids_selected()
4571
4572        ids_route_sel = trips.ids_route_matched[ids_trip_sel]
4573        inds_valid = np.flatnonzero(ids_route_sel > 0)
4574
4575        ids_route = ids_route_sel[inds_valid]
4576        ids_trip = ids_trip_sel[inds_valid]
4577        ids_route_shortest = trips.ids_route_shortest[ids_trip]
4578        ids_route_fastest = trips.ids_route_fastest[ids_trip]
4579
4580        ids_vtype = trips.ids_vtype[ids_trip]
4581        ids_mode = vtypes.ids_mode[ids_vtype]
4582
4583        #ind = 0
4584
4585        print '  analyzing %d trips' % len(ids_trip)
4586
4587        routesresults_shortest.clear()
4588        routesresults_matched.clear()
4589        routesresults_fastest.clear()
4590        routesresults_shortest_nonoverlap.clear()
4591        routesresults_matched_nonoverlap.clear()
4592
4593        edgesresults.clear()
4594        ids_res = routesresults_matched.add_rows(n=len(ids_trip))
4595        routesresults_shortest.add_rows(ids=ids_res)
4596        routesresults_fastest.add_rows(ids=ids_res)
4597        routesresults_matched_nonoverlap.add_rows(ids=ids_res)
4598        routesresults_shortest_nonoverlap.add_rows(ids=ids_res)
4599
4600        ids_edgeresmap = edgesresults.init_for_routes(routes)
4601
4602        for id_trip, id_route_matched, id_route_shortest, id_route_fastest, id_mode, id_res, dist_gps, duration_gps\
4603                in zip(ids_trip,
4604                       ids_route,
4605                       ids_route_shortest,
4606                       ids_route_fastest,
4607                       ids_mode,
4608                       ids_res,
4609                       trips.lengths_gpsroute_matched[ids_trip],
4610                       trips.durations_route_matched[ids_trip]):
4611
4612            #ids_point = trips.ids_points[id_trip]
4613            distances = distancesmap[id_mode]
4614
4615            # here we cut of first edge for analysis
4616            ids_edge_matched = np.array(routes.ids_edges[id_route_matched][1:], dtype=np.int32)
4617
4618            if self.is_oldversion:
4619                ids_points_edgeend = np.array(trips.ids_points_edgeend[id_trip], dtype=np.int32)  # [1:]
4620            else:
4621                ids_points_edgeend = np.array(trips.ids_points_edgeend[id_trip][1:], dtype=np.int32)
4622
4623            ids_points = np.array(trips.ids_points[id_trip], dtype=np.int32)
4624            #
4625            times = points.timestamps[ids_points]
4626            coords = points.coords[ids_points]
4627            accesslevels_matched = accesslevelsmap[id_mode][ids_edge_matched]
4628            priorities_matched = priorities[ids_edge_matched]
4629            nodetypes_matched = nodetypes[ids_tonode[ids_edge_matched]]
4630
4631            print '  analyzing id_trip', id_trip
4632            # print '    len(ids_edge_matched)',len(ids_edge_matched)
4633            # print '    len(ids_points_edgeend)',len(ids_points_edgeend)
4634            # print '    ids_edge_matched',ids_edge_matched
4635            # print '    accesslevels_matched',accesslevels_matched
4636            # print '    tlstype',tlstype
4637            # print '    accesslevels_mixed',accesslevels_matched==1
4638            # print '    accesslevels_mixed',np.flatnonzero(accesslevels_matched==1)
4639            # print '    distances',
4640            # print '    nodetypes_matched',nodetypes_matched
4641            if len(ids_points_edgeend) > 1:
4642                dist_matched = np.sum(distances[ids_edge_matched])
4643                duration_matched = timestamps[ids_points_edgeend[-1]] - timestamps[ids_points_edgeend[0]]
4644                n_nodes_matched = len(nodetypes_matched)
4645                n_tls_matched = np.sum(nodetypes_matched == tlstype)
4646                lineduration_matched = duration_matched-n_nodes_matched*timeloss_intersection-n_tls_matched*timeloss_tl
4647                linespeed_matched = dist_matched/lineduration_matched
4648
4649            else:
4650                dist_matched = 0.0
4651                duration_matched = 0.0
4652                n_nodes_matched = 0
4653                n_tls_matched = 0
4654                lineduration_matched = 0.0
4655                linespeed_matched = 0.0
4656
4657            # do edge by edge analyses if at least more than 1 edge
4658            n_matched = len(ids_edge_matched)
4659            if n_matched > 1:
4660
4661                # cut off last edge
4662
4663                print '  ids_edge_matched', ids_edge_matched.shape, ids_edge_matched
4664                print '  ids_points_edgeend', ids_points_edgeend.shape, ids_points_edgeend
4665                n_matched -= 1
4666                ids_edge = ids_edge_matched[:-1]
4667                edgelengths = edges.lengths[ids_edge]
4668                ids_points_ee = ids_points_edgeend
4669
4670                print '    ids_edge', ids_edge.shape, n_matched, ids_edge
4671                print '    ids_points_ee', ids_points_ee.shape, ids_points_ee
4672
4673                ids_tls = nodes.ids_tls[edges.ids_tonode[ids_edge]]
4674
4675                durations_edge = timestamps[ids_points_ee[1:]] - timestamps[ids_points_ee[:-1]]
4676
4677                # print '  durations_edge',durations_edge.shape,durations_edge
4678                edgesresults.numbers_tot_matched[ids_edgeresmap[ids_edge]] += 1
4679                edgesresults.durations_tot_matched[ids_edgeresmap[ids_edge]] += durations_edge
4680                #dists_edge = distances[ids_edge]
4681
4682                # identify gps points with  id_point_from, id_point_to
4683                # and compute line speed, waiting time....
4684                pointsposition = []
4685                pointsspeed = []
4686                pointstime = []
4687                ids_pointedge = []
4688
4689                if self.is_speedana | self.is_nodeana:  # nodes ana needs speeds ana
4690
4691                    time_inmotion_route = 0.0
4692                    time_wait_route = 0.0
4693                    time_wait_tls_route = 0.0
4694                    time_wait_junction_route = 0.0
4695
4696                    ind_matched = 0
4697                    id_edge_current = -1
4698                    id_tls_current = -1
4699                    time_enter = 0.0
4700                    location_enter = 0.0
4701                    time_speedaverage = 0.0
4702                    location_speedaverage = 0.0
4703                    time_speedinmotion = 0.0
4704                    location_speedinmotion = 0.0
4705                    time_wait = 0.0
4706                    time_wait_tls = 0.0
4707                    n_points = len(ids_points)
4708                    dists = np.zeros(n_points, dtype=np.float32)
4709                    dists[1:] = np.sqrt(np.sum((coords[1:]-coords[:-1])**2, 1))
4710                    loctions = np.cumsum(dists)
4711                    deltatimes = np.zeros(n_points, dtype=np.float32)
4712                    deltatimes[1:] = times[1:]-times[:-1]
4713                    speeds = np.clip(dists/deltatimes, 0, self.edgespeed_max)
4714
4715                    #speeds_edge_av = np.zeros(n_matched, dtype = np.float32)
4716                    print '    dists', dists
4717                    print '    deltatimes', deltatimes
4718                    print '    speeds', speeds
4719                    is_begin = False
4720                    is_waiting = False
4721                    id_longedge = -1
4722                    id_tlsedge = -1
4723                    time_start = times[0]
4724                    i_point = 0
4725
4726                    # there are basically two loops:
4727                    # the outer for loop rind through all GPS points
4728                    # and the inner while loop increases the
4729                    # edge counter until the point???
4730                    for id_point, time, coord, location, dist, deltatime, speed in zip(ids_points, times, coords, loctions, dists, deltatimes, speeds):
4731
4732                        # if id_point == ids_points_ee[ind_matched]:
4733                        #    is_begin = True
4734                        #    # try to find last edge with this point as end point
4735
4736                        if is_begin:
4737                            pos = get_pos_from_coord(id_edge_current, coord)
4738                            print '      id_point=%d,id_end %d id_edge_current=%d, id_edgeres=%d, pos= %.2f, v=%.2f' % (
4739                                id_point, ids_points_ee[ind_matched], id_edge_current, id_edgeres_current, pos, speed)
4740
4741                            pointsposition.append(pos)
4742                            pointsspeed.append(speed)
4743                            pointstime.append(time-time_start)
4744                            ids_pointedge.append(id_edge_current)
4745
4746                            if speed < self.waitspeed:
4747                                print '      waiting detected add', deltatime
4748
4749                                is_waiting = True
4750                                edgesresults.times_wait[id_edgeres_current] += deltatime
4751                                time_wait += deltatime
4752                                time_wait_tls += deltatime
4753                                # is waiting at initial part of edge
4754                                if pos < self.junctionrange:
4755                                    # halt is at junction at initial part of an edge
4756                                    # add wait to last longedge
4757                                    edgesresults.times_wait_junc[id_longedgeres] += deltatime
4758                                    time_wait_junction_route += deltatime
4759
4760                                elif pos > edgelength_current-self.junctionrange:
4761                                    # halt is at junction at the end of current edge
4762                                    edgesresults.times_wait_junc[id_edgeres_current] += deltatime
4763                                    time_wait_junction_route += deltatime
4764
4765                            else:
4766                                is_waiting = False
4767
4768                        # while loop through points of an edge until ede ends
4769                        is_cont = True
4770                        while is_cont:
4771
4772                            # is current point a last point of edge ind_matched
4773                            is_cont = id_point == ids_points_ee[ind_matched]
4774                            print '        endpoint id_point =%d, id_startpoint%d ind_matched=%d,n_matched=%d' % (
4775                                id_point, ids_points_ee[ind_matched], ind_matched, n_matched), is_cont
4776                            if is_cont:
4777                                # this is the first point in a new edge
4778                                # TODO: it should actually the last point in an edge
4779
4780                                # save previous results
4781                                # edgesresults.speeds_average[ids_edgeresmap[ids_edge]]
4782
4783                                #edgesresults.times_wait[ids_edgeres] = time_wait
4784
4785                                # update edge-values
4786
4787                                if ind_matched < n_matched:
4788                                    # edge is not the last matched edge
4789                                    if is_begin:
4790                                        #
4791                                        # this part is avoided for the first time
4792                                        # only from secon time and after is_begin is True
4793                                        #
4794                                        # do speed and average speed on right edge
4795                                        v_av = (location - location_speedaverage)/(time - time_speedaverage)
4796                                        edgesresults.speeds_average[id_edgeres_current] += v_av
4797                                        v_inmotion = np.clip((location - location_speedaverage) /
4798                                                             (time - time_speedaverage-time_wait), 0, self.edgespeed_max)
4799
4800                                        edgesresults.speeds_inmotion[id_edgeres_current] += v_inmotion
4801
4802                                        print '      id_edge = %d, v_av =%.2fkm/h, v_im = %.2fkm/h' % (
4803                                            id_edge_current, v_av*3.6, v_inmotion*3.6)
4804
4805                                        #lengths_inmotion_route += location- location_speedaverage
4806                                        time_inmotion_route += time - time_speedaverage-time_wait
4807                                        time_wait_route += time_wait
4808
4809                                        time_wait = 0.0
4810
4811                                    if time - time_speedaverage > 10.0:
4812                                        # time passed since enering last edge is
4813                                        # sufficient to do averaging
4814                                        time_speedaverage = time
4815                                        location_speedaverage = location
4816
4817                                    id_edge_current = ids_edge[ind_matched]
4818                                    id_edgeres_current = ids_edgeresmap[id_edge_current]
4819                                    edgelength_current = edgelengths[ind_matched]
4820
4821                                    if (ids_tls[ind_matched] != id_tls_current):
4822                                        # tls changed
4823
4824                                        if id_tlsedge != -1:
4825                                            # there has been an old tls recording
4826                                            edgesresults.times_wait_tls[id_tlsedge] += time_wait_tls
4827                                            time_wait_tls_route += time_wait_tls
4828
4829                                        if (ids_tls[ind_matched] != -1):
4830                                            # memorize new edge, which is used to
4831                                            # accumulate wait time of entire tls crossing
4832                                            id_tlsedge = id_edgeres_current
4833                                        else:
4834                                            # memorize no tls wait time accumulation
4835                                            id_tlsedge = -1
4836
4837                                        # memorize current tls ID (or -1 for no tls)
4838                                        id_tls_current = ids_tls[ind_matched]
4839
4840                                        # reset tls wait counter
4841                                        time_wait_tls = 0.0
4842
4843                                    time_enter = time
4844                                    location_enter = location
4845
4846                                    if (edgelength_current > self.junctionrange) | (id_longedge == -1):
4847                                        # new longedge of at least a junction range has been detected
4848                                        id_longedge = id_edge_current
4849                                        id_longedgeres = ids_edgeresmap[id_longedge]
4850                                        location_longedge = location
4851                                        length_longedge = edgelength_current
4852
4853                                    # get next matched edge
4854                                    ind_matched += 1
4855
4856                                else:
4857                                    print '      reached end of matched edges',
4858                                    is_cont = False
4859
4860                                    # update speed average for last visited edge
4861                                    v_av = (location - location_speedaverage)/(time - time_speedaverage)
4862                                    edgesresults.speeds_average[id_edgeres_current] += v_av
4863                                    v_inmotion = np.clip((location - location_speedaverage) /
4864                                                         (time - time_speedaverage-time_wait), 0, self.edgespeed_max)
4865
4866                                    edgesresults.speeds_inmotion[id_edgeres_current] += v_inmotion
4867
4868                                    print '      last id_edge = %d, v_av =%.2fkm/h, v_im = %.2fkm/h' % (
4869                                        id_edge_current, v_av*3.6, v_inmotion*3.6), time - time_speedaverage, time_wait, time - time_speedaverage-time_wait
4870
4871                                    #lengths_inmotion_route += location- location_speedaverage
4872                                    time_inmotion_route += time - time_speedaverage-time_wait
4873                                    time_wait_route += time_wait
4874                                    time_wait = 0.0
4875
4876                                    # save wait times in tls is necessary
4877                                    if id_tlsedge != -1:
4878                                        # there has been an old tls recording
4879                                        edgesresults.times_wait_tls[id_tlsedge] += time_wait_tls
4880                                        time_wait_tls_route += time_wait_tls
4881
4882                                    # this will trigger the final break for loop
4883                                    ind_matched += 1
4884
4885                                if not is_begin:
4886                                    is_begin = True
4887                                    time_start = time
4888
4889                        if ind_matched == n_matched+1:
4890                            print '    speed ana reached end. Break for loop'
4891                            break
4892
4893                    # print '  save results id_res',id_res
4894                    # print '  pointsposition',pointsposition
4895                    # print '  pointsspeed',pointsspeed
4896                    # print '  pointstime',pointstime
4897                    # print '  ids_pointedge',ids_pointedge
4898
4899                    routesresults_matched.times_wait[id_res] = time_wait_route
4900                    routesresults_matched.times_wait_tls[id_res] = time_wait_tls_route
4901                    routesresults_matched.times_inmotion[id_res] = time_inmotion_route
4902
4903                    routesresults_matched.pointspositions[id_res] = pointsposition
4904                    routesresults_matched.pointsspeeds[id_res] = pointsspeed
4905                    routesresults_matched.pointstimes[id_res] = pointstime
4906                    routesresults_matched.ids_pointedges[id_res] = ids_pointedge
4907
4908            # else:
4909            #    # no speedanalysis
4910            #    pointsposition = None
4911            #    pointsspeed  = None
4912            #    pointstime = None
4913            #    ids_pointedge = None
4914
4915            # print '  BEFORE:'
4916            # print '  routesresults_matched.ids_route',routesresults_matched.ids_route.get_value()
4917            # print '  routesresults_matched.ids_pointedges',routesresults_matched.ids_pointedges.get_value()
4918            # print '  routesresults_matched.pointspositions',routesresults_matched.pointspositions.get_value()
4919            routesresults_matched.set_row(id_res,
4920                                          ids_route=id_route_matched,
4921                                          distances=dist_matched,
4922                                          durations=duration_matched,
4923                                          lengths_mixed=np.sum(distances[ids_edge_matched[accesslevels_matched == 1]]),
4924                                          lengths_exclusive=np.sum(
4925                                              distances[ids_edge_matched[accesslevels_matched == 2]]),
4926                                          lengths_low_priority=np.sum(
4927                                              distances[ids_edge_matched[priorities_matched <= priority_max_low]]),
4928                                          lengths_overlap_matched=np.sum(distances[ids_edge_matched]),
4929                                          numbers_nodes=n_nodes_matched,
4930                                          numbers_nodes_tls=n_tls_matched,
4931                                          numbers_prioritychange=np.sum(np.logical_not(
4932                                              priorities_matched[:-1], priorities_matched[1:])),
4933                                          #pointspositions = pointsposition,
4934                                          #pointsspeeds = pointsspeed,
4935                                          #pointstimes = pointstime,
4936                                          #ids_pointedges = ids_pointedge,
4937                                          )
4938
4939            # print '  AFTER:'
4940            # print '  routesresults_matched.ids_route',routesresults_matched.ids_route.get_value()
4941            # print '  routesresults_matched.ids_pointedges',routesresults_matched.ids_pointedges.get_value()
4942            # print '  routesresults_matched.pointsposition',routesresults_matched.pointspositions.get_value()
4943
4944            if id_route_shortest >= 0:
4945                ids_edge_shortest = np.array(routes.ids_edges[id_route_shortest], dtype=np.int32)[1:]
4946                accesslevels_shortest = accesslevelsmap[id_mode][ids_edge_shortest]
4947                priorities_shortest = priorities[ids_edge_shortest]
4948                nodetypes_shortest = nodetypes[ids_tonode[ids_edge_shortest]]
4949
4950                ids_edge_overlap = list(set(ids_edge_matched).intersection(ids_edge_shortest))
4951                ids_edge_shortest_nonoverlap = np.array(
4952                    list(set(ids_edge_shortest).difference(ids_edge_matched)), dtype=np.int32)
4953                ids_edge_match_nonoverlap = np.array(
4954                    list(set(ids_edge_matched).difference(ids_edge_shortest)), dtype=np.int32)
4955                dist_shortest = np.sum(distances[ids_edge_shortest])
4956                n_nodes_shortest = len(nodetypes_shortest)
4957                n_tls_shortest = np.sum(nodetypes_shortest == tlstype)
4958
4959                edgesresults.numbers_tot_shortest[ids_edgeresmap[ids_edge_shortest]] += 1
4960                edgesresults.differences_dist_tot_shortest[ids_edgeresmap[ids_edge_shortest_nonoverlap]
4961                                                           ] += dist_matched-dist_shortest
4962
4963                routesresults_shortest.set_row(id_res,
4964                                               ids_route=id_route_shortest,
4965                                               distances=dist_shortest,
4966                                               durations=dist_shortest/linespeed_matched+timeloss_intersection * n_nodes_shortest + timeloss_tl * n_tls_shortest,
4967                                               lengths_mixed=np.sum(
4968                                                   distances[ids_edge_shortest[accesslevels_shortest == 1]]),
4969                                               lengths_exclusive=np.sum(
4970                                                   distances[ids_edge_shortest[accesslevels_shortest == 2]]),
4971                                               lengths_low_priority=np.sum(
4972                                                   distances[ids_edge_shortest[priorities_shortest <= priority_max_low]]),
4973                                               numbers_nodes=n_nodes_shortest,
4974                                               numbers_nodes_tls=n_tls_shortest,
4975                                               numbers_prioritychange=np.sum(np.logical_not(
4976                                                   priorities_shortest[:-1], priorities_shortest[1:])),
4977                                               lengths_overlap_matched=np.sum(distances[ids_edge_overlap]),
4978                                               )
4979
4980                accesslevels_nonoverlap = accesslevelsmap[id_mode][ids_edge_match_nonoverlap]
4981                # print '  ids_edge_match_nonoverlap',ids_edge_match_nonoverlap
4982                # print '  accesslevels_nonoverlap==1',np.flatnonzero(accesslevels_nonoverlap==1),np.flatnonzero(accesslevels_nonoverlap==1).dtype
4983                # print '  ids_edge_match_nonoverlap[accesslevels_nonoverlap==1]',ids_edge_match_nonoverlap[np.flatnonzero(accesslevels_nonoverlap==1)]
4984                priorities_nonoverlap = priorities[ids_edge_match_nonoverlap]
4985                nodetypes_nonoverlap = nodetypes[ids_tonode[ids_edge_match_nonoverlap]]
4986
4987                dist_nonoverlap = np.sum(distances[ids_edge_match_nonoverlap])
4988                n_nodes_nonoverlap = len(nodetypes_nonoverlap)
4989                n_tls_nonoverlap = np.sum(nodetypes_nonoverlap == tlstype)
4990
4991                routesresults_matched_nonoverlap.set_row(id_res,
4992                                                         ids_route=id_route_matched,
4993                                                         distances=dist_nonoverlap,
4994                                                         durations=dist_nonoverlap/linespeed_matched+timeloss_intersection * n_nodes_nonoverlap + timeloss_tl * n_tls_nonoverlap,
4995                                                         lengths_mixed=np.sum(
4996                                                             distances[ids_edge_match_nonoverlap[accesslevels_nonoverlap == 1]]),
4997                                                         lengths_exclusive=np.sum(
4998                                                             distances[ids_edge_match_nonoverlap[accesslevels_nonoverlap == 2]]),
4999                                                         lengths_low_priority=np.sum(
5000                                                             distances[ids_edge_match_nonoverlap[priorities_nonoverlap <= priority_max_low]]),
5001                                                         lengths_overlap_matched=np.sum(
5002                                                             distances[ids_edge_match_nonoverlap]),
5003                                                         numbers_nodes=n_nodes_nonoverlap,
5004                                                         numbers_prioritychange=np.sum(np.logical_not(
5005                                                             priorities_nonoverlap[:-1], priorities_nonoverlap[1:])),
5006                                                         numbers_nodes_tls=n_tls_nonoverlap,
5007                                                         )
5008
5009                accesslevels_nonoverlap = accesslevelsmap[id_mode][ids_edge_shortest_nonoverlap]
5010                priorities_nonoverlap = priorities[ids_edge_shortest_nonoverlap]
5011                nodetypes_nonoverlap = nodetypes[ids_tonode[ids_edge_shortest_nonoverlap]]
5012
5013                dist_nonoverlap = np.sum(distances[ids_edge_shortest_nonoverlap])
5014                n_nodes_nonoverlap = len(nodetypes_nonoverlap)
5015                n_tls_nonoverlap = np.sum(nodetypes_nonoverlap == tlstype)
5016
5017                routesresults_shortest_nonoverlap.set_row(id_res,
5018                                                          ids_route=id_route_matched,
5019                                                          distances=dist_nonoverlap,
5020                                                          durations=dist_nonoverlap/linespeed_matched+timeloss_intersection * n_nodes_nonoverlap + timeloss_tl * n_tls_nonoverlap,
5021                                                          lengths_mixed=np.sum(
5022                                                              distances[ids_edge_shortest_nonoverlap[accesslevels_nonoverlap == 1]]),
5023                                                          lengths_exclusive=np.sum(
5024                                                              distances[ids_edge_shortest_nonoverlap[accesslevels_nonoverlap == 2]]),
5025                                                          lengths_low_priority=np.sum(
5026                                                              distances[ids_edge_shortest_nonoverlap[priorities_nonoverlap <= priority_max_low]]),
5027                                                          lengths_overlap_matched=np.sum(
5028                                                              distances[ids_edge_shortest_nonoverlap]),
5029                                                          numbers_nodes=n_nodes_nonoverlap,
5030                                                          numbers_nodes_tls=n_tls_nonoverlap,
5031                                                          numbers_prioritychange=np.sum(np.logical_not(
5032                                                              priorities_nonoverlap[:-1], priorities_nonoverlap[1:])),
5033                                                          )
5034            # print '  analyzing id_trip',id_trip,'Done.'
5035
5036        if id_route_fastest >= 0:
5037            ids_edge_fastest = np.array(routes.ids_edges[id_route_fastest], dtype=np.int32)[1:]
5038            accesslevels_fastest = accesslevelsmap[id_mode][ids_edge_fastest]
5039            priorities_fastest = priorities[ids_edge_fastest]
5040            nodetypes_fastest = nodetypes[ids_tonode[ids_edge_fastest]]
5041
5042            # overlap analyses
5043            ids_edge_overlap = list(set(ids_edge_matched).intersection(ids_edge_fastest))
5044            ids_edge_fastest_nonoverlap = np.array(
5045                list(set(ids_edge_fastest).difference(ids_edge_matched)), dtype=np.int32)
5046            ids_edge_match_nonoverlap = np.array(
5047                list(set(ids_edge_matched).difference(ids_edge_fastest)), dtype=np.int32)
5048
5049            dist_fastest = trips.lengths_route_fastest[id_trip]  # np.sum(distances[ids_edge_fastest])
5050            n_nodes_fastest = len(nodetypes_fastest)
5051            n_tls_fastest = np.sum(nodetypes_fastest == tlstype)
5052
5053            edgesresults.numbers_tot_fastest[ids_edgeresmap[ids_edge_fastest]] += 1
5054            edgesresults.differences_dist_tot_fastest[ids_edgeresmap[ids_edge_fastest_nonoverlap]
5055                                                      ] += dist_matched-dist_fastest
5056
5057            routesresults_fastest.set_row(id_res,
5058                                          ids_route=id_route_fastest,
5059                                          distances=dist_fastest,
5060                                          durations=trips.durations_route_fastest[id_trip],
5061                                          lengths_mixed=np.sum(distances[ids_edge_fastest[accesslevels_fastest == 1]]),
5062                                          lengths_exclusive=np.sum(
5063                                              distances[ids_edge_fastest[accesslevels_fastest == 2]]),
5064                                          lengths_low_priority=np.sum(
5065                                              distances[ids_edge_fastest[priorities_fastest <= priority_max_low]]),
5066                                          numbers_nodes=n_nodes_fastest,
5067                                          numbers_nodes_tls=n_tls_fastest,
5068                                          numbers_prioritychange=np.sum(np.logical_not(
5069                                              priorities_fastest[:-1], priorities_fastest[1:])),
5070                                          lengths_overlap_matched=np.sum(distances[ids_edge_overlap]),
5071                                          )
5072
5073        # do mass edge result operations
5074        #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)
5075
5076        ids_valid = edgesresults.select_ids(edgesresults.durations_tot_matched.get_value() > self.edgedurations_tot_min)
5077
5078        edgesresults.probabilities_tot_matched[ids_valid] = edgesresults.numbers_tot_matched[ids_valid] / \
5079            float(len(ids_valid))
5080        edgesresults.flows_est[ids_valid] = self.flowfactor * edgesresults.numbers_tot_matched[ids_valid]
5081        ids_valid = edgesresults.select_ids(edgesresults.numbers_tot_matched.get_value() > 0)
5082
5083        edgesresults.speeds_average[ids_valid] = edgesresults.speeds_average[ids_valid] / \
5084            edgesresults.numbers_tot_matched[ids_valid]
5085        edgesresults.speeds_inmotion[ids_valid] = edgesresults.speeds_inmotion[ids_valid] / \
5086            edgesresults.numbers_tot_matched[ids_valid]
5087
5088        edgesresults.times_wait[ids_valid] = edgesresults.times_wait[ids_valid] / \
5089            edgesresults.numbers_tot_matched[ids_valid]
5090        edgesresults.times_wait_tls[ids_valid] = edgesresults.times_wait_tls[ids_valid] / \
5091            edgesresults.numbers_tot_matched[ids_valid]
5092        edgesresults.times_wait_junc[ids_valid] = edgesresults.times_wait_junc[ids_valid] / \
5093            edgesresults.numbers_tot_matched[ids_valid]
5094
5095        # print '  ids_valid',ids_valid
5096
5097        if self.is_nodeana:
5098            # do node type analyses
5099
5100            # extract the set of nodes that are involved in the mapmatching
5101            ids_edge = edgesresults.ids_edge[ids_valid]
5102            #ids_node_raw = np.array(list(set(edges.ids_tonode[ids_edge])), dtype = np.int32)
5103            ids_node_raw = list(set(edges.ids_tonode[ids_edge]))
5104
5105            print '  identified nodes:', ids_node_raw
5106            # for id_node_raw in ids_node_raw:
5107            #    print '    id_node_raw',id_node_raw
5108            #
5109            #map_id_node_to_type = {}
5110            ids_node = nodesresults.get_nodes_significant(ids_node_raw)
5111
5112            ids_noderes = nodesresults.add_rows(n=len(ids_node), ids_node=ids_node)
5113            print '  nodesresults.ids_node', nodesresults.ids_node.get_value()
5114            get_id_nodesres = nodesresults.ids_node.get_id_from_index
5115            has_id_node = nodesresults.ids_node.has_index
5116            for id_nodes_to, nodetype, time_wait, time_wait_tls in zip(
5117                edges.ids_tonode[ids_edge],
5118                nodes.types[edges.ids_tonode[ids_edge]],
5119                edgesresults.times_wait_junc[ids_valid],
5120                edgesresults.times_wait_tls[ids_valid],
5121            ):
5122                print '    id_nodes_to', id_nodes_to, nodetype
5123                if has_id_node(id_nodes_to):
5124                    id_nodseres = get_id_nodesres(id_nodes_to)
5125
5126                    if nodetype == 1:  # traffic_light
5127                        nodesresults.times_wait[id_nodseres] += time_wait_tls
5128                    else:
5129                        nodesresults.times_wait[id_nodseres] += time_wait
5130                    nodesresults.numbers_tot_matched[id_nodseres] += 1
5131
5132            nodesresults.times_wait[ids_noderes] = nodesresults.times_wait[ids_noderes] / \
5133                nodesresults.numbers_tot_matched[ids_noderes]
5134        print '  Route analyses done.'
5135        return True
5136
5137
5138def routes_to_shapefile(mapmatching, results, filepath,
5139                        dataname='routeresshapedata', name='Route result shape data',
5140                        log=None):
5141    """
5142    Writes routes to file.
5143    """
5144    print '\n routes_to_shapefile for mapmatching'
5145    net = mapmatching.get_scenario().net
5146    edges = net.edges
5147    trips = mapmatching.trips
5148    routes = trips.get_routes()
5149    shapedata = shapeformat.Shapedata(trips, dataname,
5150                                      filepath=filepath,
5151                                      name=name,
5152                                      projparams_shape=net.get_projparams(),
5153                                      offset=net.get_offset(),
5154                                      shapetype=3,  # Polygons
5155                                      log=log,
5156                                      )
5157
5158    fieldlength_default = 32
5159    attrlist = [
5160        ('id', 'id', 'ID_TRIP', 'N', fieldlength_default, 0),
5161        ('timestamps', '', 'TIMESTAMP', 'C', fieldlength_default, 0),
5162        ('durations_gps', '', 'DUR_GPS', 'N', fieldlength_default, 0),
5163        ('lengths_route_matched', '', 'LEN_MATCH', 'N', fieldlength_default, 3),
5164        ('lengths_route_shortest', '', 'LEN_SHORT', 'N', fieldlength_default, 3),
5165        ('lengths_route_matched_mixed', '', 'LEN_MIX', 'N', fieldlength_default, 3),
5166        ('lengths_route_matched_exclusive', '', 'LEN_EXCL', 'N', fieldlength_default, 3),
5167        ('lengthindexes', '', 'LENGTHIND', 'N', fieldlength_default, 0),
5168        ('errors_dist', '', 'ERR_DIST', 'N', fieldlength_default, 0),
5169        ('times_computation', '', 'TIME_COMP', 'N', fieldlength_default, 0),
5170        # ('','','','N',fieldlength_default,3),
5171    ]
5172
5173    for attr in attrlist:
5174        shapedata.add_field(attr[2:])
5175
5176    ids_trip = trips.select_ids(trips.ids_route_matched.get_value() > -1)
5177
5178    ids_shape = shapedata.add_rows(len(ids_trip))
5179    # print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
5180    shapedata.ID_TRIP[ids_shape] = ids_trip
5181
5182    for id_shape, id_trip, ids_edge in zip(ids_shape, ids_trip, routes.ids_edges[trips.ids_route_matched[ids_trip]]):
5183        # print '  export id_route',id_route
5184        #route = traces.routes.get(id_route)
5185        if len(ids_edge) > 0:
5186            polyline = []
5187            for shape in edges.shapes[ids_edge]:
5188                # print '    shape',type(shape),shape
5189                # print '    polyline',type(polyline),polyline
5190                polyline += shape.tolist()
5191
5192            shapedata.shapes[id_shape] = polyline
5193
5194    for netattrname, gettype, shapeattrname, x1, x2, x3 in attrlist:
5195        if netattrname not in ('id', 'timestamps'):
5196            getattr(shapedata, shapeattrname)[ids_shape] = getattr(trips, netattrname)[ids_trip]
5197
5198    for id_shape,  timestamp in zip(ids_shape, trips.timestamps[ids_trip]):
5199        shapedata.TIMESTAMP[id_shape] = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime(timestamp))
5200
5201    shapedata.adjust_fieldlength()
5202    shapedata.export_shapefile()
5203    return True
5204
5205
5206def edgesresults_to_shapefile(mapmatching, results, filepath,
5207                              dataname='edgeresshapedata', name='Edge result shape data',
5208                              log=None):
5209    """
5210    Writes edge results of mapmatching to shape-file.
5211    """
5212    print '\n edgesresults_to_shapefile for mapmatching'
5213    net = mapmatching.get_scenario().net
5214    edges = net.edges
5215    edgesresults = results.edgesresults
5216    #trips = mapmatching.trips
5217    #routes = trips.get_routes()
5218    shapedata = shapeformat.Shapedata(edgesresults, dataname,
5219                                      filepath=filepath,
5220                                      name=name,
5221                                      projparams_shape=net.get_projparams(),
5222                                      offset=net.get_offset(),
5223                                      shapetype=3,  # Polygons
5224                                      log=log,
5225                                      )
5226
5227    fieldlength_default = 32
5228    attrlist = [
5229        ('id', 'id', 'ID_ARC', 'N', fieldlength_default, 0),
5230        ('speeds_average', '', 'SPEED_AV', 'N', fieldlength_default, 5),
5231        ('speeds_inmotion', '', 'SPEED_MOV', 'N', fieldlength_default, 5),
5232        ('durations_tot_matched', '', 'TIME_TOT', 'N', fieldlength_default, 0),
5233        ('numbers_tot_matched', '', 'COUNTER', 'N', fieldlength_default, 0),
5234        ('times_wait', '', 'TIME_WAIT', 'N', fieldlength_default, 5),
5235        # ('','','','N',fieldlength_default,3),
5236    ]
5237
5238    for attr in attrlist:
5239        shapedata.add_field(attr[2:])
5240
5241    ids_edgeres = edgesresults.select_ids(edgesresults.numbers_tot_matched.get_value() > 0)
5242    ids_edge = edgesresults.ids_edge[ids_edgeres]
5243
5244    ids_shape = shapedata.add_rows(len(ids_edge))
5245    # print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
5246    shapedata.ID_ARC[ids_shape] = ids_edge
5247    shapedata.shapes[ids_shape] = edges.shapes[ids_edge]
5248
5249    for netattrname, gettype, shapeattrname, x1, x2, x3 in attrlist:
5250        if netattrname not in ('id',):
5251            getattr(shapedata, shapeattrname)[ids_shape] = getattr(edgesresults, netattrname)[ids_edgeres]
5252
5253    shapedata.adjust_fieldlength()
5254    shapedata.export_shapefile()
5255    return True
5256
5257
5258def points_to_shapefile(mapmatching, filepath, dataname='pointshapedata',
5259                        parent=None, log=None):
5260    """
5261    Export GPS points to shapefile.
5262    """
5263    net = mapmatching.get_scenario().net
5264    points = mapmatching.points
5265    trips = mapmatching.trips
5266    shapedata = shapeformat.Shapedata(parent, dataname, name='GPS points shape data',
5267                                      filepath=filepath,
5268                                      shapetype=shapeformat.SHAPETYPES['Point'],
5269                                      projparams_shape=net.get_projparams(),
5270                                      offset=net.get_offset(), log=log)
5271
5272    #attrname, ftype, flen, fdigit = field
5273    attrlist = [
5274        ('id', 'id', 'ID_POINT', 'N', 12, 0),
5275        ('timestamps', 'val', 'TIME', 'N', 32, 0),
5276        ('ids_trip', 'id', 'ID_TRIP', 'N', 32, 0),
5277        # ('radii','val','RADIUS','N',5,3),
5278    ]
5279    #
5280
5281    print 'nodes_to_shapefile', filepath
5282
5283    for attr in attrlist:
5284        shapedata.add_field(attr[2:])
5285
5286    ids_point = points.select_ids(trips.ids_route_matched[points.ids_trip.get_value()] > -1)
5287
5288    ids_shape = shapedata.add_rows(len(ids_point))
5289    # print '  shapedata.ID_ARC',shapedata.ID_ARC,'dir',dir(shapedata.ID_ARC)
5290    shapedata.ID_POINT[ids_shape] = ids_point
5291    shapedata.coords[ids_shape] = points.coords[ids_point]
5292
5293    # copy rest of attributes
5294    for netattrname, gettype, shapeattrname, x1, x2, x3 in attrlist:
5295        if netattrname not in ('id',):
5296            getattr(shapedata, shapeattrname)[ids_shape] = getattr(points, netattrname)[ids_point]
5297
5298    shapedata.adjust_fieldlength()
5299    shapedata.export_shapefile()
5300    return True
5301