Web Application Development Exercise

The Transport View

The idea of the transport view is to generate a visualization based on a open data feed that provides near real-time data on the public transport system in The Netherlands. Just like with the previous cases, this content requires its own view object and also a controller. Create the file for the TransportView. The file for this view should be called CityApp/app/view/transport/TransportView.js. The code for the view is included in Listing 8-1.

This view will only contain one panel, which fills the whole page. We will fill this panel with a map. Now, create the CityApp/app/view/transport/TransportController.js file for the controller of this view (see Listing 8-2).

Nothing new to discuss here. Like we did with the other controllers, we have defined a global variable pointing to this controller 10, which we need to declare. Update the CityApp/app/Application.js with the new declaration as shown in Listing 8-3.

Now we attach the new view to the application by updating the CityApp/app/view/home/HomeView.js file as shown in Listing 8-4.

Nothing new to discuss here either. Note that we have change the active tab so that the first tab that is displayed is the one we are working on. Reload the application.

Repeat the process we used for the map creation process of the statistics view, to create a map for this view. All you need is to add to the TransportController: a map variable; an initialization function associated to the proper event; and, a call to the createMap function of the HomeController, from within this controller's initialization function (Check Listings 4-7, 4-8 and 4-12.). The result of your changes should look similar to the image in Figure 8-1.

Well, if your application's look and feel resembles Figure 8-1, congratulations, you have done a very good job. Let us move forward then.

The Transit Feed

Now, let us focus on the data. For this view we are going to use an open data feed from transit-agancies in the netherlands YOu can find detail information on the data feed at: http://gtfs.ovapi.nl/README. The data comes in GTFS format, whcih is a common format for the publication of transportation schedules. You can read about it at: https://developers.google.com/transit/gtfs/.

Accessing this feed from within our application is not possible due to the standard cross-domain restrictions of the Web. To be able to use it we hav to create a server-side script. This script will work as a proxy between the data feed provider and our application. The code of the script is shown in Listing 8-5. Use the code in the listing to create the CityApp/app/api/transitfeed.py file.

This script is primarily informative. A couple of important things are: the script expects a bounding box as a parameter. This bounding box represents the area for which data is expected; the script returns a FeatureCollection in GeoJSON format; To reduce network traffic during the execution of the exercise, we will use a virtual version of the data feed located in one of our servers, rather than the feed from the original source (http://gtfs.ovapi.nl/). Let us test the service using the following URL.

https://gisedu.itc.utwente.nl/student/<<SNUMBER>>/CityApp/app/api/transitfeed.py

This example request does not include a bounding box therefore the service will use the default bounding box that is coded in the service's script. Later we can check if the service also works when a bbox parameter is provided.

The Vehicles Layer

The response of the transitfeed service can be used to include a vector layer on the map of the transit view. We will place the code for the layer inside the initialization function as shown in Listing 8-6. Use the code to update the CityApp/app/view/transport/TransportController.js file.

This code will basically create a layer with no features for now. Later we will add features to this layer using the data from the transit feed.

Remember to adapt the code presented in the listings to your own. For example, use your own variable as a replacement for the txpMap variable in the listings, or rename your variable to match the listings. Reload the application and check the console to make sure that there are no error messages.

To take the next step we need to decide when are we going to retrieve the data from the feed. There are basically three moments when this should happen. 1) when the map extent changes (this includes the first time when it is rendered). 2) when a certain amount of time has pass since the last time the feed was read. 3) when the transport view receives focus. Let us code for cases one and two. If we check the documentation of OpenLayers, we observe that a change in the map extent will trigger the moveend event. We can associate that event with a function and use that function to read the feed. Listing 8-7 shows the code to associate the map's moveend event with a function, which should be included in the CityApp/app/view/transport/TransportController.js file.

The refreshVehicleLayer function, does two things. It calls the function to read the data feed, and then it schedules the next call to take place every minute afterwards. The code for the getVehiclePositions function, which is the function that reads the transport feed is shown in Listing 8-8. Use it to update the CityApp/app/view/transport/TransportController.js file.

The getVehiclePositions function uses an AJAX request to read the data feed using the bounding box coordinates passed on from the function called. The updateVehiclePositions function is assigned as the callback function responsible to handle the response to the request. With that response, the updateVehiclePositions function adds new features to the vehicle later or updates the features that have been added before. Every new or updated feature from the current response from the feed gets its timestamp updated 47. If after processing the response from the feed there are features which timestamps were not update. It means they are no longer valid and then are removed from the layer 49–51.

One important issue here is that the map uses RD new coordinates (EPSG:28992) and the data from the feed is in WGS84 (EPSG:4326). That means we need to transform the coordinates of the feed before features can be added to the map 33–36, 39–42. OpenLayers is fully capable of handling coordinates in EPSG:4326, but it does not know the transformation parameters for EPSG:28992. To be able to transform the coordinates as needed by the application we have to include those parameters. Use Listing 8-9 to create the CityApp/projdefs/epsg28992.js file.

Now we need to include in the application the transformation parameters for EPSG:28992, and the javascript projection library. Upadate the CityApp/index.html with the code in Listing 8-10.

Reload the application now. The new result should now include point markers for the various points that were available in the feed. Your application should look similar to Figure 8-2, that is, with some points representing public transport.

We will use an interaction object to display data about each point when the user hovers over one of the points. Update the CityApp/app/view/transport/TransportController.js file as shown in Listing 8-11.

Reload the application and test the hover interaction. Next, we will make sure the map is redrawn in case the browser window is resized. Use the code in Listing 8-12 to update the CityApp/app/view/transport/TransportController.js file.

Try resizing the browser to see that the map behaves properly. Pay attention to the console, you will see that as a response to the resize action, a new request to the data feed is made.

Now we will take care of disabling the periodic request to the feed, when the user is not using the transport tab, and also to update the points when the transport tab is active again. Update the CityApp/app/view/home/HomeController.js file as shown in Listing 8-13.

Finally we will also disable the request to the data feed when the user switches to another web page or to another application. Include the code in Listing 8-14 in the CityApp/app/view/home/HomeController.js file.