Extraction of trip travel time informationΒΆ

In the bus-stop detection section, we have applied the Density-Based Spatial Clustering of Applications with Noise (DBSCAN) based clustering algorithm to detect the bus-stops on a route. Now for developing the arrival time predictor scheme, we will need the travel time information of a bus at different bus-stops or junctions / crossroads. These travel time information will be used in the subsequent unit to built the arrival time predictor based on historical bus trajectories.

[1]:
'''Imports'''
from pymongo import MongoClient

import os
import sys
import pprint
import pandas as pd
sys.path.append("/".join(os.getcwd().split('/')) +'/Codes/LibCodes')

'''Import project specific library'''
import Preprocessing

'''Initialize MongoClient'''
con = MongoClient()

RouteName='Git_ISCON_PDPU'

We begin by extracting the bus-stops and junction or crossroads on a route for both the direction i.e. from ISCON to PDPU (North bound) and PDPU to ISCON (South bound).

[2]:
'''BusStops, use of ProcessStatus collection and record: BusStops:True'''
BusStopsListNorth = [BusStop for BusStop in con[RouteName]['BusStops.NorthBound'].find().sort([('id',1)])]
#New Addition for Dist_th
BusStopsListSouth = [BusStop for BusStop in con[RouteName]['BusStops.SouthBound'].find().sort([('id',1)])]
Dist_TH = 50

Now, to compute travel time we compare the filtered location records with three consecutive bus-stops on a route. Because, we have observed that if the location record corresponding to a particular bus-stop is missing due to GPS outage, then the travel time extraction module would get stuck waiting for the location record corresponding to the bus-stop location. In order to cater with these types of an occasional GPS outage, we compare the location records with three consecutive bus-stop. If the distance between the bus-stop location and location record is less than \(D_{th}\) meters (\(50 m\)), then the travel time extraction module marks the corresponding location record of the bus as the record at a bus-stop.

We need to emphasize that the id of bus-stop increases as the bus moves during its trip in the case of north bound whereas in the case of south bound the id of bus-stop decreases as the bus moves during its trip. Let us print the BusStopsListNorth and BusStopsListSouth to observe this point.

[3]:
pprint.pprint([(BusStop['Name'],BusStop['id']) for BusStop in BusStopsListSouth])
[('ISCON', 0),
 ('Pakwaan', 1),
 ('Gurudwara', 2),
 ('Thaltej', 3),
 ('Zydus', 4),
 ('Kargil', 5),
 ('Sola', 6),
 ('Gota', 7),
 ('Vaishnodevi', 8),
 ('Khoraj', 9),
 ('Adalaj-uvarsad', 10),
 ('Sargasan', 11),
 ('RakshaShakti', 12),
 ('Bhaijipura', 13),
 ('PDPU', 14)]
[4]:
pprint.pprint(
    [(BusStop['Name'],BusStop['id']) for BusStop in con[RouteName]['BusStops.SouthBound'].find().sort([('id',-1)])])
[('PDPU', 14),
 ('Bhaijipura', 13),
 ('RakshaShakti', 12),
 ('Sargasan', 11),
 ('Adalaj-uvarsad', 10),
 ('Khoraj', 9),
 ('Vaishnodevi', 8),
 ('Gota', 7),
 ('Sola', 6),
 ('Kargil', 5),
 ('Zydus', 4),
 ('Thaltej', 3),
 ('Gurudwara', 2),
 ('Pakwaan', 1),
 ('ISCON', 0)]

Therefore, we have formulated two functions separately for North bound and South bound to compute travel time estimates. One must emphasize on the condition and index using for North bound and South bound.

For north bound in function ExtractTimeStampNorthBound,

if (BusStopIndex+j) < BusStopsCount:
'''and'''
BusStopsListNorth[BusStopIndex+j],

and for south bound in function ExtractTimeStampSouthBound,

if BusStopsCount-BusStopIndex-1-j >=0:
'''and'''
BusStopsListSouth[BusStopsCount-BusStopIndex-1-j]
[5]:
def ExtractTimeStampNorthBound(LocationRecords, BusStopsListNorth, Dist_TH):
    '''
    input: Location records of the trip, bus-stop list, and distance threshold
    output: The dictionary of location records corresponding to bus
    function: Compares the location records of the trip with three consecutive
              bus-stop and if distance is less than Dist_TH then marks the corresponding
              record as a location record at a bus-stop.
    '''
    BusStopsTimeStampList = []
    BusStopIndex = 0
    LocationRecordsCount = len (LocationRecords)
    BusStopsCount = len (BusStopsListNorth)

    for i in range(0, LocationRecordsCount):
        for j in range(0,3):
            if (BusStopIndex+j) < BusStopsCount:
                DistanceFromStop = Preprocessing.mydistance(LocationRecords[i]['Latitude'],
                                              LocationRecords[i]['Longitude'],
                                              BusStopsListNorth[BusStopIndex+j]['Location'][0],
                                              BusStopsListNorth[BusStopIndex+j]['Location'][1])

                if DistanceFromStop < Dist_TH:
                    BusStopDict = {}
                    BusStopIndex += j
                    BusStopDict['id'] = BusStopIndex
                    BusStopDict['epoch'] = LocationRecords[i]['epoch']
                    BusStopDict['Latitude'] = LocationRecords[i]['Latitude']
                    BusStopDict['Longitude'] = LocationRecords[i]['Longitude']
                    BusStopDict['Name'] = BusStopsListNorth[BusStopIndex]['Name']
                    BusStopsTimeStampList.append(BusStopDict)
                    BusStopIndex +=1
                    break

        if BusStopIndex == BusStopsCount:
            break
    return(BusStopsTimeStampList)
[6]:
def ExtractTimeStampSouthBound(LocationRecords, BusStopsListSouth):
    '''
    input: Location records of the trip, bus-stop list, and distance threshold
    output: The dictionary of location records corresponding to bus
    function: Compares the location records of the trip with three consecutive
              bus-stop and if distance is less than Dist_TH then marks the corresponding
              record as a location record at a bus-stop.
    '''
    BusStopsTimeStampList = []
    BusStopIndex = 0
    LocationRecordsCount = len (LocationRecords)
    BusStopsCount = len (BusStopsListSouth)

    for i in range(0, LocationRecordsCount):
        for j in range(0,3):
            if BusStopsCount-BusStopIndex-1-j >=0:
                DistanceFromStop = Preprocessing.mydistance(LocationRecords[i]['Latitude'],
                                              LocationRecords[i]['Longitude'],
                                              BusStopsListSouth[BusStopsCount-BusStopIndex-1-j]['Location'][0],
                                              BusStopsListSouth[BusStopsCount-BusStopIndex-1-j]['Location'][1])
                if DistanceFromStop < Dist_TH:
                    BusStopIndex +=j
                    BusStopDict = {}
                    BusStopDict['id'] = BusStopsCount-BusStopIndex-1
                    BusStopDict['epoch'] = LocationRecords[i]['epoch']
                    BusStopDict['Latitude'] = LocationRecords[i]['Latitude']
                    BusStopDict['Longitude'] = LocationRecords[i]['Longitude']
                    BusStopDict['Name'] = BusStopsListNorth[BusStopIndex]['Name']
                    BusStopsTimeStampList.append(BusStopDict)
                    BusStopIndex +=1
                    break
        if BusStopIndex == BusStopsCount:
            break
    return(BusStopsTimeStampList)

We will update the travel time of a trip in the MongoDB with the collection name dd_mm_yyyy__hh_mm_ss.BusStopsRecord in the function addTravelTimeInformationToMongoDB. Additionally, we update the BusStopRecordExtracted flag of a trip to True in the TripInfo collection. It would be used to retrieve only those trips for which the travel time information related to bus-stop is extracted. Furthermore, one should observe the update of TripStartTimeAggregate collection.

'''Create collection to store the trip aggregate information'''
con [RouteName]['TripStartTimeAggregate'].update_one({},{'$addToSet':
                                                         {'TripStartTimeBound':
                                                          (TripInfoList[0]['TripStartHour'], Bound)}},True)

TripStartTimeAggregate maintains the starting time of all the trips on a particular bound using the tuple (TripStartHour, Bound).

[7]:
def addTravelTimeInformationToMongoDB(SingleTripInfo, BusStopsTimeStampList, Bound):
    '''
    input: Trip name, bus-stop location record and bound
    output: void
    function: Stores the bus-stop location record in the MongoDB database with collection name
              SingleTripInfo.BusStopsRecord. It also updates the flag Bound and BusStopRecordExtracted
              in TripInfo collection. Further, the function updates the TripStartTimeAggregate to
              maintains the starting time of all the trips on a particular bound using the tuple
              (TripStartHour, Bound).
    '''
    TripInfoList = [Trip for Trip in
                    con[RouteName]['TripInfo'].find({'SingleTripInfo':SingleTripInfo}).limit(1)]

    '''If travel time record of trip is not available'''
    if len(BusStopsTimeStampList) == 0:
        con [RouteName]['TripInfo'].update_one({'SingleTripInfo':SingleTripInfo},
                                               {'$set':{'Bound': Bound, 'BusStopRecordExtracted':False}})
    else:

        '''Drop if any previous records are stored in MongoDB collection'''
        con [RouteName].drop_collection(SingleTripInfo+'.BusStopsRecord')
        con [RouteName][SingleTripInfo+'.BusStopsRecord'].insert_many(BusStopsTimeStampList)
        con [RouteName]['TripInfo'].update_one({'SingleTripInfo':SingleTripInfo},
                                               {'$set':{'Bound': Bound, 'BusStopRecordExtracted':True}})

        '''Create collection to store the trip aggregate information'''
        con [RouteName]['TripStartTimeAggregate'].update_one({},{'$addToSet':
                                                                 {'TripStartTimeBound':
                                                                  (TripInfoList[0]['TripStartHour'], Bound)}},True)

Now, given that we have built the required functions for travel time information, we can execute it for the trips on North bound and South bound.

[8]:
'''For Morning trips'''
SingleTripsInfoNorthBound = [rec['SingleTripInfo'] for rec in con[RouteName]['TripInfo'].find({'$and':
                                                                   [ {'filteredLocationRecord':True},
                                                                    {'TripStartHour':'07'} ] })]

for SingleTripInfo in SingleTripsInfoNorthBound:
    print('Extracting travel time for trip: '+ SingleTripInfo)
    LocationRecords = [LocationRecord for LocationRecord in
                       con[RouteName][SingleTripInfo+'.Filtered'].find().sort([('epoch',1)])]
    BusStopsTimeStampList = ExtractTimeStampNorthBound(LocationRecords, BusStopsListNorth, Dist_TH)

    addTravelTimeInformationToMongoDB(SingleTripInfo, BusStopsTimeStampList, 'North')
Extracting travel time for trip: 29_01_2018__07_39_47
Extracting travel time for trip: 30_01_2018__07_42_30
Extracting travel time for trip: 01_02_2018__07_39_12
Extracting travel time for trip: 02_02_2018__07_38_50
Extracting travel time for trip: 18_01_2018__07_38_10
Extracting travel time for trip: 19_01_2018__07_38_47
Extracting travel time for trip: 22_01_2018__07_41_04
Extracting travel time for trip: 22_12_2017__07_38_21
Extracting travel time for trip: 26_12_2017__07_32_35
Extracting travel time for trip: 20_12_2017__07_38_14
Extracting travel time for trip: 21_12_2017__07_52_59
Extracting travel time for trip: 08_01_2018__07_41_43
Extracting travel time for trip: 09_01_2018__07_40_01
Extracting travel time for trip: 27_12_2017__07_55_48
Extracting travel time for trip: 29_12_2017__07_37_27
Extracting travel time for trip: 01_01_2018__07_38_27
Extracting travel time for trip: 12_02_2018__07_40_14
Extracting travel time for trip: 15_02_2018__07_45_52
Extracting travel time for trip: 16_02_2018__07_45_41
Extracting travel time for trip: 19_02_2018__07_46_19
Extracting travel time for trip: 20_02_2018__07_41_48
Extracting travel time for trip: 21_02_2018__07_42_42
Extracting travel time for trip: 13_03_2018__07_29_52
Extracting travel time for trip: 14_03_2018__07_35_46
Extracting travel time for trip: 20_03_2018__07_28_45
Extracting travel time for trip: 21_03_2018__07_32_39
Extracting travel time for trip: 22_03_2018__07_38_43
Extracting travel time for trip: 14_02_2018__07_41_04
Extracting travel time for trip: 22_02_2018__07_42_45
Extracting travel time for trip: 12_02_2018__07_40_14
Extracting travel time for trip: 15_02_2018__07_45_52
Extracting travel time for trip: 16_02_2018__07_45_41
Extracting travel time for trip: 19_02_2018__07_46_19
Extracting travel time for trip: 20_02_2018__07_41_48
Extracting travel time for trip: 21_02_2018__07_42_42
Extracting travel time for trip: 13_03_2018__07_29_52
Extracting travel time for trip: 14_03_2018__07_35_46
Extracting travel time for trip: 20_03_2018__07_28_45
Extracting travel time for trip: 21_03_2018__07_32_39
Extracting travel time for trip: 22_03_2018__07_38_43
Extracting travel time for trip: 14_02_2018__07_41_04
Extracting travel time for trip: 22_02_2018__07_42_45
[9]:
'''For Evening trips'''
SingleTripsInfoSouthBound = [rec['SingleTripInfo'] for rec in con[RouteName]['TripInfo'].find({'$and':
                                                                   [ {'filteredLocationRecord':True},
                                                                    {'TripStartHour':'18'} ] })]
for SingleTripInfo in SingleTripsInfoSouthBound:
    print('Extracting travel time for trip: '+ SingleTripInfo)
    LocationRecords = [LocationRecord for LocationRecord in
                       con[RouteName][SingleTripInfo+'.Filtered'].find().sort([('epoch',1)])]

    BusStopsTimeStampList = ExtractTimeStampSouthBound(LocationRecords, BusStopsListSouth)

    addTravelTimeInformationToMongoDB(SingleTripInfo, BusStopsTimeStampList, 'South')
Extracting travel time for trip: 22_12_2017__18_38_34
Extracting travel time for trip: 19_12_2017__18_41_16
Extracting travel time for trip: 20_12_2017__18_31_19
Extracting travel time for trip: 08_01_2018__18_37_49
Extracting travel time for trip: 14_02_2018__18_30_22
Extracting travel time for trip: 15_02_2018__18_33_19
Extracting travel time for trip: 20_02_2018__18_31_07
Extracting travel time for trip: 28_03_2018__18_39_21
Extracting travel time for trip: 21_03_2018__18_32_40
Extracting travel time for trip: 21_02_2018__18_28_29
Extracting travel time for trip: 14_02_2018__18_30_22
Extracting travel time for trip: 15_02_2018__18_33_19
Extracting travel time for trip: 20_02_2018__18_31_07
Extracting travel time for trip: 28_03_2018__18_39_21
Extracting travel time for trip: 21_03_2018__18_32_40
Extracting travel time for trip: 21_02_2018__18_28_29

Now let us look at the .BusStopsRecord for one of the trips, for which BusStopRecordExtracted is True.

[10]:
SingleTripsInfo = [rec['SingleTripInfo'] for rec in
                             con[RouteName]['TripInfo'].find({'BusStopRecordExtracted':True})]

for SingleTripInfo in SingleTripsInfo:
    BusStopTimeStamp = [LocationRecord for LocationRecord in
                        con[RouteName][SingleTripInfo+'.BusStopsRecord'].find().sort([('epoch',1)])]

    #pprint.pprint(BusStopTimeStamp)
    break

pd.DataFrame(BusStopTimeStamp)
[10]:
Latitude Longitude Name _id epoch id
0 23.038331 72.511583 Pakwaan 5da5496d52d4e7104951285d 1.517192e+12 1
1 23.045992 72.515372 GuruDwara 5da5496d52d4e7104951285e 1.517192e+12 2
2 23.049854 72.517070 Thaltej 5da5496d52d4e7104951285f 1.517192e+12 3
3 23.058588 72.520010 Zydus 5da5496d52d4e71049512860 1.517192e+12 4
4 23.076662 72.525262 Kargil 5da5496d52d4e71049512861 1.517192e+12 5
5 23.086117 72.527993 Sola 5da5496d52d4e71049512862 1.517193e+12 6
6 23.098678 72.531615 Gota 5da5496d52d4e71049512863 1.517193e+12 7
7 23.136477 72.542575 Vaishnodevi 5da5496d52d4e71049512864 1.517193e+12 8
8 23.160550 72.556503 Khoraj 5da5496d52d4e71049512865 1.517193e+12 9
9 23.176030 72.584007 Adalaj-Uvarsad 5da5496d52d4e71049512866 1.517193e+12 10
10 23.192585 72.614842 Sargasan 5da5496d52d4e71049512867 1.517194e+12 11
11 23.185825 72.637597 Raksha-shakti circle 5da5496d52d4e71049512868 1.517194e+12 12
12 23.160963 72.635945 Bhaijipura 5da5496d52d4e71049512869 1.517194e+12 13
13 23.154698 72.664407 PDPU 5da5496d52d4e7104951286a 1.517194e+12 14

Here, the field epoch gives the time stamp corresponding to the bus-stop or a junction / crossroad.