Next, we create the BusTrackAnalyzer object to be used for the event detection in line 28 of main.py providing the bus dictionary and depot list as parameters to the constructor together with a list of class names for the bus event classes that we want the analyzer to detect. This list is defined in line 17 of main.py.
eventClasses = [ LeavingDepotEvent, EnteringDepotEvent, BusStoppedEvent, BusEncounterEvent ] # list of event classes to detect ... # create main BusTrackAnalyzer object analyzer = BusTrackAnalyzer(busData, depotData, eventClasses)
If you look at lines 12 to 21 of bus_track_analyzer.py, you will see that the constructor takes these parameters and stores them in its own instance variables for performing analysis steps later on (lines 16, 17 and 19).
def __init__(self, busData, depotData, eventClasses): self.allBusTrackers = [] # list of BusTracker objects for all buses currently being processed self.allEvents = [] # used for storing all Event objects created during an analysis run self.lastProcessedTimepoint = None # Timepoint of the last Observation that has been processed self._busData = busData # dictionary mapping bus Id strings to Bus objects with GPS data self._depotData = depotData # list of Depot objects used for Event detection self._observationQueue = [] # priority queue of next Observation objects to be processed for each bus self._eventClasses = eventClasses # list of instantiable subclasses of BusEvent that should be detected self.reset() # initialize variables for new analysis run
In addition, the constructor sets up some more instance variables that will be needed when the analysis is run: a list of bus trackers (one for each bus) in variable allBusTrackers, a list of events detected in variable allEvents, a variable lastProcessedTimepoint for the Timepoint of the last observation processed, and a list in variable _observationQueue that will serve as the priority queue of Observation objects to be processed next. Then in the last line, we call the method reset() of BusTrackAnalyzer defined in lines 23 to 40 whose purpose is to reset the value of these instance variables to what they need to be before the first analysis step is performed, allowing the analysis to be reset and repeated at any time.
def reset(self): """reset current analysis run and reinitialize everything for a new run""" self.allBusTrackers = [] self.allEvents = [] self.lastProcessedTimepoint = None self._observationQueue = [] for busId, bus in self._busData.items(): # go through all buses in the data busTracker = BusTracker(bus) # create new BusTracker object for bus # set initial BusTracker status to "IN DEPOT" if bus is inside bounding box of one of the depots isInDepot, depot = Depot.inDepot(bus.timepoints[0].lat, bus.timepoints[0].lon, self._depotData) if isInDepot: busTracker.status = BusTracker.STATUS_INDEPOT busTracker.depot = depot self.allBusTrackers.append(busTracker) # add new BusTracker to list of all BusTrackers heapq.heappush(self._observationQueue, Observation(busTracker, 0)) # create Observation for first Timepoint of this bus # and add to Observation priority queue
The main thing the method does is go through the dictionary with all the Bus objects and, for each, create a new BusTracker object that will be placed in the allBusTrackers list, set the initial status of that BusTracker to STATUS_INDEPOT if the first Timepoint for that bus is inside one of the depots (else the status will be the default value STATUS_DRIVING), and create an Observation object with that BusTracker for the first Timepoint from the Timepoint list of the corresponding bus that will be put into the observation priority queue via the call of the heapq.headpush(…) function (line 40). The image below illustrates how the main instance variables of the BusTrackAnalyzer object may look after this initialization for an imaginary input data set.
The buses with IDs 5, 2145, and 270 are the ones with earliest GPS observations in our imaginary data but there can be more busses that we are not showing in the diagram. We are also not showing all instance variables for each object, just the most important ones. Furthermore, Timepoint objects are shown as simple date + time values in the diagram not as objects of class Timepoint. The arrows indicate which objects the different instance variables contain starting with the _observationQueue, allBusTrackers, and allEvents instance variables of the single BusTrackAnalyzer object that we have.
The Bus objects at the top that contain the GPS data read from the input file will not change anymore and we are not showing here that these are actually maintained in a dictionary. The list of BusTracker objects (one for each Bus object) will also not change anymore but the properties of the individual BusTracker objects in it will change during the analysis. The observation queue list is the one that will change the most during the analysis because it will always contain the Observation objects to be processed ordered by the time point information. The event list is still empty because we have not detected any events yet.