GEOG 489
Advanced Python Programming for GIS

4.10.2.4 Producing the output GPGK files

PrintPrint

Remember that we said that the code from main.py will also produce output vector data sets of the bus tracks and events in the end. This happens in lines 57 to 63 of main.py which are only executed when analyzer.isFinished() returns True, so when the analysis has processed all observations:

        else:                               # when the analyzer has finished write events and bus tracks to new GeoPackage files   
            analyzer.saveBusTrackPolylineFile("dublin_bus_tracks.gpkg", "GPKG")
            analyzer.saveEventPointFile("dublin_bus_events.gpkg",  "GPKG")

            # reset analyzer and enable button again
            analyzer.reset()
            mainWidget.button.setEnabled(True) 

This code assumes that the QGIS environment has already been set up which happens in lines 69 to 73 of main.py; this code should look familiar from Section 4.5.3. The code for creating the output files can be found in the two methods saveBusTrackPolylineFile(…) and saveEventPointFile(…) of BusTrackAnalyzer in lines 73 to 114 of bus_track_analyzer.py.

def saveBusTrackPolylineFile(self, filename, fileFormat):  
         """save event list as a WGS84 point vector dataset using qgis under the provided filename and using the given format. It is 
            expected that qgis has been initalized before calling this method"""  
         # create layer for polylines in EPSG:4326 and an integer field BUS_ID for storing the bus id for each track 
         layer = qgis.core.QgsVectorLayer('LineString?crs=EPSG:4326&field=BUS_ID:integer', 'tracks' , 'memory') 
         prov = layer.dataProvider() 

         # create polyline features 
         features = [] 
         for busId, bus in self._busData.items(): 
             # use list comprehension to produce list of QgsPoinXY objects from bus's Timepoints 
             points = [ qgis.core.QgsPointXY(tp.lon,tp.lat) for tp in bus.timepoints ] 
             feat = qgis.core.QgsFeature() 
             lineGeometry = qgis.core.QgsGeometry.fromPolylineXY(points) 
             feat.setGeometry(lineGeometry) 
             feat.setAttributes([int(busId)]) 
             features.append(feat) 

         # add features to layer and write layer to file 
         prov.addFeatures(features) 
         qgis.core.QgsVectorFileWriter.writeAsVectorFormat( layer, filename, "utf-8", layer.crs(), fileFormat)

saveBusTrackPolylineFile(…) creates a list of QgsPointXY objects from the Timepoints of each Bus object (line 105) and then creates a Polyline geometry from it (line 107) which is further turned into a feature with an attribute for the ID of the bus, and then added to the created layer in line 113. Finally, the layer is written to a new file using the name and format given as parameter to the function. We here use the GeoPackage format “GPKG” but this can easily be changed in main.py to produce, for instance, a shapefile instead.

	def saveEventPointFile(self, filename, fileFormat):   
         """save event list as a WGS84 point vector dataset using qgis under the provided filename and using the given format. It is
            expected that qgis has been initalized before calling this method"""          
         # create layer for points in EPSG:4326 and with two string fields called TYPE and INFO 
         layer = qgis.core.QgsVectorLayer('Point?crs=EPSG:4326&field=TYPE:string(50)&field=INFO:string(255)', 'events' , 'memory') 
         prov = layer.dataProvider() 

         # create point features for all events from self.allEvents and use their Event class name  
         # and string provided by description() method for the TYPE and INFO attribute columns 
         features = [] 
         for event in self.allEvents:  
             p =  qgis.core.QgsPointXY(event.timepoint.lon, event.timepoint.lat) 
             feat = qgis.core.QgsFeature() 
             feat.setGeometry(qgis.core.QgsGeometry.fromPointXY(p)) 
             feat.setAttributes([type(event).__name__, event.description()]) 
             features.append(feat) 

         # add features to layer and write layer to file 
         prov.addFeatures(features)  
         qgis.core.QgsVectorFileWriter.writeAsVectorFormat( layer, filename, "utf-8", layer.crs(), fileFormat) 

saveEventPointFile(…) works in the same way but produces QgsPointXY point features with the attribute fields TYPE and INFO for each event in the allEvents list. The TYPE field will contain the name of the event class this event is from, and the INFO field will contain the short description produced by calling the description() method of the event. Notice this just needs a single line (line 87) because of our event class hierarchy and polymorphism. When opening the two produced files in QGIS, adding a basemap, and adapting the symbology a bit, the result looks like this:

map with bus routes, encounters, and events indicated through several layers      
Figure 4.28 Produced bus track and event layers in QGIS

We hope the way this program works got clear from this explanation with (a) the BusTrackAnalyzer being the central class for running the event detection in a step-wise fashion, (b) the Observation objects maintained in a priority queue being used to process the GPS observation in chronological order, (c) the BusTracker objects being used to keep track of the current status of a bus during the analysis, and (d) the different bus event classes all providing their own function to detect whether or not an event of that type has occurred. The program is definitely quite complex but this is the last lesson so it is getting time to see some larger projects and learn to read the source code. As the final step, let's look at the BusTrackerWidget class that provides a visualization of event detection while the analysis process is running.