Build Your Own Bing Maps API Mash-Up: Architecture
In my last post, I mentioned the “starter-kit” applications my colleagues and I have built around the many open APIs available across the web. My contribution to this effort is a sample that leverages both the Bing Maps control and API and the TomTom Traffic Cams API to show the location of and display the images captured by over 13,000 traffic cameras in the US and Canada. In this first of some deeper-dive posts, I’ll provide an overview of how the application is structured, so you can plug-in your own location-centric API to create a completely new app!
To follow along, I suggest downloading the APIMASH projects from GitHub, specifically the one in the APIMASH_TomTom_BingMaps_StarterKit folder. It contains the three Visual Studio projects discussed next. And as also mentioned in my previous post, can get your own API keys from Bing Maps and Mashery to run the sample code.
Class Library: APIMASH
This class library provides the lower-level plumbing for making HTTP GET requests to an API provider and deserializing the results into a class you've provided. In most cases, you shouldn’t need to touch the code here.
The core class here is ApiBase, which should be the base class for any additional APIs you integrate into this application framework. ApiBase exposes two members:
- _apiKey: the getter for which you’ll need to implement in your API-specific class
- Invoke<T>: the method you’ll call for each specific HTTP endpoint you invoke in your API-specific class. The type parameter, T, refers to a type you’ll define to hold the results of the API call. Within the implementation of the APIMASH library, deserializer classes are provided to automatically handle HTTP response payloads in XML, JSON, and image formats according to the Content-Type header returned by the API call itself. For invocations that don’t specify a media type in the header or for which the default serializers aren’t sufficient, you can add your own (see the Deserializers.cs file for the general pattern used here).
There are three other classes of note in this library:
- ApiMonitor is intended to be a singleton for the application (you'll see it defined as a resource in App.xaml). It retains the result of the last API call that was made and includes a Boolean status flag indicating whether an API call is currently processing. It’s used as a target for data binding in the main application both to show error information (see ApiErrorPanel.xaml) and to drive the progress bars that appear while an external call is underway.
- ApiResponse<T> encapsulates the response for every API call made through this starter-kit framework. The type T refers to the class instance (typically one you've created) that holds the deserialized response from the API call. In the Model-View-ViewModel pattern, this is essentially your model class. ApiResponse<T> also records the HTTP status and any exception that might have occurred during the last API invocation.
- ApiResponseStatus is the base class for ApiResponse and exposes all of the API response information except for the actual content. As such it does not require a type-specific parameter.
Class Library: APIMASH_APIs
This library houses the classes derived from ApiBase that drive the application. In the starter-kit there are two classes provided, one for Bing Maps and the other for TomTom, but the structure of each (and that of any others you might add) is identical and includes the following components:
A class extending ApiBase which will be instantiated within the Windows Store application whenever access is needed to the associated API. The class should include
- One or more publically accessible ViewModel classes. It's through these classes you'll be able to bind the results of API calls to user interface components like ListViews and GridViews.
- An implementation of the getter for the API key. In most cases the key is a simple string obtained from the API provider that you might include as a resource in App.xaml.
- One or more asynchronous methods, each returning ApiResponse<T>, each performing a specific method call against the targeted API. The TomTom implementation, for example, includes two methods, one to obtain all of the traffic cameras within a specific area and the other to obtain the image from a specific camera. The implementation of each of these methods will typically populate a appropriate ViewModel class reference that is also exposed by the class containing the method.
An API-wide ViewModel class (implementing BindableBase and thereby INotifyPropertyChanged) which includes the ObservableCollections and other properties that can be bound to the user interface. The main API class should include an instance of this class as a publically accessible field or property.
One or more model classes that reflect the data returned by a given API call. This is the class provided as the generic type argument to the Invoke method in ApiBase, and as such it defines how how the results from the API call will be serialized into a C# type. You'll create this type based on the structure of the data returned by the API call. When the API invocation is complete, the deserialized response is contained in the ApiResponse<T>.DeserialzedResponse property.
If you are using Visual Studio Professional or above, you can generate C# class from a JSON or XML snippet by copying it to the clipboard and using Paste XML as Classes or Paste JSON as Classes. For JSON, I've also used https://json2csharp.com/.
Be sure to consult the documentation for your API if you are using a sample response to generate your classes, since all the possible properties may not have been relevant (and therefore returned) for the specific call you made to obtain the output.
One or more item-specific ViewModel classes that reflect the subset of data returned from an API call that is relevant to the user experience in the application. These ViewModel classes will be the base classes for ObservableCollections and other properties exposed by the API's ViewModel class (the one extending ApiBase).
In this starter-kit, you'll note there are PopulateViewModel static methods defined on the Model class that transfer (and optionally modify) elements from the Model class to the ViewModel class to provide a format conducive to databinding to the user interface. These methods are invoked in the body of each specific API method after receiving the response from the associated API invocation.
In this project you’ll also find a static class of utilities and types for mapping applications as well as the more pivotal IMappable interface. That interface should be implemented by whatever ViewModel class you are planning to build your application around, so that you can easily leverage the built-in synchronization between the class and the Bing Maps control.
If all that sounds a bit convoluted, I’ve include the diagram below to map out the relevant parts of a representative API class, the TomTom API which is used in the sample starter kit.
Windows Store Project: APIMASH-TomTom_BingMaps-StarterKit
The Windows Store application includes two pages, a main page (MainPage.xaml) and a search results page (LocationSearchResultsPage.xaml), representative images of which appear below. The main page includes the Bing Maps control as well as a user control (LeftPanel.xaml) in which are shown the cameras retrieved from the TomTom API (or whatever other items you want to display from your API of choice).
In this starter kit, MainPage.xaml contains the Bing Maps control and all of the map-specific functionality for your application. It interacts with the Bing Maps API on your behalf, so you don’t really need the change any code within it. Likewise, the landing page for the Search contract (LocationSearchResulsPage.xaml) provides a generic location search capability for your application so no modifications are required there either.
The UserControl LeftPanel.xaml is where API-specific code is centralized. It’s here where I instantiate an instance of the TomTom API class, invoke the methods to retrieve the traffic cameras, and bind the resulting data to a ListView (and an image control). The collection to which the list view is bound has code (also included) that synchronizes the placement of pins on the map, so as long as the class of items contained in that collection implements IMappable you don’t have to write any code to update the map. Likewise, tapping a map pin will select the appropriate element of the list view (and vice versa).
There are two other user controls in this project, but neither require any modifications.
- SearchPanel.xaml provides an in-page location search capability (versus the Search contract which navigates to a search result page, namely LocationSearchResulsPage.xaml).
- APIErrorPanel.xaml enables you to incorporate an (optional) error display, should an API call not succeed
In the next article, I'll focus more deeply on the implementation of the left panel user control and show how you can mash up a completely different API!