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.